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


Армстронг: Безусловно. На конференциях по функциональному программированию мы спорим, выпячивая расхождения. Мы спорим о "жадных" и "ленивых" вычислениях, о динамической и статической типизации. Но несмотря ни на что, остается центральная идея функционального программирования: х не обозначает место в памяти, это неизменяемое значение. Мы задаем х равным 3, и дальше с этим ничего не сделаешь. Различные функциональные сообщества сходятся на том, что это крайне полезно для понимания программ, для параллелизма, для отладки. Однако есть функциональные языки с динамической типизацией, такие как Erlang, и функциональные языки со статической типизацией. У тех и других свои сильные и слабые стороны.

Было бы здорово, если бы Erlang пользовался преимуществами статической типизации. Возможно, в отдельных местах мы сумеем аннотировать программы так, чтобы типы стали более явными, тогда компилятор сможет порождать типы и генерировать более качественный код.

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

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

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

Сейбел: Считаете ли вы, что у вас был более систематический подход?

Армстронг: Да, другие попросту сдавались, неясно почему. Я не понимал, почему они не могут отладить. Как вы полагаете, отладка - трудное дело? По-моему, нет. Просто останавливаешь программу и прокручиваешь в замедленном режиме. Я сейчас говорю о пакетном Фортране.

Конечно, если говорить об отладке систем реального времени или программ чистки памяти, то я помню, как однажды "упал" Erlang. Это было в самом начале, сразу после запуска, я сидел и что-то настукивал - и Erlang "упал". У него в оболочку было встроено что-то вроде команд Emacs. Набираешь erl, чтобы запустить его, и оказываешься в REPL. Я набрал четыре-пять символов и сделал орфографическую ошибку. Потом я несколько раз сдвинул курсор назад, исправил, и он "упал" с ошибкой сборки мусора. Я знал, что это была очень, очень серьезная ошибка. Я попытался в точности вспомнить, что я настукивал, - там было около 12 символов - начал сначала, набил их, и все пошло без сбоя. Я сидел часа полтора, пробуя сотню разных штук. И вот снова сбой! Тут я все записал и смог приступить к отладке.

Сейбел: А чем вы пользуетесь? Операторами печати?

Армстронг: Да. Великие боги программирования говорят: "Вставь оператор печати в программу там, где, по-твоему, допущена ошибка, ре-компилируй и запусти".

Еще есть Закон отладки Джо - не помню, вычитал я его где-то или сам придумал. Звучит он так: "Все ошибки будут не дальше трех операторов в ту или другую сторону от места последнего изменения программы". Когда я работал в Шведской космической корпорации, мой начальник там раньше занимался "железом". Мы были вместе с ним в Эсранге на севере Швеции - там площадка для запуска ракет и станция слежения за спутниками. Как-то раз он ломал голову, отлавливая ошибку в оборудовании, подсоединяя осциллографы, что-то меняя. Я спросил: "Может, я могу чем-нибудь помочь?" От ответил: "Нет, Джо, это ведь железо". Тогда я сказал: "Это должно быть как в программах - ошибка недалеко от места последнего изменения". Он подумал и сказал: "Ты гений! Я же поменял конденсатор". Дело в том, что он заменил конденсатор на другой, более крупный. Мой шеф отпаял его, поставил тот, что был вначале, и все заработало. И это верно для всего. Ремонтируешь машину, что-то не так - причина в последнем действии. Всегда вспоминайте, что вы поменяли.

Сейбел: Вы доказывали когда-нибудь корректность своих программ? Импонирует ли вам такой формализм?

Армстронг: И да и нет. Я преобразовывал программы алгебраически, чтобы показать, что они эквивалентны, но никогда не применял доказательство через теоремы как таковое. Помню, когда я читал курс денотационной семантики, то отказался от этой идеи. Было дано упражнение: пусть х = 3иу = 4вх + у; докажите, что схема жадного вычисления, заданная уравнением таким-то, и схема ленивого вычисления, заданная уравнением таким-то, обе приводятся к 7.

Четырнадцать страниц лемм и прочего. Потом я подумал: "Ну как нее х = 3, у = 4, х + у = 7". В то время я писал компилятор для Erlang. Если бы пришлось на десятках страниц доказывать, что 3 + 4 = 7, то доказательство правильности компилятора заняло бы не одну тысячу страниц.

Сейбел: Вы предпочитаете работать один или в команде?

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

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

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

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

Сейбел: А что это за особые задачи?

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

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

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

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

Сейбел: Но это приносило пользу?

Армстронг: О, да. Я был в восторге, когда ему удавалось что-то улучшить. Мы продвигались очень хорошими темпами. Роберт был склонен к обобщению. Однажды я нашел переменную - отслеживал ее в 45 подпрограммах, и в конце концов выяснилось, что она ни разу не использовалась, хотя присутствовала в 45 различных функциях. Я спросил: "Зачем эта штука - она ни разу не используется?" И Роберт ответил: "Зарезервирована под будущее расширение". Я ее убрал.

Я решил написать специализированный алгоритм, убирающий все, что не нужно в данной конкретной программе. Чем более специализированными они были, тем короче становились. А когда Роберт брал мою программу, она удлинялась, делаясь более универсальной. Думаю, это философия UNIX: программа должна делать то, что от нее требуется, и ничего больше. У Роберта была другая философия: программа должна быть частным случаем некоей более общей программы. Поэтому мы попеременно добавляли то общего, то специального.

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

Армстронг: Была. Каждый цикл оказывался улучшен. Думаю, программа в целом очень выиграла - больше, чем если бы ее писал один из нас.

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

Армстронг: ОТР проектировали я, Мартин Бьёрклунд и Магнус Фрё-берг. Над первоначальным проектом работали только мы трое. Мы собирались каждое утро за чашкой кофе, долго беседовали - час, два часа - и за это время исписывали всю доску. Я сразу же писал всю документацию, а они - весь код. Правда, иногда я тоже выдавал фрагмент-другой кода. И когда я писал документацию и понимал, что не могу что-то описать, мы это меняли. Или они приходили ко мне со словами: "Программа не будет работать, мы поняли сегодня утром, потому что вот это не так, и это, и это". К концу дня у нас была вся нужная документация и весь нужный код - или достаточно того и другого, чтобы работать. И на этом мы успокаивались.

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

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

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

Сейбел: В этом случае вы писали документацию еще до кода или, по крайней мере, одновременно с ним. Вы так делаете всегда?

Армстронг: Зависит от сложности задачи. Если она очень сложная, я часто начинаю с документации. Чем сложнее, тем больше я склонен начинать с документации.

Мне нравится делать документацию. По-моему, до появления документации в нормальном виде программу нельзя считать готовой. Писать спецификации мне тоже нравится. Некоторые говорят: "Хотите знать, что делает программа? Читайте код". Думаю, это непрофессиональный подход. Код показывает мне, что программа делает, а не то, что она должна по идее делать. Код - это решение задачи. Если нет спецификации или какой-либо документации, приходится догадываться о задаче по решению. Догадка может быть неверной. Я хочу иметь объяснение - в чем состоит задача.

Сейбел: На этой стадии вы пишете внутреннюю документацию для других программистов или документацию для пользователя?

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

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

Если бы я программировал на Haskell, то с самого начала задумался бы о типах, написал для них документацию. Работая с Лиспом или Erlang, можно начать с кода, не особенно заботясь о типах. В каком-то смысле написание документации - тоже забота о типах. Начинается со связки "есть". "Мелодия есть последовательность нот". Хорошо. Мелодия есть последовательность аккордов, каждый из которых является сочетанием нот равной длины. Простыми определениями терминов в документации - такое-то есть то-то - вы делаете своего рода анализ типов и декларативно размышляете о структурах данных.

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

Армстронг: Да. Новые языки вполне хороши. Haskell и ему подобные, Erlang. Есть интересные языки, которые надо использовать активнее.

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

Сейбел: Дэн Ингаллс говорит, что мы должны пересмотреть свои взгляды на Пролог, после того как уже несколько десятилетий испытываем действие Закона Мура.

Армстронг: Пролог сильно отличается от других языков. Там реализован любопытный способ мышления, и он подходит не для всех задач, хотя и для очень многих. Он мало распространен, к нашему стыду, - ведь программы на нем выходят очень короткими. Когда я написал первую свою программу на Прологе, то испытал нечто вроде шока. Поразительное было ощущение. Смотри - где программа, которую ты написал? Ты всего лишь рассказал немного фактов о системе, о своей задаче, а он сообразил, что делать. Просто чудесно! Надо бы бросить Erlang и вернуться к Прологу.

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

Армстронг: Умение писать. Кто-то из компьютерных теоретиков сказал: "Если у вас плохо с английским, вы никогда не станете хорошим программистом".

Сейбел: Кажется, Дейкстра.

Армстронг: Со мной время от времени советуются университеты насчет учебных планов по компьютерным наукам. Я ведь работаю в компании - вот они и хотят знать, что нужно на практике. Я говорю: "Учите их писать и подбирать убедительные доводы". Большинство выпускников, имеющих степень по компьютерным наукам, не сильны в писании текстов.

Думаю, учить этому очень тяжело, потому что это очень индивидуально. Кто-нибудь перечеркивает твой текст красной ручкой и объясняет, в чем ты неправ. Это отнимает очень много времени. Вы знакомы с советом Хэмминга молодым исследователям?

Сейбел: Из доклада "You and Your Research" (Вы и ваше исследование)?

Армстронг: Хэмминг говорит примерно так: "Делайте правильные вещи. Если вы не делаете правильные вещи в правильных областях, то неважно, что именно вы делаете". Еще он говорит: "Один день в неделю я обязательно осваиваю что-то новое. То есть трачу на освоение нового на 20% больше, чем мои коллеги. Выходит, через 4,5 года я буду знать вдвое больше них, а с учетом сложных процентов получается, что через 5 лет я буду знать втрое больше". Не помню точно, какие там были цифры. По-моему, это верно. Поскольку я занимаюсь исследованиями, то на освоение чего-то нового трачу не 20% времени, а 40%. Я занимался ими 30 лет. И понял, что знаю очень много. Вы спрашивали, как улучшить навыки программирования? Тратьте 20% своего времени на узнавание чего-нибудь нового. Прочтите Хэмминга, он очень хорошо пишет.

Сейбел: Бывало так, что вы находили какой-то код просто красивым?

Назад Дальше