Программизм: история одной болезни

:

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

Стадия первая. Рождение


«Я программист. Я олимпиадник. Я знаю что такое «о»-маленькое. Я знаю, что такое «О»-большое. Я понимаю, чем отличается «эн-квадрат» от «эн-факториала» и почему они оба стыдливо прячутся при виде «эн-логарифм-эн». Сейчас я приду на проект и перепишу эту тормозную кашу из кода так, что она будет работать в много раз быстрее! Смотрите, я знаю алгоритм Кнута-Морриса-Пратта! А здесь можно сэкономить одно сравнение строчек на равность! А если эту рекурсию развернуть в цикл, то за счет экономии вызовов методов и выделения памяти в стэке… Что, программа тормозит? Сейчас я посмотрю код… Вот! Смотрите, здесь вместо двух вложенных циклов можно написать один и использовать бинарный поиск вместо внутреннего!»

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

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

Стадия вторая. Идеализм и самоуверенность


«Я разработчик. Да, именно так, я разработчик программного обеспечения, а не программист. Мое задание – разработать продукт, который будет стабильно работать и состоять из правильного кода. Код, который я пишу, должен работать сначала правильно, а потом уже быстро. Код должен быть структурированным и откомментированным – возможно его будут поддерживать другие разработчики. ООП – мой главный инструмент, я умею анализировать предметную область и выделять в ней иерархии классов. Все, что можно описать шаблонами или дженериками описывается шаблонами или дженериками. Я применяю декларативное прораммирование везде, где это возможно, ведь этот инструмент делает код максимально читабельным и понятным. Я использую кодогенерацию ведь это экономит мое время на написание кода. Я использую только новые технологии. Я провожу много времени в спорах с коллегами, какой паттерн или технология лучше подходят для решения поставленной задачи и правильно ли использовать здесь тот или иной подход...»

А это уже вторая. Пациент повзрослел и набил много шишек во время предыдущей стадии. Изречение Кнута о том, что преждевременная оптимизация – корень всех бед, выучено на память и выжжено каленым железом на теле пациента. Больше всего на свете его волнует уже не быстродействие, а красота кода.
Гигантские деревья иерархий классов покрывают проект густым лесом, с их развесистых крон свисают специальные аттрибуты, дженерики, интерфейсы. В гуще этого леса монолитными скалами возвышаются фабрики объектов, бродят функции обратного вызова, наблюдатели, итераторы, посетители, контроллеры и другие представители племени поклонников банды четырех. Где-то в глубине леса генерируется код, который в свою очередь генерирует код, который генерирует код, который генерирует XSLT-шаблон, который генерирует интерфейс пользователя. Весь код обильно покрыт комментариями о том, как его правильно использовать и что он делает. Восторг у пациента вызывают проекты, в которых он не способен разобратся быстрее чем за несколько дней из-за нетривиальной объектно-ориентированной структуры.
Изменения в требованиях часто сопровождаются крупномасштабным рефакторингом, введением еще одного абстрактного уровня, выделением новых сущностей и применением новых технологий и подходов, про которые только что прочитал на Хабре. После прочтения статьи о защитном программировании он начинает пихать ассерты во все методы. После знакомства с кешированием данных в памяти он кеширует все данные в проекте. Познакомившись с TDD он пишет кучу тестов для элементарной логики прежде чем написать саму логику. Плод его трудов монументален и вызывает восторг у коллег находящихся на этой же стадии. Он искренне верит, что язык, на котором он пишет, идеален, а технология самая современная. Часто пациент заводит блог, в котором пишет о том как правильно писать код или использовать определенный фреймворк, описывает найденные в своем коде баги и остроумные, как ему кажется, пути их устранения и предупреждения. Он подписывается на блоги евангелистов его любимых технологий и внемлет их словам как истине последней инстанции.
Когда его ставят перед фактом, что его детище тормозит, он берется за профайлер и пытается разобратся с его помощью в дереве вызовов высотой в тысячу этажей. Иногда это удается и проблема решается… да, введением еще одного абстрактного уровня с дополнительным кешированием. Или наоборот, часть логики переносится на сервер базы данных в виде хранимых процедур.
При приближении сроков сдачи проекта пациент внезапно понимает, что слишком многое еще не готово, а время потрачено на постоянные рефакторинги и споры о том, какой паттерн выбрать. Он ночует на работе чтобы успеть в срок и иногда ему это удается. Впрочем, после сдачи проект надо еще поддерживать, дописывать новую функциональность для следующей версии, удовлетворять требования новых пользователей и тут, опять же, внезапно, оказывается, что новый функционал практически невозможно вписать в монолит проекта, не разрушив его до основания. Так как времени на тотальное разрушение не выделяют, ведь продукт уже работает на продакшене и этот баг надо было пофиксить еще в прошлую пятницу, то код начинает обрастать костылями, подпорками, быстрыми и грязными исправлениями с комментариями «todo: fix this later». А чем монументальнее конструкция, тем монументальнее подпорки и костыли для нее.
Проблема пациента в том, что он потерял содержание за формой. Он пишет код так, как художник рисует картину: с восторгом, с восхищением, он влюблен в этот код и часто забывает о том, что основная цель его работы написать не красивый код, а продукт, который будет работать, будет работать быстро и будет легко поддерживаемым. Он часто забывает и о том, что программный продукт – это товар, который должен найти своего покупателя, которому ( нет, вы только подумайте!) совершенно плевать, какова концентрация паттернов на строку в коде программы. Зато этот пользователь готов заплатить деньги за то, что в продукт добавят новую фичу, о которой никто не догадывался при написании первой версии, при чем добавление этой фичи не должно отвалить ни единой существующей функции проекта и времени этот процесс должен занять минимум. И да, пользователю правда важно, что бы у этих кнопочек были кругленькие уголки.

Стадия третья. Просветление


«Кажется я ошибаюсь. Нет, определенно, я ошибаюсь.»

Да, третья стадия – это осознание пациентом того, что он находится на второй стадии болезни и понимание собственных недостатков и ошибок. Осознание болезни ведет к исцелению.
Иерархии классов в проекте редко превышают два-три уровня наследования, методы содержат не более пары десятков строк, а чаще всего меньше десяти. Код практически не содержит комментариев, за исключением внешних интерфейсов, с которыми могут работать сторонние разработчики, не имеющие доступа к коду. На удивление, остутствие комментариев не делает код нечитабельным – правильные имена переменных и методов, четкий code-convention и небольшой объем кода творят чудеса. Если действие можно описать просто и без применения сложных паттернов проектирования – то оно будет описано именно таким образом. Если существует вероятность того, что более сложное решение упростит жизнь в будущем, то оно будет реализовано ровно настолько, чтобы обеспечить это упрощение.
Декларативное программирование используется в тех местах, где это не вредит производительности. Код генерируется, но только там, где это экономит время разработчиков, при чем в результат генерации максимально простой и отлаживаемый, даже если для этого приходится генерировать в десять раз больше кода. Юнит-тесты покрывают лишь часть кода, но это именно та часть, которая может пострадать в случае ошибки при внесении новой функциональности или исправлении бага. Прежде чем добавить кеширование данных из базы, пациент обязательно задумается о том, насколько это необходимо, и не повредит ли масштабируемости решения, точно так же, как и в случае переноса логики в код хранимой процедуры на сервере базы данных. Он понимает, что покупка еще одного сервера может в итоге обойтись намного дешевле, чем переписывание и тестирование части проекта. Впрочем, в тех местах, где оптимизация может дать серьезный прирост производительности она… не делается до поры до времени, но код пишется таким образом, чтобы в случае необходимости можно было легко ускорить его выполнение. Если это, конечно, будет нужно.
Щенячий восторг от технологий и языков сменяется более спокойным отношением – всему свое время и место. Пациент понимает, что каждый инструмент хорош для определенного круга задач, поэтому не принимает участия в холиворах на тему «динамические языки vs. статические», «Java vs. .Net vs. C++» и т.п. Он с удовольствием пробует новые языки и технологии для своих собственных проектов, но решается на их использование в рабочем коде только после внимательного взвешивания всех «за» и «против» и опробирования новшевств на прототипе или отдельной ветке проекта. Он критически относится ко всему новому, пытаясь выделить в нем как плюсы так и минусы и внимательно выслушать все стороны спора, если таковой имеет место быть. Он хорошо знает несколько языков и технологий, достаточно много, чтобы решить любую поставленную перед ним задачу, но достаточно мало, чтобы овладеть ими в нужном ему объеме. Изречения евангелистов и гуру не воспринимаются им как истина последней инстанции – он пытается понять не столько что они сказали, а почему и почему это имеет смысл. Если пациент понимает, что в определенном случае собственный велосипед может быть эффективнее существующих технологий – он пишет собственный велосипед, но только тогда, когда докажет сам себе его необходимость. Если велосипед доставляет ему удовольствие своей эстетикой и остроумными решениями и это не противоречит соглашению с работодателем – он не гнушается опубликовать код под оупен-сорсной лицензией или хотя бы в общих чертах описать решение в своем блоге. И да, если эти идиоты и правда хотят видеть закругленные уголки в кнопках, то пациент обязательно их сделает. Впрочем, пациент ли?

Вместо послесловия


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

Спасибо.