Совершенный код и реальные проекты



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

Но, увы, всё это великолепие разбивается о суровую действительность и реальные проекты. Если мы говорим о продакшн-проекте, то пользователей не волнует, насколько красив ваш код и насколько хороша архитектура, их волнует, чтобы проект хорошо работал. Но я всё равно считаю, что в любом случае нужно стремиться писать правильно, просто при этом фанатизма быть не должно. После чтения различных холиваров на тему правильных подходов к написанию кода мне в глаза бросилась одна тенденция: каждый пытается применить означенные подходы не в целом к программированию, а только к своему опыту разработки, к своим проектам. Многие не осознают, что хорошие практики — это не абсолютные правила, которые должны строго соблюдаться в 100% сценариев, это лишь советы о том, как следовало бы поступать в большинстве ситуаций. На каждую хорошую практику всегда можно придумать несколько дюжин примеров, в которых она работать не будет. Но это вовсе не означает, что хорошая практика не такая уж и хорошая, просто её применили не к месту.

Есть и другая проблема — некоторые программисты не настолько хороши, как им кажется. Часто приходится видеть такую ситуацию: подобный разработчик увидел в большой статье про совершенный код какое-то одно соображение (не вникая в контекст) и начал его повсюду применять, отчего код этого разработчика стал значительно хуже. А потом приходится слышать: «вот начитаются тут статей про такие-то подходы, а потом начинают так-то писать». А может, виноваты тут вовсе не статьи? Если некоторые программисты криво и не по месту применяют где-то услышанные хорошие практики, то это не значит, что правильные подходы к программированию не нужно обсуждать. Стремление писать хорошо — это похвально, но при этом нужно трезво оценивать свои способности. Для пилотов самолётов описано много манёвров из разряда «высшего пилотажа», но это вовсе не значит, что каждый начинающий пилот должен все их попробовать в свой первый же полёт. Так и джуниор-программист после прочтения книги банды четырёх не должен в свой следующий проект лепить все несколько десятков паттернов, которые он теперь «знает».

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

Размер проектов

Маленькие проекты

Например, имеется 1 человек, который пишет проект несколько дней. Это какая-нибудь мелкая утилита для решения какой-нибудь небольшой отдельно взятой задачи. Скорее всего, она не будет особо развиваться и превращаться в нечто большое. В таких проектах все элементы системы (а может и все переменные) можно удержать в уме. В таком проекте вам не нужна крутая архитектура: если появилась какая-то подзадача, которую можно решить костылём на две строчки, то лучше так и сделать. Конечно же, можно потратить пару дней на выработку сложной архитектуры, которую будет очень удобно использовать при появлении новых сложных подзадач. Но есть нюанс: эти подзадачи вряд ли появятся в маленьком проекте, а время уже будет потрачено. В данной ситуации не стоит особо заморачиваться на написании сверхчистого кода. Быдлокодить тоже не следует, просто старайтесь писать нормально.

Средние проекты

Допустим, у нас уже 5-6 человек и проект на несколько месяцев. Тут уже особо похалтурить не получится, нужно более или менее продумать систему, структурировать весь код. Лучше бы следить, чтобы костыли особо не накапливались. Можно потратить какое-то время на предварительный анализ и проектирование, но не сильно много. Это похвально, если до дедлайна вы успеете составить идеальный план построения идеальной системы, но лучше бы вы успели написать не особо идеальную, но работающую систему. На худой конец, если всё пошло не так, то за несколько дней совместными усилиями не так уж и сложно всю архитектуру переделать. ( Мне приходилось несколько раз поступать подобным образом, это не так страшно. ) Если это, скажем, проект на заказ, то заказчик будет вам платить не за чудесный код, а за рабочий функционал, реализованный в срок. Не стоит об этом забывать.

Большие проекты

А теперь у нас несколько десятков человек и проект, который будет писаться несколько лет. Вот тут бы лучше бы нам очень хорошо продумать архитектуру с самого начала. И если появилась потребность вставить костыль, то, возможно, стоит на ранних этапах переделать архитектуру так, чтобы новая функциональность красиво в неё вписалась. Каждая халтура, сделанная сейчас, обернётся ужасными страданиями через год-другой. Читайте книги про чистый код и правильную архитектуру — там много советов, которые вам пригодятся. Только их нужно применять по месту, а не везде подряд.

Мне очень нравится байка из книжки Мартина Фаулера: Фаулер занимался консультированием одной фирмы по разработке достаточно большого проекта. Проект был написан ужасно, и Мартин настоял на небольшом рефакторинге. После пары дней работы удалось удалить половину кода без какого-либо ущерба для функциональности системы. Программисты очень радовались, а вот начальство не было довольно — ведь эта работа не привела к появлению нового функционала. Старый код прекрасно работал, деятельность по его «чистке» не казалась экономически оправданной. Поэтому последующим советам руководство не вняло, настаивая на скорейшем появлении нового функционала без какой-либо дополнительной работы над кодом. Через полгода проект закрылся, т.к. код стал слишком сложным для поддержки.

Поддержка проекта

Проекты без поддержки

Деятельность, хорошая знакомая фрилансерам и аутсорсерам. После сдачи проекта вам больше никогда не придётся вспоминать про весь этот ужас, который таится под капотом. В глубине души вы надеетесь, что проектом будут просто пользоваться, а исходники никто никогда не откроет. И это допустимый подход, ведь от нас требуется не замечательный код, а рабочее приложение. В начале проекта вы ещё можете позволить себе попроектировать архитектуру, пописать чистый код, но когда до дедлайна останется два дня, а функционал реализован лишь наполовину, то тут не до высоких материй. Позволительно вбивать любые костыли, нарушать мыслимые и немыслимые подходы к хорошему коду. И в данном случае это нормально. Я не говорю, что это хорошо, я не агитирую всех всегда так поступать. Но это нормально. Тут не идёт речь о программировании как об искусстве, тут идёт речь о проекте, который нужно сдать в срок и не нужно поддерживать. Если вы начнёте писать всё идеальным образом, то вы просто рискуете не уложиться в сроки — вы подведёте заказчика, не получите денег, потратите своё время, а код всё равно окажется никому не нужным. Всегда помните о своих непосредственных целях.

Проекты с поддержкой

А вот тут я бы постарался писать нормально. Чтобы и архитектура нормальная была, и код был чистенький. Это такое прекрасное ощущение, когда заказчик просит добавить какую-нибудь нетривиальную функциональность, а вы справляетесь с задачей за час — ведь новый код добавить легко, поскольку он прекрасно ложится на существующую архитектуру. С уже готовой кодовой базой очень легко работать, код весьма понятен, в нём просто ориентироваться. А есть и другое ощущение, когда заказчик просит сделать какую-нибудь мелочь (а заказчику-то безумно очевидно, что это действительно мелочь, сделать её должно быть очень-очень просто), а вы смотрите на сложившуюся какофонию классов, прикидываете сколько дней нужно потратить на эту мелочь, но к клавиатуре прикасаться уже почему-то не хочется. Да и смотреть на этот код просто противно.

Масштаб проекта

Внутренние проекты

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

Публичные проекты

Тут у нас уже совсем другая ситуация. Лучше бы вам нормально документировать ваш проект, чтобы по нему не возникало каждый день по сотне вопросов от ваших любимых пользователей. И лучше бы писать документацию на английском (как, впрочем, и комментарии). Да и код лучше бы писать почище, чтобы человеку со стороны было легко в нём разобраться. Если же у вас есть API, то хорошо бы его продумать, а не просто налепить какой-то интерфейс, из которого при большом желании как-то можно вытащить все нужные данные. Помните, что проект принадлежит уже не только вам, но и сторонним программистам — уважайте тех, кто будет работать с вашим кодом. Пишите программу так, чтобы вас потом не хотели поймать в тёмной подворотне и сделать с вами плохие вещи.

Проекты со спецификой

Проекты с высокой нагрузкой

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

Проекты с использованием сторонних библиотек

Когда я начинаю говорить о сторонних библиотеках, то некоторые мои коллеги начинают смотреть на меня понимающим взглядом. И я вижу по их лицам — они испытали те же страдания, что и я. В реальных условиях большого проекта вы редко будете писать сами абсолютно весь функционал. Обычно попадаются весьма распространённые задачи, которые уже кто-то решал. В данной ситуации намного разумней будет взять готовое решение, чем самому изобретать велосипед. Так-то оно так, но порой оказывается, что авторы этого готового решения не очень хорошие программисты. Их проект справляется со своей основной задачей, но написан он... Ну, не совсем профессионально. И интегрировать его в ваш проект... Ну, несколько сложновато. Это обстоятельство в очередной раз заставляет вас писать ужасные костыли, уродующие вашу милую архитектуру. Но это очередная производственная необходимость, ведь реализовывать этот функционал самостоятельно зачастую бывает нерентабельно. (Скажу по секрету, несколько раз я не выдерживал и писал свою библиотеку вместо сторонней. Но это скорее исключение, чем правило.)

Проект команды новичков

Часто можно слышать заявление, что любой средний разработчик должен знать то, сё и это. Предполагается, что разработчик неплохо владеет языком (скажем, если речь идёт об ООП-языке, то нужно хотя бы знать что такое полиморфизм и наследование), может без проблем воспринимать сложные синтаксические конструкции, понимает основные механизмы платформы, знает элементарные паттерны проектирования (увидев класс с названием Visitor, он сразу поймёт определённый фрагмент системы), легко читает комментарии на английском и ещё много чего умеет. Но до становления хотя бы средним разработчиком все в своё время были новичками. И на сегодняшний день в мире программирования существует много людей, которые только начинают постигать это ремесло. Вполне нормально, что они не знают и не умеют многих вещей. Вполне нормальна ситуация, когда собираются несколько начинающих программистов и пишут небольшой проект. В процессе разработки они многому научатся. Разумеется, они будут делать многие вещи не совсем правильно. Но от них и не стоит этого ожидать. Хорошо бы, чтобы старшие коллеги подсказывали разные моменты: как переписать код получше, какие книжки почитать. Но именно предъявлять к ним требования наравне с сеньорами не следует. В проекте новичков позволительны очень многие отступления от того «как надо», ведь они только учатся. Советы и рекомендации — это хорошо, а требование написать с первого раза идеальную архитектуру с идеально чистым кодом — это не совсем хорошо.

Не-продакшн проекты

Демонстрационные проекты

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

Академические проекты

В этом проекте мы уже особо ничего никому не показываем, а сами разбираемся с какой-нибудь интересной штукой. Скажем, изучаем какой-нибудь алгоритм. Вполне нормально написать рядышком 10 версий алгоритма. Возможно, версии будут на разных языках. Вполне нормально будет наплевать на соглашения об именованиях (которые хорошо бы соблюдать при разработке реального проекта) и назвать одинаковые вещи одинаковыми именами — такими, которые указаны в книжке. Да какая разница, как там в каком языке принято писать? Мы тут с алгоритмом разбираемся, сейчас нас не должны волновать такие вещи. Если вы собираетесь показывать кому-то результат, то тут над кодом можно уже поработать, но это будет совсем другая история. А пока вы находитесь в стадии изучения, ваша основная цель — это именно изучение, а не написание совершенного кода. Конечно, эти вещи можно совместить, можно постараться написать самую идеальную реализацию алгоритма, но это вовсе не обязательно. Главное — не подменять основную академическую цель целью совершенного кода.

Локальные проекты

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

Но помните, что ситуация совершенно меняется, когда творческий процесс заканчивается и нужно будет показать свои результаты другим (скажем, отправить локальные наработки в центральный репозиторий). Перед этим код обязательно нужно «причесать». Все наши эксперименты, костыли и лишние комментарии нужно убрать, оставшийся код сделать максимально понятным и читаемым. Уважайте тех, кто будет разбираться с вашей писаниной.

Проекты-прототипы

Задача таких проектов — по-быстрому накидать определённую функциональность, чтобы стало более понятно, как она будет выглядеть. Это весьма разумный подход. Скажем, есть у нас штук 5 вариантов реализации: мы пробуем кратко накидать общий концепт каждого варианта. После этого можно посмотреть все подходы на живых примерах и выбрать тот, на основе которого будет строиться основной проект. Очень важно правильно понимать задачи прототипирования. Вам не нужно вычитывать такой код, вам не нужно пытаться написать его идеально. Меня всегда печалят люди, которые ругаются на прототип с заявлениями вида «вот эту переменную можно назвать немного понятнее» или «а вот эту кнопку в интерфейсе хорошо бы подвинуть на 1 пиксель влево». Да какая тебе разница, как там переменная названа, это прототип, отстань от него. Такие обсуждения можно проводить уже на готовом проекте, но на уровне прототипа это делать бессмысленно.

Увеселительные проекты

Помню, как-то раз мы с друзьями решили сделать одному хорошему человеку подарок на день рождения. Мы сделали Java-проект, который в ООП-виде представлял его жизнь, его друзей и разные интересности, с которыми он взаимодействует. Причём программа действительно работала, из консоли можно было выполнять разные весёлые команды. Что касается исходного кода, то абсолютно все именования (классы, методы, переменные и т.п.) были написаны на русском (добрая Java позволяет так делать). Javadoc был также написан на русском и не нёс в себе никакой полезной информации. Логика была реализована самым простым способом. Вместо быстрых сложных алгоритмов мы использовали самые простые. Архитектура была не особо красива, ведь мы её даже не пытались продумывать.

А подарок удался, хоть мы и не использовали ни одной хорошей практики по написанию совершенного кода. А всё дело в том, что цели у проекта были совершенно иные.

Резюме

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

Кросс-посты

Поделиться:
Исходный код поста находится на GitHub:
https://github.com/AndreyAkinshin/aakinshin.net/blob/master/ru/_posts/dev/perfect-code-and-real-projects.md