Текст книги "Кодеры за работой. Размышления о ремесле программиста"
Автор книги: Питер Сейбел
сообщить о нарушении
Текущая страница: 11 (всего у книги 41 страниц) [доступный отрывок для чтения: 15 страниц]
Язык должен совершенствоваться, чтобы можно было решить стоящие перед программистами задачи. С некоторыми можно справиться, если писать собственные библиотечные абстракции. Но возможность написания абстракций для языка очень ограничена, если нет расширений – вы не можете написать геттеры и сеттеры[47]47
Геттер, сеттер – специальные методы, используемые в объектно-ориентированном программировании и позволяющие реализовать гибкий меха низм инкапсуляции. -Прим. науч.ред.
[Закрыть]. Объекты будут смотреться чужеродно, свойства не смогут стать кодом и так далее. Кроме того, вопросы безопасности нельзя будет разрешать неявно или автоматически.
Сейбел: Как вы полагаете, языки в целом со временем улучшаются?
Айк: Думаю, да. Может быть, сейчас наступает второй «золотой век» – растет интерес к языкам и их созданию. Мы говорим о программировании: нужно совершенствовать свои навыки, как в писательском ремесле или музыке. Но используемый язык – «тональная система» – тоже важен. Надо поэтому совершенствовать языки, а не довольствоваться тем, что есть. Сеть требует совместимости, и JavaScript, возможно, склонен оставаться таким, каков он есть. Но не стоит зацикливаться на этом. Мы обязаны улучшать JavaScript, даже если он не заменит тот, что применяется для веб-разработок. Другой вариант – создать нечто совсем новое.
Есть, например, Ruby, испытавший влияние языков Ada и Smalltalk. Это прекрасно. Я не имею в виду эклектичность. Но Ruby перехваливают. Нет, ничего плохого сказать не могу, просто некоторые мальчики-фанаты трубят, что он решит все проблемы, а это не так. Новые языки нужны, но хвалить их надо в меру, не как C++ – «шаблоны проектирования нас спасут». Конечно, может быть, это реакция на консерватизм приверженцев Си-мира UNIX 1980-x.
Но в какой-то момент нам требуются более совершенные языки. Ведь необходимо иметь инструменты для доказательства, производящие автоматическую верификацию некоторых постулатов, сделанных в вашем коде. Вам ведь не нужны ответы на все, правильно? Динамические инструменты, такие как Valgrind и его детекторы состояний гонки[48]48
Race conditions – ошибки в многопоточном коде из-за одновременного изменения объекта в памяти несколькими потоками, оставляющими объект в некорректном состоянии. – Прим. науч.ред.
[Закрыть], тоже очень полезны. Простых решений нет, но есть более совершенные языки, на которые надо переходить.
Сейбел: В какой степени языки должны предотвращать ошибки программистов?
Айк: Язык для «синих воротничков», такой как Java, должен иметь четкую и логичную базовую систему, так как «синим воротничкам» трудно понять, что это за вывороченный синтаксис с ковариантными и контравариантными ограничениями типов. Я немало пострадал из-за того, что порой выкидывают Си и C++. Программирование отчасти состоит в конструировании, а конструирование отчасти состоит в решении проблем безопасности. Они важны при разработке браузера и еще больше – если вы делаете программу для аппарата лучевой терапии. Так или иначе, речь идет о более совершенных языках для написания параллельных программ или эффективного использования аппаратного параллелизма. Мы не всегда будем использовать синхронизированные блоки и уж точно не будем использовать мьютексы или взаимные блокировки. Поэтому языки могут служить для установления разумного компромисса между безопасностью и выразительностью.
Мне кажется, с JavaScript мы стремились именно к этому, в противоположность диким, неуправляемым французам, которые хотели видеть JavaScript чем-то вроде #86 с лямбда-выражениями. Нам совершенно незачем вводить оператор call/cc. Предположим на минуту, что обойдется без проблем с реализацией: народ будет сбит с толку. Не обязательно большинство, но многие из тех, кто считает себя крутыми программистами. Есть нечто вроде программистской пирамиды, на вершине которой располагается Правильная вещь. Люди карабкаются к вершине, даже если некоторые срываются, получая травмы.
В JavaScript можно нарваться на неприятности разными способами. Есть функции первого класса. Есть малопонятные для людей прототипы – они не отвечают классическим стандартам объектно-ориентированного программирования.
Многовато. Я не минималист и не утверждаю, что мы должны прекратить развитие языка. Это удобная отговорка в Microsoft, которая меня бесит, потому что люди тратят время, а ошибки не устраняются. Даже при наличии лямбда-выражений вы все равно не избавитесь от кое-каких трудноустранимых ошибок.
Дуг стал учить народ применению различных шаблонов, но я согласен с Питером Норвигом: эти шаблоны выявили определенный дефект языка. Они не бесплатны. Бесплатного ланча не бывает. Мы должны добиваться эволюции языка в верном направлении. Не исключено, что появится необязательная типизация, и она может напоминать систему контрактов PLT.
Сейбел: Многое из того, что вы делаете, от статического анализа вашего C++ до работ в области компиляции «на лету» и добавления новых свойств в JavaScript, говорит о том, что вы стараетесь держаться на переднем крае компьютерных исследований.
Айк: Мы ведем справедливую борьбу, но надо делать это с умом. Надо также изменить направление исследований, так как – для меня это было очевидным еще в школе – академическая наука выглядит чем-то ненормальным. Она сильно оторвана от практики.
Надо это исправлять. Мы успешно работали с практически мыслящими учеными. Денег у нас немного, поэтому отчасти мы занимаемся тем, что попросту разговариваем с людьми, служим для них объединяющим центром.
Если ученые охотятся за правительственными грантами и никак непричастны к вашей деятельности, это в какой-то степени потеря для вас. Далее, вы наблюдаете бурный взлет динамических языков, слушаете безумные заявления насчет того, как они убьют Java, и что статические языки – просто смешно. Но ученые убеждены, что статическая типизация – это венец эволюции, они исследуют особые ее виды – язык ML, вывод типов Хиндли-Милнера. Это полный отрыв от практики.
Сейбел: Почему так происходит? Они не решают реальных проблем – или решение было бы половинчатым?
Айк: Мы делали кое-что с SML/NJ для размещения на резидентном сервере эталонной реализации четвертой версии JavaScript, ныне несуществующей. Мы пытались создать дефинициональный интерпретатор и даже не использовали модель Хиндли-Милнера. Мы собирались аннотировать типы и аргументы так, чтобы избежать этих безумных сообщений об ошибке, когда типы не унифицируются и подбирается наудачу фрагмент исходного кода – обычно не тот, что нужно. Это вопрос качества реализации. Возможно, это также теоретическая проблема, связанная с типами, – когда не получается унификация, трудно найти, какой фрагмент кода тому причиной.
Итак, вы можете проводить дополнительные исследования и разрабатывать высокоуровневую модель когнитивных ошибок, допускаемых программистами, чтобы лучше определить этот фрагмент кода. Может быть, я касаюсь мелкой проблемы, но, скорее всего, она значительна.
Академическая наука не способна предложить нам более совершенную модель. Мне кажется, сейчас это негодная вещь – но, возможно, не по вине ученых. Экономические основы этой науки прогнили. Мы все знали, что придем к массовому параллелизму, – никто не предложил решения. Сейчас все только и говорят, что о транзакционной памяти. Но это не решение. Вы не хотите иметь дело с тем, как вложенные транзакции отменяются и распространяются на один процессор за другим. Это неэффективно, это не будет работать в отдельных случаях. Вы не сможете переложить на это все свои конкурентные или параллельные алгоритмы, даже пытаться не будете.
Некоторые, например Джо Армстронг, проделали действительно большую работу с подходом без разделяемых ресурсов. Таких встречается много в создаваемых индивидуально системах в реализациях браузеров. В этом смысле выделяется Chrome. Мы сделали это по-своему в нашей реализации JavaScript. Но этот подход, по-моему, совсем не интересует ученых. Транзакционная память вызывает больше интереса, особенно там, где это касается компьютерной архитектуры, поскольку они способны разработать хорошие наборы инструкций и оборудование под них. Но это не решит всех наших сегодняшних проблем.
Мне кажется, что прогресс неизбежен, что он затронет языки программирования. И поэтому разговоры о «втором золотом веке» я воспринимаю как должное. Просто пока не установлена связь между пользователями языков и потенциальными разработчиками из числа ученых, способными создать действительно революционный язык.
Сейбел: Вы получили степень магистра, но диссертацию так и не защитили. Вы бы порекомендовали будущим программистам защищать диссертацию по компьютерным наукам? Или это нужно далеко не всем?
Айк: По-моему, далеко не всем. Для этого нужны особые навыки, и в конце концов вы спрашиваете себя, а может, степень досталась мне просто в качестве компенсации за перенесенные страдания? Зато после этого мы можете прибавлять после фамилии «доктор компьютерных наук». Это открывает перед вами кое-какие двери. Но мой опыт работы в Кремниевой долине на протяжении 20 лет инфляционного бума – который, возможно, подходит к концу – говорит о том, что это дело невыгодное. И я ни о чем не жалею.
Это соблазнительно – поизучать что-нибудь систематически, и может быть, даже не особо напрягаясь. Выход на рынок, зависимость от закона Мура, конкуренция, короткий жизненный цикл продукта, одноразовые программы – все это тягостно, если в этом варятся все. Поэтому тем, кто хочет получить степень, кто имеет для этого нужные навыки, всегда будет чем заняться. Есть интересные области для исследования. Мы в Mozilla занимаемся вещами, промежуточными между тем, что ценится в академических кругах, и тем, что воплощается на практике. Это компиляторы, виртуальные машины, даже отладчики, профилировщики – вроде тех, что делает Valgrind. Для исследователей это слишком пресно и малоденежно, не очень ново, слишком много чисто инженерных задач – но здесь возможны крупные прорывы. Мы сотрудничаем с Андреасом Галом (Andreas Gal), и его работы по этим темам были отвергнуты как чрезмерно прикладные.
Конечно, нам нужны исследователи, которые склоняются к прикладным задачам, но также и программисты, которые занимаются исследованиями. Мы должны четко понимать, что делаем, а не быть «синими воротничками», на которых смотрят свысока мудрецы, засевшие в башнях из слоновой кости.
Сейбел: А что насчет доказательства правильности программ?
Айк: Это трудно. А люди по большей части ленивы. Ларри Уолл прав: лень должна считаться достоинством. Вот почему я предпочитаю автоматизировать этот процесс. Ученые его любят, большинство программистов ненавидят. Писать предикаты утверждений – это может принести пользу. У нас в Mozilla было несколько плохих утверждений – скорее они должны были быть предупреждениями, – но со временем хороших становится все больше. Благодаря этому нас наконец озарило, что есть инварианты – те, которые вы хотели бы реализовать в некоей идеальной системе типов.
Думаю, полезно считать утверждения точками доказательства правильности программы. Но не нужно стремиться к полному доказательству. Сколько дыр в серьезных доказательствах, напечатанных в научных журналах!
Сейбел: Давайте сменим тему. Можете ли вы припомнить худшую из ошибок, которую вам довелось отлавливать?
Айк: Худшие ошибки связаны с многопоточностью. В Silicon Graphics я делал работу, связанную с ядром UNIX. Как и все тогдашние UNIX-ядра, оно представляло собой монолитный монитор, который завершался после вхождения в ядро через системный вызов. Исключая прерывания, оно гарантированно работало вплоть до завершения, и блокировка вашей структуры данных никогда не наступала. Прекрасно и просто.
Но вот в SGI пришли блестящие молодые умы из HP. Настала эпоха симметричной мультипроцессорной обработки данных. Старая группа, которая занималась ядром, распалась. Теперь ядро делали новые ребята. Темп работы сильно ускорился, но какие у них были инструменты? Си, семафоры, блокировки, возможно, также мониторы, условные переменные. Все коды написаны от руки. Тысячи ошибок. Полный кошмар.
Мне предложили тогда съездить в Австралию и Новую Зеландию – я описал все это в своем блоге. Мы тогда как раз исправляли ошибку в полевых условиях. Это было страшно тяжело – найти ее и исправить, потому что ошибка была такого свойства: код для однопроцессорного ядра помещался в ядро, созданное для симметричной мультипроцессорной обработки данных, и мы совсем не беспокоились насчет определенных условий гонки. Поэтому для исправления пришлось создавать контрольный пример, что само по себе было непросто. И все это при нехватке времени – клиент хотел исправления в полевых условиях.
Диагностировать ее было трудно, так как она была связана с синхронизацией по времени. Машины использовались не по назначению, как концентраторы терминалов. Люди подвешивали псевдотерминалы к реальным терминалам. Это делали студенты в лаборатории или сотрудники брисбенской компании, производившей ПО для горной промышленности: множество отсеков и в конце стеклянная стена, а за ней компьютеры, в том числе двухпроцессорная машина от SGI. Было нелегко, и я рад, что мы все же нашли ошибку.
Обычно такие ошибки не сидят годами, но отыскать их крайне трудно. Нужно как бы приостановить все, думать о них постоянно, видеть их во сне... А заканчивается все тем, что вы делаете элементарные вещи. Так бывает со многими ошибками. Все заканчивается бисекцией, по методу волка и забора[49]49
«Wolf fence» – алгоритм поиска, аналогичный методу поимки льва в пустыне (здесь – волка на Аляске). Реализуется размещением в коде инструкций print, позволяющих определить местоположение ошибки. – Прим.ред.
[Закрыть]. Вы постоянно следите за выполнением, за состоянием памяти, пытаетесь прикинуть размер ошибки, течение исполнения программы, понять, к каким данным можно обратиться. Если это куча голых указателей, дело плохо: следует обратиться к более современным инструментам, которые появились вместе с гигагерцными процессорами, вроде Valgrind и Purify.
Инструментирование и наличие контролируемой модели всей иерархии памяти – это большое дело. Роберт О'Каллагэн, могучий новозеландский ум, создал собственный отладчик на базе Valgrind: он записывает каждую инструкцию, и можно в любой момент восстановить состояние программы целиком. Это не только отладчик, путешествующий во времени. Это целая база данных: вы видите структуру данных, замечаете поле с безумными значениями, выясняете, кто делал там последнюю запись. Вы идете от следствий к причинам – в отладке это занимает очень много времени. Это в тысячу раз медленнее, чем все происходит в реальном времени, но у вас есть надежда.
Можно также использовать записывающие виртуальные машины – они записывают состояние только при системных вызовах и на границах ввода/вывода. Они могут воссоздать состояние поврежденной программы на каждой границе – правда, со всем, что между границами, намного сложнее. Зато все можно закончить быстро, практически в реальном времени, потом перенести программу в Chronomancer, запустить ее в медленном темпе, воссоздать все состояния и найти ошибку.
К сожалению, технология отладки мало исследована. Вот еще пример пропасти между учеными и практиками. Ученые создают доказательства правильности, часто вручную, – правда, эта работа все больше автоматизируется благодаря POPLmark и подобным инструментам. Но в реальной жизни везде есть только отладчики, встречаются даже развалюхи родом из 1970-х, вроде GDB.
Сейбел: В реальной жизни есть еще разрыв между приверженцами символических отладчиков и операторов вывода?
Айк: Да. Поэтому я использую GDB и доволен тем, что в нем, по крайней мере на Маке, есть возможность поставить точку прерывания, и это обычно работает. Я могу наблюдать за адресом, могу засечь момент, когда правильные биты сменяются неправильными. Это довольно полезно. Я также использую команду printf для бисекции. Когда я уже близок к цели, то обычно пытаюсь сделать что-то внутри GDB или пользуюсь командными сценариями (скриптами), хотя они очень слабы. Язык сценариев сам по себе очень слаб. По-моему, Ван Якобсон добавил циклы, но не знаю, использовались ли они в настоящем GDB, после семинаров, организованных Фондом свободного программного обеспечения.
Однако отладчики могут сделать бесконечно больше, чем делают сейчас, и в этом смысле Chronomancer и Replay – шаг вперед. Они изменили для меня весь процесс. Но вот насчет многопоточности не знаю. Есть Helgrind и другие динамические детекторы гонок, которыми мы пользуемся. Они дают ложные срабатывания, которые надо отсеивать. С ними пока еще не все до конца понятно.
Многопоточные процессы страшат меня – до того как я женился и завел детей, они съели немалую часть моей жизни. Не все задумываются насчет параллелизма и всех возможных комбинаций команд, происходящих даже в небольших сценариях. Если ваш код соединяется с чужим, он выходит из-под контроля. Вы не можете мысленно смоделировать происходящее. Большинство людей не могут. Я мог бы стать одним из этих всезнаек с сайта Slashdot: когда я писал в своем блоге, что против многопоточности, кое-кто говорил: «Да он ничего не знает, это несерьезно». Давай, болтай. Я слетал в Австралию и Новую Зеландию, мне достались кое-какие бонусы. Но все это было так мучительно и продолжалось слишком долго. Как сказал Уайльд о социализме, «Он отнимает слишком много вечеров».
Сейбел: Как вы проектируете код?
Айк: Я много прототипирую. Создаю нечто вроде высокоуровневого псевдокода и затем постепенно заполняю его снизу вверх. Этот псевдокод я обычно не пишу, а держу в голове, и иду снизу вверх, пока оба конца не сойдутся. Часто я работаю с готовыми фрагментами кода, добавляя новую подсистему или что-то постороннее, и почти все делаю снизу вверх. Если в середине я сталкиваюсь с трудностями, то опять пишу псевдокод и начинаю работать все в том же направлении – снизу вверх, пока не закончу его. Я стараюсь не затягивать с этим, так как проверить псевдокод невозможно – надо смотреть, как он работает, выполнять его шаг за шагом, убеждаясь, что он делает именно то, чего от него ждешь.
Еще до этого этапа я могу установить кое-какие связи между объектами, вчерне набросать модули. Обычно есть два-три алгоритма, и можно прикинуть их сложность – линейная она или константная. Всякий раз, когда я выполнял поиск с линейной сложностью, который затем складывался в квадратичный, для веб-разработчиков это была проблема. Поэтому мы предпочитаем делать много структур данных с константным временем доступа. Но даже и тогда эта константа может не быть единицей – она может быть достаточно большой, чтобы о ней побеспокоиться.
Поэтому мы создаем множество прототипов, работаем над разными кусками с обоих концов, которые сводим в середине. Я считаю, что сейчас мы в Mozilla недостаточно переписываем код. Мы слишком консервативны. У нас открытый исходный код, поэтому вокруг нас создаются сообщества, мы заинтересованы в новых людях. Мы работаем в интересах пользователей и поэтому не можем позволить себе трехлетний перерыв на переписывание – хотя если очень постараться, то смогли бы.
Но если вам нужно избрать другой путь, и вы не знаете в точности какой, – переписывайте. Если хотите понять, что, черт возьми, вы тут делаете, потребуется несколько попыток. Код становится лучше по своему проектному решению, вы останавливаетесь на этой версии, начинаете ее латать, пока не дойдете до предела. Это нечто вроде эволюционного тупика для кода. Возможно, при приемлемых невозместимых издержках этот код останется на годы. А может, потребует замены. Вдруг в мире открытого исходного кода появится более совершенная стандартная библиотека?
Это возвращает нас к ремеслу программирования. Вы не просто пишете код в соответствии со старым проектным решением. Вы хотите постоянно практиковаться, а это значит думать о проектном решении кода и применять свой опыт в написании кода к этому решению.
У меня страшная аллергия на всякого рода эзотерические решения, шаблоны проектирования, доступные немногим. Питер Норвиг, работая в Harlequin, сказал о том, что шаблоны проектирования – всего лишь дефекты в вашем языке. Возьмите язык получше! И он был абсолютно прав. Что это такое – молиться на шаблоны, постоянно думая, какой бы из них применить!
Сейбел: Итак, постоянное обогащение опыта позволяет лучше выбирать направление. Но что если, создавая код, вы видите крупные дефекты в проектном решении?
Айк: Так бывает и нередко. Иногда очень сложно отказаться от кода и вернуться, чтобы начать все заново, – попадаешь в ловушку обязательств. У меня было такое с JavaScript. Я написал интерпретатор байт-кода в страшной спешке, и делая это, я уже понимал, что кое о чем пожалею. Но то было решение, понятное для других, и я надеялся, что другие помогут мне с этой программой. Поэтому о проектном решении я думаю всегда – не каждый раз мы можем позволить себе роскошь пересматривать фундаментальные основы кода. А именно это случается при масштабном переписывании.
Сейбел: Каким образом вы решаете, что необходимо масштабное переписывание? Благодаря Джоэлу Спольски Netscape в некотором смысле стала примером того, как опасно масштабное переписывание.
Айк: В Netscape хотели, чтобы приобретенная ими компания, потрясавшая известной книгой о шаблонах проектирования, вывела их на первое место благодаря новому движку рендеринга, который стал бы для всех ориентиром. Сверху это смотрелось хорошо: там использовались C++ и шаблоны проектирования. Но было множество проблем.
А вот вторая причина того, что мы взялись за переписывание: я трудился в mozilla.org и был сильно недоволен Netscape, как и Джейми, – тот вообще собирался уходить. Я считал, что нужно пустить на наше поле новых работников, а со старым запутанным кодом, сделанным на коленке в 1994 году, сделать это было нельзя. И с моим прекрасным кодом интерпретатора в стиле ядра UNIX.
Нам нужна была большая перезагрузка. Четыре года с момента выпуска программы! Не говоря ничего такого топ-менеджерам, поскольку они и слушать об этом не желали, мы стали искать оптимальное решение за них. В итоге полетело несколько топ-менеджерских голов. Правда, менеджеры, в отличие от меня, все равно сказочно заработали на опционах. Но для Mozilla это было выгодно.
Сегодня можно назвать это удачей, поскольку развитие Сети ускорилось. Видимо, Microsoft – некоторые утверждают, что это было связано скорее с антимонополистскими расследованиями, чем с желанием его руководителей, – хотела плотно оседлать Сеть и не допустить ее эволюции. Это дало нам возможность заняться переписыванием, размахивая знаменем стандартов (довольно сомнительный ход, особенно с учетом качества стандартов). Как и Джоэл, я скептично смотрю на переписывание. Трудно примирить все интересы, найти деньги и при этом правильно отреагировать на требования рынка. Исключений единицы.
Те случаи переписывания, о которых я говорил раньше, связаны с прототипами. Это крайне важно, а объем работы намного меньше. Можно порезать кучу кода, не очень много по числу строк, но с большими последствиями, и удовлетворить всем нужным инвариантам. Или это компиляция «на лету», или еще что-то, что позволяет решить задачу.
Сейбел: Вы занимались литературным программированием в духе Кнута?
Айк: Я делал кое-что наподобие его первоначальных программ – просто классно, мне очень нравилось. Это было извлечение слов. Там было некое подобие древовидного хеша, программирование было целиком литературным. Потом Дуг Макилрой сделал все то же самое, только с конвейером.
Наши программы подробно откомментированы, но нет средства изъять из них прозу и проверить ее – хотя бы вручную – относительно кода. Разработчики Python сделали в этом смысле кое-что интересное. Все что я сделал – не более чем подробное комметирование. Я периодически обновляю старые комментарии, но это тяжело, и иногда мне это не удается, и я жалею, что кто-то из-за меня получил неверную информацию.
Сейчас мне нравятся возражения Макилроя. Это не то что полное опровержение литературного программирования, но близко к тому. Не хочется писать много – неважно, прозы или кода. В каком-то смысле, на низшем уровне код должен говорить сам за себя. На более высоких уровнях – гигантские функции, границы модулей – уже нужна документация. Документирующие комментарии, или документарные строки. Встраивание тестов в комментарии. Думаю, разработчики Python сделали тем самым большое дело.
Кое-что, как видно, пришло из литературного программирования – документарные строки, встроенные тесты. Хотелось бы, чтобы языки поддерживали больше таких вещей. Мы пытались включить встроенную документацию в ES4 через поддержку метаданных первого класса или интроспекции, но не смогли договориться между собой.
Сейбел: Вы читаете чужой код?
Айк: Это часть моей работы. Ревизия кода – обязательный шаг перед коммитом, который был когда-то необходим в основном из-за плохой кадровой политики Netscape, но мы до сих пор пользуемся им, а также делаем интеграционные ревизии. Мы устраиваем также особые «суперревизии», когда изменяется много модулей и вы не знаете всех скрытых инвариантов, которые Джо Шмо[50]50
Джо Шмо – аналог Васи Пупкина. – Прим. ред.
[Закрыть], который больше не работает в Mozilla, держал в своей голове. В принципе, есть люди, способные охватить взглядом целостную картину. Иногда мы обходимся без этого, когда все хорошо знают, что делают, и понимают друг друга без слов, как джедаи. Но лучше поступать так не слишком часто.
У нас нет предварительных ревизий проектных решений. Поэтому иногда такие ревизии случаются потом сами. «У тебя слишком много кода. Вернись-ка назад и сделай по-другому». Но так бывает редко. Мы не навязываем «модель водопада», жестко последовательную схему разработки. Когда я занялся программированием в начале 1980-х, она была как раз в моде, просто кошмар. Вы пишете документацию, потом код, потом понимаете, что код не годится, вы его полностью меняете – и вся документация насмарку.
Сейбел: Вы говорите о коде, который пишется для Mozilla. А вы читали когда-нибудь код не по работе, а ради самообразования?
Айк: В этом смысле открытый исходный код – потрясающая вещь. Я люблю смотреть код, который пишут люди с разных концов света, хотя посвящаю этому не так много времени. Больше всего меня привлекают серверные фреймворки, а еще такие языки, как Python и Ruby.
Сейбел: То есть их реализации?
Айк: Реализации и код библиотек. Возьмем библиотеки Ajax: приятно видеть, насколько умны могут быть люди и как с небольшим набором инструментов – замыкания, прототипы, объекты – можно создавать удобные, порой очень удобные абстракции. Нельзя сказать, что они всегда крепко скроены или безопасны, – но до чего удобны!
Сейбел: Как вы поступаете, если надо прочесть большой кусок кода?
Айк: Я начинаю «сверху». Если фрагмент достаточно велик, у вас есть указатели функций, а течение исполнения программы не слишком ясно. Иногда я пропускаю его через отладчик и терзаю таким вот способом. Я также смотрю на шаблоны низкого уровня, которые могу распознать. Если это языковой процессор или в нем есть понятные мне системные вызовы, я пытаюсь выяснить, как используются эти примитивы. Как они используются на более высоких уровнях? Это позволяет мне немного освоиться с кодом. Но по-настоящему его понимаешь, когда создается целостный образ, когда смотришь на код сверху, снизу, с разных сторон, возясь с отладчиком, пропуская через него код шаг за шагом, как бы это ни раздражало.
Если удается посмотреть, что происходит в куче – проследить указатели, пройтись по списочным ячейкам, что-то еще, – осознаешь, что это стоило труда, как бы тошно ни казалось. Для меня это так же важно, как чтение исходника. Исходник можно читать долго, можно застрять в нем, смертельно скучая и понапрасну уверяя себя, что понимаешь вон то темное место.
Создавая регулярные выражения в JavaScript, я обращался к Perl 4. Я пропускал его через отладчик и читал код. Это давало мне идеи: реализация в обоих языках оказалась схожей. Их рекурсивный характер с поиском с возвратом был немного необычен, так что пришлось поднапрячься. Удалось только отладить некоторые регулярные выражения и проследить за исполнением. Другие программисты, как я слышал, тоже говорили об этом: нелегко пробираться сквозь код, понимать, как динамическое состояние системы выглядит при беглом взгляде или при проверке базовой функциональности. Я согласен с ними.
Сейбел: Вы делаете то же самое с собственным кодом, даже если не ищете ошибку?
Айк: Разумеется, я делаю проверку на вменяемость. И делаю много операторов-утверждений, и если они срабатывают, я тут же оказываюсь в отладчике. Но иногда пишешь код, применяя разные хитрые вспомогательные схемы. Тестируешь его, он работает... пока не пропустишь его через отладчик. Особенно если в нем есть особо заумный кусок, срабатывающий, только если звезды сходятся определенным образом. Используешь условные точки прерывания, точки прерывания по изменению данных, наконец, ловишь его, когда этот код срабатывает, – ага, звезды сошлись! И понимаешь, что живешь не на планете сказок. Если, пребывая в исходном коде, вы чувствуете себя обитателем этой планеты, беритесь за отладчик. Это важно, я так всегда поступаю.
Сейбел: Вы обнаруживаете проблему, когда пробираетесь сквозь исходник, держа в уме, что должно произойти вот это и вот это, но ничего не происходит?
Айк: Да, или – это моя личная проблема – я живу на планете сказок. Сейчас я постарел, стал скептичнее, работаю лучше, но все еще настроен слишком оптимистично. В каком-то уголке моего сознания Говорящий сверчок шепчет: «Да у тебя там, должно быть, ошибка, ведь ты наверняка о чем-то забыл». Со мной так всегда.
Иногда я точно знаю это – знаю, что где-то ошибся, – и покрываюсь потом. У меня свербит не то в заднем мозгу (где это, кстати?), не то в микротрубочках. Так или иначе, я чувствую, что должен быть начеку, и отладчик помогает мне быть начеку. Он помогает мне принять решение или понять, что вот этот тест-вектор не покрывает всех комбинаций кода, так как передо мной обширное гиперпространство.
Сейбел: Кроме чтения кода многие программисты читают еще и книги по специальности. Есть ли книги, которые вы можете порекомендовать?
Айк: Мне надо было бы читать побольше. Но это как в музыке: главное – практика. Можно узнать очень многое, читая чужой код. Мне очень нравились книги Брайана Кернигана, написанные предельно логично – написать небольшой фрагмент кода, использовать его заново по мере продвижения, строить модули. Еще «Искусство программирования» Кнута, тома 1-3, особенно та часть, где говорится о получисленных алгоритмах. Двойное хеширование – я люблю эти главы. Лемма о золотом сечении, которую требуется доказать в качестве упражнения.
Но, по-моему, по книгам не слишком-то научишься программировать. Программирование отчасти близко инженерному искусству: в один прекрасный день может понадобиться математика. И есть куча практических вещей, которые даже не поднимаются на уровень инженерного искусства, как оно понимается в строительстве и технике. Возможно, со временем все это чуть больше формализуется.