Хоть и безобразно, но единообразно
Здравствуйте! Сегодня мне хотелось бы обсудить с вами один армейский принцип, который может оказаться невероятно полезным при написании и поддержке вашей кодовой базы.

Почему единообразие — это не про красоту, а про выживание
Давайте начнём с осмысления принципа, отражённого в заголовке статьи. Представьте себе 100 человек в вашем подчинении. У половины из них надеты уставные сапоги или фуражки, у другой половины — нет. В итоге для вас, как для командира, предпочтительнее, чтобы все были одеты единообразно, пусть даже не по уставу, чем чтобы половина была уставной, а половина — нет. И это касается не только формы, но и любого аспекта воинского устава: от распорядка дня до способа связи или подачи доклада. Если где‑то наблюдается отклонение, то пусть оно будет единообразным для всех до тех пор, пока не удастся вернуть всё к уставному стандарту.
Почему так? Единообразие снижает энтропию, упрощает управление личным составом (даже если не все стандарты строго уставные) и повышает дисциплину и сплочённость. Оно помогает командирам поддерживать порядок и быстро выявлять несоответствия. Точно так же в кодовой базе единообразие облегчает контроль, понимание и восприятие всего проекта.
Легаси как место столкновения стилей
Допустим, вам достался легаси‑проект, который теперь нужно развивать и поддерживать. Худшее, что вы можете сделать, — запрыгнуть со своим уставом в чужой монастырь и начать внедрять свои порядки хаотичным и фрагментированным образом.

Начнём с нескольких простых примеров:
-
Наименования. Вы видите какое‑то название, которое вас не устраивает. Например, в коде везде написано
usDt
, а вы точно знаете, что это плохое сокращение, и его определённо нужно писать какuserData
. И вы сразу же начинаете в новом коде именовать переменные правильным для вас образом, не переделав старый код. Это сразу же создаёт вам проблемы: по сути, одни и те же вещи именуются по‑разному, что затрудняет чтение и поддержку кода. Кроме того, поиск нужного элемента во всём коде становится сложнее, ведь приходится искать сразу несколько вариантов одного и того же названия. Чтение кода тоже усложняется. Поскольку ваш мозг имеет свойство распознавать паттерны и, работая с ними длительное время, быстрее воспринимать привычные обозначения, вам придётся постоянно перестраиваться, переключаясь между разными частями кодовой базы. -
Называние сущностей. В проекте что‑то названо не так, как должно быть на уровне бизнес логики приложения. Допустим, сущность
developer
на самом деле должна называтьсяadmin
в соответствии с её функциональностью. Возможно, изначально в проекте админами были только разработчики, но потом появилась возможность выдавать расширенные права и другим пользователям. И вам очень хочется сразу использовать новые, правильные термины в новом коде, не отрефакторив старый код.В результате кодовая база превращается в хаос: в одних местах сущность всё ещё называется
developer
, в других ужеadmin
, и теперь вам приходится постоянно держать в голове, что это одно и то же. Поиск по коду снова усложняется, потому что вам приходится искать сразу оба варианта. Опять же, чтение и поддержка кода становятся менее удобными, так как мозг ожидает единообразия, а вместо этого сталкивается с непоследовательностью, заставляя вас тратить лишнее время на переключение контекста. -
Структура HTTP endpoint‑ов API. Некоторые HTTP‑ручки вашего API имеют структуру, которую вы считаете неверной. Допустим, в проекте повсеместно используется подход, при котором все параметры передаются в строке запроса —
/api/users?user_id=123
, а вы уверены, что правильнее передавать идентификаторы через путь к ресурсу —/api/users/123
. И вам очень хочется сразу писать новые endpoint‑ы по правильному, на ваш взгляд, стандарту, не трогая старые. В результате API становится неунифицированным: одни ручки используют один стиль, другие — другой. Это создаёт путаницу как для вас, так и для внешних потребителей API, которым теперь приходится разбираться с разными схемами работы. Интеграции усложняются, документация становится менее понятной, а поиск и сопровождение API требуют больше времени, так как больше нельзя просто полагаться на единообразие структуры endpoint‑ов. -
Фронтенд. Вы работаете над фронтендом приложения и понимаете, что вам не нравится, как выглядят отдельные части формы: например, поля для ввода текста. И вы решаете, что прямо здесь и сейчас замените их на новый компонент с другим API и внешним видом.
Но без чёткого плана и рефакторинга может легко случиться так, что из‑за более приоритетных задач остальные страницы так и останутся со старыми полями. В результате интерфейс вашего приложения теряет визуальное единство: в одних местах используются старые компоненты, в других — новые, и пользователь постоянно сталкивается с разными вариантами оформления одного и того же элемента. Это вызывает фрустрацию, снижает удобство работы с интерфейсом и делает продукт менее цельным и продуманным.
Очень плохая идея начать писать новые фичи, используя подходы, чуждые проекту, который вы поддерживаете. Пара итераций передачи проекта из команды в команду или смена разработчиков могут привести к тому, что в кодовой базе окажется три варианта названия одних и тех же сущностей, четыре разных способа реализации одних и тех же задач. Либо в приложении появятся страницы и формы с хаотично различающимся внешним видом, что может длиться годами.
И даже если некоторые из этих решений окажутся лучше других, в целом это значительно усложнит поддержку и понимание кодовой базы. Время, необходимое для введения новых разработчиков в проект, существенно увеличится, поскольку им придётся постоянно переключаться между разными подходами, зачастую даже не понимая, какой из них правильный. Со временем возрастёт количество багов, возникающих из‑за разрастающегося хаоса, а также значительно увеличится время на внедрение новых фич. Но как же поступать, если старые, неправильные подходы однозначно нужно устранять?

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

Второе правило: изучайте тот проект, с которым вам приходится работать. Пытайтесь понять логику и стиль, которым руководствовались предыдущие разработчики. Не всегда это бывает просто и очевидно, но навык распознавания таких подробностей поможет вам не только лучше понять проект, но и осознать собственные подходы к написанию кода. Здесь стоит упомянуть pull request‑ы и code review. При их изучении вы должны искать отклонения от принятого стиля написания кода, обращать внимание на несоответствия используемым подходам и практикам, а также указывать, где можно взять готовое решение, чтобы не плодить лишние реализации одной и той же функциональности.
Кстати, об этом стоит поговорить отдельно. Исследование от GitClear за 2024 год (на основе анализа 211 миллионов строк опенсорсного кода) показывает чёткую тенденцию увеличения количества кода вида «копировать/вставить» и стремительное сокращение перемещённого/отрефакторенного кода. Это очень плохой тренд, вызванный повсеместным использованием ассистентов по написанию кода в IDE. Часто IDE выдают в качестве подсказки готовое решение, которое можно вставить одним нажатием клавиши Tab. Но такое же решение в готовом виде может уже присутствовать в кодовой базе, а порой и в нескольких экземплярах. Именно это способствует стремительному наращиванию энтропии в коде. Результат становится всё более очевидным и болезненным с течением времени, когда всё больше багов появляется из‑за того, что, изменяя одну часть кода и его логику, вы оставляете идентичные фрагменты с устаревшей логикой в других местах проекта.
Третье правило (принцип «разработка через болтологию»): больше общайтесь в команде. Обсуждайте, как и что вы делаете, какие подходы используете, как лучше реализовать те или иные задачи, вызывающие сомнения. Это не только приводит к более чёткому пониманию всей командой того, как пишется код, но и помогает вырабатывать лучшие решения для проекта благодаря плодотворной дискуссии. В конечном итоге ваша цель — кодовая база, которая выглядит и читается так, как будто её писал один человек, с единым стилем и понятными подходами.
Четвёртое правило: используйте линтеры и автоформатирование при сохранении. Да, может быть, в 2025 году это звучит для многих странно, но всё ещё есть команды и разработчики, которые решительно отвергают возможности автоформатирования кода, предпочитая вручную передвигать скобочки и отступы. Однако если вы действительно хотите добиться хорошо читаемого и единообразного проекта, то автоформатирование — одна из лучших вещей, которую можно внедрить в команде. Лично я предпочитаю максимально детерминированные решения, где почти нет места для обсуждения настроек форматирования и линтинга.
На одном конце спектра можно привести пример JavaScript и тот зоопарк подходов, применяемых в разных командах, как должен выглядеть и писаться код: разрозненные файлы с настройками линтера, которые ещё кому‑то нужно дополнительно настроить. А на другом конце — язык Go с его фактически принятым на уровне стандарта инструментом gofmt. Мне особенно нравится сформулированный в Go принцип:
gofmt’s style is nobody’s favorite, yet gofmt is everybody’s favorite. — Стиль gofmt никому не нравится, но gofmt нравится всем.
Также замечательный тезис к этому принципу прозвучал на Gopherfest 2015 года:
То, как gofmt форматирует код, — это даже не то, как Роберту Гризмеру нравится, как выглядит код, а ведь он написал эту программу.
Пятое правило: каталогизируйте, по возможности, все разночтения в подходах, которые вы находите в разных частях кодовой базы. Если некоторые вещи сделаны до вас в разных местах программы совершенно разным образом, то ваша первоочередная задача — постараться определить такие расхождения. А после нужно решить:
-
Выбрать какой‑то из подходов, который уже используется, и последовательно при возникновении возможности приводить всё к этому единому варианту, начиная все новые фрагменты с использованием этого подхода. Даже если он вам не очень нравится, лучше придерживаться именно его, так как уже есть значительный объём кода, сделанный именно таким образом.
-
А если ни один из наблюдаемых вами в коде подходов не подходит для гармоничного развития кодовой базы, то нужно выработать новый подход самостоятельно и вернуться к предыдущему правилу. Одновременно с внедрением вашего нового подхода у вас уже должен быть сформирован план, каким образом и примерно в какие сроки вы будете устранять все разночтения.

Заключение: у каждого проекта должен быть тот, кто заботится о нём душой
Главный принцип во всём, что вы делаете, — знать меру и искать золотую середину. Иногда, волей‑неволей, приходится принимать решения, противоречащие этим принципам. Возможно, кодовая база настолько огромна и плохо написана, что вы решите полностью переписать проект. В таком случае новые фичи реализовывать следует исключительно в новой, специально созданной кодовой базе, куда итерационно будет переноситься старая функциональность, а в старую добавлять только исправления в рамках технической поддержки. Однако при каждом таком шаге необходимо чётко понимать, почему вы отступаете от первоначальных принципов и к каким последствиям для развития кодовой базы это может привести. Худшее, что может случиться с проектом, — превратиться в «проект‑сироту», о котором никто не заботится и никто не думает в долгосрочной перспективе. Поэтому старайтесь находить инженерное удовлетворение даже в работе с легаси‑кодом, если он был передан вам во владение. Со временем, с каждой итерацией рефакторинга вам будет всё приятнее осознавать, насколько сильно проект преображается благодаря вашим усилиям.
Автор: CodeShaman