Кодеры за работой. Размышления о ремесле программиста - Питер Сейбел 35 стр.


Следующий шаг - отделить сложные пути от легких. Вам придется очень туго при неверном выборе архитектуры - если вы нарушили встроенные ограничения или просто создаете не то, что нужно. В Google мы все время сталкиваемся с проблемами этого типа. Есть также проблема масштабирования. Исходя из сегодняшнего состояния вы говорите, что программа должна быть, скажем, в десять раз мощнее - через несколько лет этот показатель будет недостаточным и программу можно выбрасывать. Но важно, по крайней мере, сделать правильный выбор для заданных рабочих условий. Вы собираетесь обрабатывать от миллиарда до десяти миллиардов страниц. Как распределить это по машинам? Каков окажется объем входящего и исходящего трафика? Надо, чтобы на этом уровне все держалось крепко. Что-то можно рассчитать на салфетке, что-то - при помощи симуляции, а в чем-то вам придется предсказать будущее.

Сейбел: Для такого рода задач подсчеты на салфетке или симуляции полезнее, чем написание кода.

Норвиг: Наверное, да. Здесь подсчеты будут лучшим выходом. Затем вам могут сказать, что в будущем году появится коммутатор, позволяющий пропускать вдесятеро больший трафик. Полагаться на это - или рассчитывать исходя из сегодняшних возможностей? И такие вопросы возникают на каждом шагу.

Есть пользовательские интерфейсы, про которые что-то можно понять только после их создания. Считаешь какое-то взаимодействие очень удобным, а половине пользователей оно оказывается недоступным. Тогда возвращаешься назад и придумываешь что-то новое.

Сейбел: Отвлечемся от пользовательских взаимодействий. Когда бывает полезным создавать прототипы, а не просто смотреть и обдумывать, как все должно работать?

Норвиг: Думаю, полезно придумать решение и посмотреть, как оно работает, удобно ли это. Вам нужны инструменты, которые позволят построить систему сейчас и совершенствовать ее потом. Тогда вы создаете прототип, и он оказывается неуклюжим - возможно, неверно задан набор примитивов. Лучше выяснить это как можно раньше.

Сейбел: Как насчет разработки через тестирование?

Норвиг: Тесты для меня - скорее метод исправления ошибок, чем разработки. Этот крайний подход к проектированию мне не кажется правильным. Мы пишем тест, на который знаем верный ответ, программа его не прошла - думаем, что делать дальше.

Это имело бы смысл только в случае одного-единственного заранее известного решения. Надо подумать прежде всего об этом. Каковы ваши строительные элементы? Как вы будете писать тесты для элементов, о которых еще ничего не знаете? Но если элементы известны, то полезно иметь тесты для каждого из них, чтобы понимать, как элементы взаимодействуют друг с другом, какие у вас пограничные случаи и так далее. Здесь тесты необходимы. Но нельзя построить на них проектирование целиком.

Еще мне не нравится вот что - мы в Google то и дело сталкиваемся с этим: программа не укладывается в простую булевскую модель теста. В тесте есть assertEqual, assertNotEqual, assertTrue и так далее. Это хорошо, но нам нужно также иметь assertAsFastAsPossible. Нам нужны утверждения относительно огромной базы данных возможных запросов: мы получаем результаты, которые оцениваются по стоимости достижения определенной точности, стоимости повторного вызова - и хотим их оптимизировать. Но вот этих статистических или непрерывных значений, которые мы хотим оптимизировать, в тестах нет. А от булевского типа - "верно или нет" - проку мало.

Сейбел: Но в конце концов все можно свести к булевским типам - послать сколько-то запросов, получить все эти значения и посмотреть, находятся ли они в пределах допустимого.

Норвиг: Да. Но просто поглядев на методы, применяемые в тестах, вы поймете, что они не годятся для этого, что в них не предусмотрена такая возможность. Я поражаюсь тому, насколько этот подход распространен в Google. Работая в Junglee, я однажды читал об этих вещах лекцию в отделе контроля качества. Мы занимались исследованиями покупок и сказали тогда: "Нужен тест - можем ли мы по такому-то запросу получить 80% верных ответов". Они спросили: "Хорошо, а неверный ответ - это ошибка в программе?" Я сказал: "Нет, один неверный ответ здесь не будет ошибкой". И они удивились: "Как так, неверный ответ не считается ошибкой?" Как будто здесь был выбор только между "да" и "нет", в то время как это скорее напоминало компромисс.

Сейбел: Однако вы по-прежнему верите в модульные тесты. Как программисты должны продумывать тестирование?

Норвиг: Нужно писать много тестов, думая о разных условиях. Нужны и модульные тесты, и сложные регрессионные тесты. Нужно думать о вариантах отказа оборудования. Помнится, один из лучших уроков программирования я получил однажды в аэропорту Хитроу, когда там не было электричества и все компьютеры не работали. Тем не менее мой самолет улетел вовремя.

У них были распечатки для всех рейсов. Не знаю, откуда они взялись, возможно с какого-то компьютера за пределами здания. Может, они распечатали их именно в то утро, а может, делают это каждую ночь, а днем выбрасывают, если с электричеством все в порядке. Но распечатки были, и работники на каждом выходе пользовались ими вместо компьютеров.

Отличный урок проектирования программ. Мало кто задается вопросом: "Как будет работать моя программа, если отключат электричество?".

Сейбел: А как в этом случае идет работа в Google?

Норвиг: Работа в Google в этом случае идет так себе. Но у нас есть резервное питание и множество дата-центров. Мы продумываем такие варианты: что будет, если сервер, с которым идет соединение, недоступен? Что будет при каком-либо другом отказе? Программа выполняется на тысячах машин - что будет, если одна выйдет из строя? Можно ли это вычисление перезапустить в другом месте?

Сейбел: В очерке о разработке ТеХ Кнут говорит о переключении сознания: как стать своим собственным инспектором по качеству и начать беспощадно крушить свой же код. Как по-вашему, разработчикам в целом это удается?

Норвиг: Нет. И в качестве примера могу привести свою программу проверки правописания. Я сделал ошибку в коде, который оценивал мое правописание, и одновременно немного изменил реальный код. Запустил его - и получил для себя результаты гораздо лучше обычных. И я поверил им! Будь результат очень плохим, я бы стал разбираться. Но тут я поверил в свои хорошие результаты, которые получились из-за небольшого изменения в программе. А надо было трезво все оценить и понять, что результаты не могут так сильно отличаться, что где-то допущена ошибка.

Сейбел: Как вы избегаете чрезмерной универсализации и создания того, что не понадобится, а только приведет к лишней трате ресурсов?

Норвиг: По этому вопросу идет борьба, даже горячая борьба. Но меня лучше не спрашивать - я всегда предпочитал изящные решения практичным. Поэтому я вынужден сражаться с собой, напоминая себе, что в своей работе не могу себе этого позволить. Приходится повторять - и себе, и своим коллегам: "Мы ищем самое разумное решение, и если есть идеально красивое, не факт, что оно применимо" или так: "Мы занимаемся тем, что важнее всего в данный момент". Есть поговорка "Лучшее - враг хорошего". Каждый инженер обязан ее помнить.

Сейбел: Почему так соблазнительно решать задачи, которые на самом деле не актуальны?

Норвиг: Мы любим чувствовать себя умными и любим завершенность. Мы хотим как можно скорее завершить работу - закончить и двигаться дальше. Мне кажется, так устроен человек - он может чем-то позаниматься, но потом ему хочется сказать: "Все, готово, выкидываю это из головы и иду дальше". Но надо еще подсчитать рентабельность полностью завершенной работы. Это кривая в форме S - после 80- или 90-процентной готовности рентабельность неуклонно снижается. И у вас есть сотня других проектов, где вы находитесь внизу кривой и рентабельность намного выше. В какой-то момент вы говорите: "Хватит с этим, переходим к более рентабельным вещам".

Сейбел: Как научить программистов понимать, на каком отрезке кривой они находятся?

Норвиг: Нужна правильная рабочая среда, ориентированная на результат. Думаю, люди способны учиться сами. Вы хотите оптимизировать что-то, но предоставленные сами себе, вы оптимизируете ваше личное удобство. А надо иметь в виду другое, одни говорят - рентабельность, другие - удовлетворенность клиента. Что лучше для клиента - если я закончу эту программу с 9 5-процентной готовностью или примусь за десять других, где готовность 0%?

В Google с этим проще - здесь действует принцип "выпускать как можно раньше и чаще". Причин тому несколько. Прежде всего, большая часть наших продуктов распространяется бесплатно: значит, выпускаем как можно раньше - кто будет жаловаться? И потом, мы ведь не штампуем и не расфасовываем компакт-диски, поэтому не совсем готовый продукт, даже продукт с ошибками - это не катастрофа. Программы в основном хранятся на наших серверах, можно исправить их завтра, и обновление будет у всех мгновенно. Нас не преследует кошмар установки обновлений. Мы рассуждаем так: "Запустим это, посмотрим на реакцию пользователей, исправим то, что нужно, и не будем волноваться об остальном".

Сейбел: Проектируя большую программу, чем вы пользуетесь - блокнотом, линованной бумагой, UML-программой для рисования?

Норвиг: Нет, все эти UML-инструменты мне никогда не нравились. Я всегда считал, что, если это нельзя выразить на самом языке, это недостаток языка. Многое приходится делать на более высоком уровне. В Google немалая часть работы связана с разбиением программ на модули и организацией их параллельного выполнения. Нам нужно запустить программу на большом числе машин, но у нас столько-то пользователей, столько-то данных для приложений - как все это будет работать? Поэтому мы скорее думаем в терминах машин и машинных комплексов, чем на уровне функций и взаимодействий. Если это улажено, можно переходить к частным функциям и методам.

Сейбел: Описания делаются на уровне простого языка?

Норвиг: В основном, да. Иногда кто-то рисует картинки, рассуждая в таком духе: "У нас есть сервер, который обрабатывает такие-то запросы, он подключается к другому серверу, мы используем различные средства хранения, большие распределенные хеш-таблицы и так далее. Мы берем три готовых инструмента и дальше выясняем, нужен ли новый инструмент, какой из этих трех будет работать, потребуется ли что-то еще".

Сейбел: Как оцениваются такие схемы?

Норвиг: Их показывают тем, кто уже этим занимался, и они говорят: "Похоже, здесь вам нужен кэш - система станет тормозить, но поскольку тут много повторяющихся запросов, кэш такого-то размера должен помочь". Есть стадия ревизии проекта - на ней решается, есть ли у него смысл, а потом начинается разработка и тестирование.

Сейбел: У вас принято устраивать формальные ревизии проекта? Вы ведь работали в НАСА, а там с этим строго.

Норвиг: Да, строже, чем в НАСА, не бывает. У нас планка ниже - как я уже говорил, мы можем допустить недочет и исправить его. В НАСА первый же недочет будет фатальным, и они вынуждены быть куда внимательнее. А мы не сильно на этот счет беспокоимся. Скорее консультации, чем ревизии.

Есть, конечно, те, кто официально занимается чтением предложений по проектам и одобряет или отклоняет их. Но это намного менее формальная процедура, чем в НАСА. Это происходит перед запуском. В ходе реализации бывают проверки, но в коде никто не копается, просто задают вопросы: "Как все идет? С отставанием от графика или с опережением? Есть ли большие проблемы?" Примерно на таком уровне.

Самая формальная часть - запуск проекта. Есть таблица контрольных проверок - в плане безопасности все проверяется очень тщательно. Если мы это запустим, сможет ли кто-то получить доступ через меж-сайтовый скриптинг? Тут все довольно строго.

Сейбел: Вы рассказывали, как проверяли Гвидо ван Россума на знание Python, а Кена Томпсона - на знание Си, выясняя, способны ли они придерживаться довольно жестких стандартов кодирования. Для проектирования у вас такие же жесткие стандарты?

Норвиг: Нет. Некоторые стандарты кодирования касаются вопросов проектирования, но все гораздо свободнее. Разумеется, есть определенная политика - нужно получить разрешение на участие в написании кода. И каждое изменение кода кем-то контролируется и проверяется.

Сейбел: Значит, любое изменение кода в хранилище р4 контролируется?

Норвиг: Можно писать экспериментальные фрагменты для себя. Есть исключения, когда ревизия выполняется позднее. Но лучше так делать пореже.

Сейбел: То есть это эквивалент классической проверки - кто-то смотрит ваш код и подтверждает, что в нем все в порядке.

Норвиг: Да. На самом деле это был первый проект Гвидо. Мы использовали для этого утилиту diff, но это слишком громоздкий способ. А Гвидо создал распределенную систему с красивым интерфейсом и подсветкой, так что ревизии кода облегчились.

Сейбел: Во многих компаниях говорят, что ревизии нужны, но не все этому следуют. Ведь этому людей надо обучать.

Норвиг: Думаю, такие вещи делались всегда, и люди к этому привыкают сразу. Ну, не все - некоторым требуется время. Вот типичный случай: в компанию приходит не привыкший к этому новичок, создает экспериментальную ветвь и делает все в ней. Вы говорите: "Слушай, ты же не зафиксировал ни одного изменения". Он отвечает: "Да-да-да, я всего лишь кое-что чищу, зафиксирую завтра". Проходит неделя, другая, и в конце концов фиксируется одно гигантское изменение. Прошло много времени, весь этот объем изменений сложно оценить, да и то, с чем надо было сравнивать, тоже успело измениться. Новичок видит, какая это головная боль, и впредь так не поступает.

Сейбел: Понятно. А проверяющим нужны какие-то навыки?

Норвиг: Известно, что один проверяет лучше, другой - хуже. Приходится всегда делать выбор, кому отдать код на ревизию - тому, кто сделает качественно, или тому, кто сделает быстро.

Сейбел: Чем отличаются хорошие проверяющие?

Норвиг: Они отслеживают больше разных вещей. Иногда вы ошибаетесь в чем-то банальном - скажем, пробелы в отступах, но иногда вам предлагают изменить структуру кода - переставить такой-то кусок в другое место. Одни подходят к этому добросовестно, другие не замо-рачиваются.

Сейбел: В связи с этим хочу спросить: каждый ли хороший программист по мере роста становится хорошим проектировщиком? Или некоторые программисты блестящи только на своем уровне, и им никогда не дадут проектировать сложные программы?

Норвиг: У всех разные умения. Один из лучших наших специалистов по поиску не очень хорошо программирует - код выходит средний. Но, допустим, его спрашиваешь: "Вводится новый фактор - сколько раз человек щелкает на этой странице, сделав то и то. Как учесть его в наших результатах поиска?" И он отвечает: "В строке 427 есть альфа-переменная, возьмите новый фактор, возведите в квадрат, умножьте на 1,5 и прибавьте к переменной". Через несколько месяцев экспериментов с разными подходами выясняется, что он был прав, только умножать надо, скажем, на 1,3.

Сейбел: Значит, он четко представляет работу программы.

Норвиг: Он прекрасно понимает код. Другие пишут лучше, но он сразу прикидывает все последствия изменений в коде.

Сейбел: А это как-то связано между собой? Нередко тот, кто пишет самый жуткий спагетти-код, как раз и способен удержать его в голове целиком. Ведь только поэтому он так и пишет.

Норвиг: Да, не исключено.

Сейбел: Итак, проверки у вас не такие формальные, как в НАСА. Что еще скажете о разнице между культурой "инженеров" и "хакеров", в лучшем смысле обоих слов?

Норвиг: Разница есть в организационной структуре и в отношении к ПО. Google начинался как компания, производящая ПО, и в те времена были наняты исполнительный директор - PhD компьютерных наук из Беркли, вице-президент по продажам - бывший компьютерный инженер, и так строилась вся фирма. А в НАСА все они специалисты по ракетам! Они мало соображают в программах, считая их необходимым злом. "Линейный код я еще понимаю, но вот циклы - это уже подозрительно. А если там еще и условие внутри цикла - о, это уже слишком далеко от того, что я могу решить через дифференциальное уравнение из теории управления!" Там все настроены очень недоверчиво.

Сейбел: Но так и нужно.

Норвиг: Да, так и нужно. Еще они не любят инноваций. Если сказать кому-нибудь: "У меня есть новый отличный прототип, посмотри", - он ответит: "С удовольствием использую это в своем запуске, после того как он успешно отлетает два других". И все говорят одно и то же.

Дон Голдин пришел на административную работу в НАСА и сказал: "Лучше, быстрее, дешевле - вот что нам нужно. Запуски обходятся слишком дорого. Лучше делать больше запусков, пусть некоторые будут неудачными, но все равно мы сделаем больше за те же деньги". И поспорить было сложно. К сожалению, политически решение оказалось неверным. Потерять спутник - совсем не здорово, потому что люди запоминают только одно: НАСА потеряла спутник. Разницы между спутником за 100 миллионов и за 1 миллиард они не видят. Так что потерять десять стомиллионных спутников и один миллиардный - разные вещи. Поэтому решение оказалось не совсем верным.

Сейбел: Какова худшая ошибка из всех найденных вами?

Норвиг: С самыми большими ошибками (не моими) я имел дело, когда участвовал в чистке после программных сбоев на Марсе в 1998 году. Первый - из-за того, что вместо ньютонов были указаны футофунты. И второй - преждевременное отключение двигателей из-за сбоя программы, - мы так думали, но без полной уверенности.

Сейбел: Помню один из отчетов по Mars Climate Orbiter - как раз ту историю с футофунтами и ньютонами. Вы там были единственным специалистом по компьютерам. Вы тоже участвовали в разговоре с производителями ПО, выясняя проблему?

Норвиг: Ну, после событий, когда нашли причину, все было просто. Они разобрались довольно быстро. А потом было расследование - как такое могло случиться? Думаю, было несколько причин. Первая - аутсорсинг: одни программы разрабатывала JPL в Пасадене, другие -Lockheed-Martin в Колорадо. Два специалиста из двух разных команд попросту не обедали вместе, иначе, убежден, этой проблемы бы не было. А так, один написал другому по электронной почте: "С этими измерениями что-то не так, кажется, мы слегка просчитались. Но не очень сильно, все должно быть в порядке".

Сейбел: Это происходило уже во время полета?

Назад Дальше