Глава тридцатая. Руби-дуби-ду
23 января 2002 года, среда
Желание переписать весь свой код заново иногда возникает потому, что изначально код разрабатывался не для тех целей, в которых его стали использовать. Он мог быть прототипом, экспериментальным или учебным кодом, демо-версией «на скорую руку» или попыткой за 9 месяцев пройти путь от нуля до Ай-Пи-О. После этого он превратился в нечто большое и бесформенное, ненадежное и не позволяющее развивать его дальше, и все жалуются, старые программисты в отчаянии уходят, а новые не могут разобраться в коде и добиваются от руководства согласия начать все заново, а в это время Microsoft отнимает у них бизнес. Сегодня я хочу вам рассказать, как можно действовать иначе.
FogBugz возникла шесть лет назад в качестве эксперимента, когда я изучал программирование ASP. Довольно скоро она превратилась во внутрифирменную программу отслеживания ошибок. Почти каждый день к ней добавлялись новые полезные функции, пока программа не стала настолько хороша, что дальнейшая доработка ей не требовалась.
Многие друзья спрашивали меня, нельзя ли им установить FogBugz в своих компаниях. Но в программе было слишком много жестко закодированных вещей, что мешало куда-либо перенести ее с компьютера, на котором она была развернута. Я использовал ряд хранимых процедур SQL Server, поэтому для работы FogBugz требовался SQL Server — слишком дорогой и избыточный продукт для команд из двух человек, желающих воспользоваться нашей программой. И так далее. Поэтому я говорил своим друзьям: «Если вы заплатите мне 5000 долларов за консультационные ус-
луги, я готов потратить пару дней и причесать код так, чтобы он выполнялся с Access вместо SQL Server». Обычно друзьям казалось, что это слишком дорого.
После нескольких таких случаев мне пришло в голову, что я мог бы продать ту же программу троим, взяв с них по 2000 долларов и оставшись с прибылью. Или тридцати покупателям по 200 долларов. Ведь с программами это просто. Поэтому в конце 2000 года Майкл взялся за дело, переработал код так, чтобы он мог выполняться как с Access, так и с SQL Server, вытащил в заголовок все, что касалось конкретного сайта, и мы начали продавать программу. На особый успех я не рассчитывал.
Мне казалось тогда, что есть куча пакетов для отслеживания ошибок, и что каждый программист пишет что-то свое для этой цели, — зачем кому-то покупать наш продукт? Я знал, что начинающие свое дело программисты часто заблуждаются, считая других такими же программистами, как они сами, с теми же потребностями, и у них возникает нездоровое желание продавать программные инструменты. Отсюда столько мелких компаний, торгующих вразнос безделушками для генерации исходного кода, обнаружения ошибок с отправкой сообщений по почте, расцвечивания синтаксиса в редакторе, FTP и отслеживания ошибок, — всякой ерундой, которая может понравиться только программисту. И я не хотел попасть в эту ловушку.
Конечно, все идет не совсем так, как планировалось. FogBugz стала популярной. По-настоящему. Fog Creek обязана ей существенной частью своих доходов, и объем продаж неуклонно растет. Народ все покупает и покупает ее.
Мы выпустили версию 2.0, постаравшись добавить в нее все необходимые функции. Когда Дэвид работал над версией 2.0, мы искренне считали, что на нее не стоит тратить много сил, поэтому он больше заботился о «целесообразности», чем об «элегантности». Некоторые, кхм, конструктивные проблемы первоначального решения остались догнивать. Имелись два законченных фрагмента почти идентичного кода для отображения главной страницы редактирования данных об ошибке. Операторы SQL были разбросаны по всему HTML-коду, там и сям. Наш скрипучий HTML предназначался для древних браузеров, нашпигованных ошибками и способных рухнуть даже при загрузке about:blank.
Да, она работала великолепно, и какое-то время не было ни одной зарегистрированной ошибки. Но внутри код представлял собой «большую помойку». Добавление новых функций было большим геморроем. Чтобы добавить одно поле в центральную таблицу ошибок, требовалось внести полсотни исправлений, и то, что мы забывали исправить, всплывало бы постоянно, вплоть до той поры, когда все заведут собственные бунгало на марсианском взморье, летая на уикенд в собственном ракетоплане.
В другой компании, управляемой каким-нибудь бывшим администратором службы экспресс-доставки, могли бы решить выкинуть весь код на помойку и начать все заново.
Я уже говорил, что не верю в начало с чистого листа? Кажется, я только об этом и говорю.
Как бы то ни было, я решил не начинать все заново, а потратить три недели своей жизни на то, чтобы полностью привести в порядок код. Руби-дуби-ду. Для этого упражнения я установил ряд правил в духе рефакторинга:
1. Не добавлять никакие новые функции, даже самые мелкие.
2. В любой момент времени, при каждом сохранении в системе код должен работать идеально.
3. Я буду выполнять только логические преобразования — то, что делается почти механически и позволяет тут же убедиться, что поведение кода не изменилось.
Я просмотрел все исходные файлы, один за другим, сверху донизу, рассматривая код, обдумывая лучшую структуру для него и внося простые изменения. Вот несколько примеров того, чем я занимался эти три недели:
Всюду заменил HTML на XHTML. Например, заменил тег <BR> на <br />, заключил все атрибуты в кавычки, проверил соответствие всех вложенных тегов и валидность всех страниц.
Удалил все форматирование (теги <FONT> и так далее), перенеся его в таблицу CSS.
Убрал все SQL-инструкции из кода представления и полностью из всей программной логики (того, что маркетологи называют бизнесправилами). Все это было отправлено в классы, которые я даже не проектировал, — просто не спеша, по мере необходимости добавлял методы. (Кто-то с толстой пачкой карточек 4x6 уже точит карандаш, чтобы выколоть мне глаз. Вы хотите сказать, что не проектировали свои классы?)
Нашел повторяющиеся блоки кода и создал классы, функции или методы, чтобы убрать повторения. Разбил крупные функции на несколько мелких.
Удалил оставшийся в основном коде английский текст и поместил его в отдельный файл, чтобы упростить интернационализацию.
Реструктурировал сайт ASP, сделав одну точку входа вместо кучи разных файлов. В результате то, что раньше давалось с большим трудом, стало стало гораздо проще; например, теперь мы можем выводить сообщения об ошибке ввода в той самой форме, где были введены недопустимые данные, — это должно быть просто, если все на своих местах, а я, когда еще только изучал программирование ASP, не размещал все правильно.
Все три недели код заметно улучшался. Для пользователя мало что изменилось. Некоторые шрифты стали лучше благодаря CSS. Я мог прекратить эту работу в любой момент, потому что мой код всегда был на 100% работоспособным (и я на всякий случай сохранял каждую версию на нашем действующем внутреннем сервере FogBugz). У меня практически не было сложных проблем, и я ничего не проектировал, потому что выполнял только простые логические преобразования. Попадались замысловатые куски кода. Обычно это были заплатки, накопившиеся за годы. К счастью, мне удалось сохранить заплатки в целости. Часто я замечал, что, начав все с нуля, снова повторил бы ту же самую ошибку, и она могла бы долго оставаться незамеченной.
Я, в основном, закончил. Как и планировалось, на все ушло три недели. Изменилась почти каждая строка кода. Да, я просмотрел каждую строку и изменил почти все. Структура совершенно изменилась. Все функции слежения за ошибками теперь отделены от HTML-функций интерфейса пользователя.
Вот положительные стороны моей работы по приведению кода в порядок:
Потрачено гораздо меньше времени, чем заняло бы полное переписывание. Допустим (исходя из срока, за который FogBugz пришла в нынешнее состояние), полное переписывание заняло бы год. Таким образом, я сэкономил сорок девять рабочих недель. Эти сорок девять недель представляют известную конструкцию кода, которую я сохранил. Мне не приходилось думать, что где-то нужна новая строка. Я просто тупо заменял <BR> на <br /> и двигался дальше. Мне не надо было думать, как организовать загрузку файлов по частям. Она работала. Требовалось только немного причесать ее.
Я не ввел никаких новых ошибок. Конечно, без пары мелких ошибок, скорее всего, не обошлось. Но я не делал того, что могло бы породить новые ошибки.
При необходимости я мог в любой момент остановиться и представить работающий продукт.
График работы был полностью предсказуем. Неделя такой работы -и вы точно знаете, сколько строк обрабатываете за час, что позволяет оценить оставшуюся работу. Вот бы разработчикам Mozilla так, а не вилами по воде.
Теперь гораздо проще добавить в код новые функции. Эти три недели с большой вероятностью окупятся при реализации следующей крупной новой функции.
Корифеем рефакторинга считается Мартин Фаулер (Martin Fowler), хотя, разумеется, принципы приведения кода в порядок издавна известны программистам. Интересная новая область — инструменты рефакторинга, то есть программы, частично автоматизирующие такую работу. До полного спектра всех необходимых инструментов здесь еще далеко — в большинстве сред программирования нельзя даже изменить имя переменной (с автоматическим изменением всех ссылок на нее). Но ситуация улучшается, и если вы хотите основать небольшую компанию, торгующую вразнос безделушками для автоматизации программирования, или внести полезный вклад в виде open source, перед вами полный простор.