Любой программист, вне зависимости от уровня, когда-то сталкивался с этим. Ты садишься за проект, полный планов и идей, предвкушаешь, как создашь нечто совершенное, но... проходит час, второй, а на экране только десяток папок, пара заглушек и файл README.md. И вот уже начинаешь возвращаться к документации в поисках “правильной” структуры проекта или подсматриваешь, как сделали другие.
Примерно то же самое происходит, когда ты работаешь в команде: собрания перетекают в бесконечное обсуждение архитектуры, абстракций и фреймворков. Идеи летят кометами, все соглашаются, что “всё должно быть как надо”, но реальная работа — застопорилась. Потому что команда вместе с тобой подсознательно ждет чего-то: идеального старта.
Синдром идеального старта — не просто миф, а настоящая преграда для продуктивной разработки. В этой статье мы поговорим о том, почему ждать "идеальности" — значит тормозить, как такой подход мешает развитию, и что с этим можно сделать.
Зачем мы ждём идеального старта?
Давайте начистоту. Инженерный подход прививает большинству программистов стремление к "чистоте" и "вечному". Важно, чтобы проект был построен на устойчивом фундаменте, который можно будет развивать много лет. Но эта стремительность к "безупречности" часто оборачивается довольно токсичной привычкой откладывать реальные действия, пока не будет найдено "идеальное решение".
Проблема в том, что идеальное, как правило, недостижимо. Вот почему:
Мир меняется быстрее, чем мы пишем код. Пока вы анализируете новый фреймворк или паттерн, релизнется его следующая версия, ваше окружение изменится, а требования проекта могут вообще перерасти из веб-приложения в мобильное.
Мы не можем всё предусмотреть. На старте проекта кажется, что нужно принять сразу сотню решений: будет ли разделение на микросервисы? Поддерживать ли monorepo? Какими должны быть слои абстракции? Но в большинстве случаев это приводит к параличу. Пока вы думаете, реальная работа замедляется.
Боимся "испачкать" кодовую базу. Привычка к чистому коду иногда мешает. Мы так сильно отталкиваемся от идеи, что код должен быть понятным, тестированным и стильным прямо с первой строчки, что боимся писать что-то, что выглядит "не идеальным".
Почему важно перестать ждать идеального старта?
В программировании, как и в жизни, самое важное — движение. Делать маленькие изменения быстрее и поступательно гораздо эффективнее, чем тратить дни на создание теоретически идеальной структуры или идеологически верной архитектуры.
Вот несколько причин:
Решения всегда меняются по мере роста проекта. На стадии планирования вы даже не представляете, какие реальные вызовы ждут позже. Проект определяет архитектуру, а не наоборот.
Рабочий прототип важнее абстрактных концепций. В большинстве случаев сделать что-то работающее, пусть даже далеко от совершенства, — это уже половина пути. Реальный код порождает не только движение вперёд, но и обратную связь.
Процесс — это опыт, который впоследствии улучшит систему. Даже если начальный код будет "грязным", путь проложен. А рефакторинг (да-да, любимое слово) — верный друг любого разработчика.
Польза для бизнеса. Важно помнить, что деньги зарабатываются не за "идеальный код". Заказчику, продукту или пользователю важно, чтобы это работало. Код всегда можно улучшить позже, а вот время вернуть не получится.
История с попыткой «идеального начала»
n-нное количество лет назад мне воочию пришлось наблюдать такие стремления к "правильной архитектуре со старта". Это была разработка приложения для внутреннего использования: большой дашборд для анализа данных, интерактивные таблицы, графики, интеграция с несколькими API. Задача амбициозная, но на старте всё шло довольно быстро: дизайн-систему выбрали, фреймворки обсудили, даже базовую структуру за несколько дней собрали.
А дальше понеслась... Команда, уставшая от поддержки "старых" проектов, проникнувшаяся идеей, что старый проект плохой потому что
он старый и вообще legacy
на рефакторинг времени бизнес не дает, поэтому всё там плохо
пустилась во все тяжкие в стремлении "сделать круто", современно и далее по тексту.
Казалось, что решение любой задачи должно быть сразу на высшем уровне: например, чтобы каждое взаимодействие с API было строго типизированным, универсализированным, а обработчики ошибок настроены по всем лучшим практикам. Начали выстраивать кастомный механизм взаимодействия со всеми сервисами, на тот случай, если их API когда-нибудь изменится, рисовали зубодробительные ролевые модели и логику доступов… и что в итоге?
Через месяц у нас не было вообще ничего полезного, кроме горы абстракций, десятков интерфейсов и базового логирования. Как в том бородатом меме:
У нас было 35 строчек кода, 2 цикла, 2 конструкции try/catch для безопасности, 4 условия if/else и целое множество отступов и костылей. Не то чтобы это был необходимый запас для решения этого задания. Но если начал писать г*внокод, становится трудно остановиться. Единственное что вызывало у меня опасение была рекурсия. Нет ничего более запутанного чем рекурсия. Я знал что рано или поздно мы перейдем и на эту дрянь.
В конце концов всё пришлось просто выбросить и начать заново, максимально упростив подход. Мы сосредоточились на базовых сценариях: нужно ли сразу заморачиваться с универсальным API-модулем? Нет. Создаём обычные функции для каждого сервиса. Требуется ли нам с первого дня универсальная абстракция для обработки данных с фронта? Нет. И т.д. и т.п. per rectum ad astra, как говорят в русских селениях.
Мы быстро собрали рабочий прототип, который можно было показывать бизнесу. И знаете что? Многие добавленные фичи, которые мы так хотели реализовать в начале, просто не понадобились вовсе. Если бы мы их сразу сделали, они были бы пустой тратой времени.
Парадокс идеального старта
Самое интересное в гонке за идеальным началом работы в том, что в реальной жизни он никогда не наступает. Вот пара причин, почему это происходит:
Идеальная архитектура рождается только из практики. Пока проект живёт только в голове, у вас нет полной картины — лишь гипотезы, которые не прошли проверку практикой. Реальные требования бизнеса или пользователей ломают ваши планы.
Фреймворки и технологии устаревают быстрее вашей разработки. Не стоит ожидать, что выбор прошлого года на старте сильно поможет вам через год.
Всё становится понятнее после первого хаоса. Когда проект уже заработал в минимальном виде, вы простыми глазами видите, что действительно нужно улучшить.
Как перестать ждать идеального старта?
1. Разделяй задачи — дроби их до безобразия
Когда вы сидите перед пустым проектом с глобальной целью "создать что-то большое", эта цель просто подавляет. Решение — дробить задачи так мелко, как только возможно, до тех пор, пока они не сделаются совершенно очевидными.
Пример:
Вместо глобальной "Настроить архитектуру API" попробуйте начать с "Создать базовый сервер Express, который выводит Hello World". Да, это звучит слишком просто. Но через час у вас будет что-то, с чем можно двигаться дальше.
2. Не принимайте слишком много решений одновременно
Если вы разрабатываете новый проект, не нужно сразу соглашаться, что он "уже сегодня" должен иметь:
чёткую структуру директорий;
“идеальный” Redux/Signals/Vuex-стор;
пользовательскую CI/CD-пайплайн;
100%-ный тестовый coverage.
Оставьте эти задачи на потом и фокусируйтесь на минимальных, но работающих версиях.
3. Пусть ранние этапы будут хаосом
Превращайте процесс построения проекта в последовательность итераций. На самом деле, никакого идеального кода на старте не существует, потому что сам проект находится на этапе неопределённости.
В работе нужно перестать рассматривать написание "грязного" кода или использование временных решений как проблему. Главное — постоянно держать в голове эту цепочку:
Написать рабочий код.
Улучшить что-то в существующей кодовой базе.
Повторить.
Рефакторинг не страшен, если вы делаете его регулярно.
4. Ставьте цели, которые зависят от действий, а не от результата
Вместо того чтобы говорить себе: "Сегодня я построю модульную структуру backend-а", лучше напишите: "Сегодня я создам функционал аутентификации с минимальной связностью". Такой подход помогает сосредоточиться на процессе и меньше страдать от ощущения, что результат пока не "грандиозный".
5. Учитесь отпускать
Отпустите иллюзию, что первое решение — всегда окончательное. Это не так. Код предполагает, что его пишут, а не высекут в камне. Если выбрали стратегию, но она потом оказалась неэффективной, ничто не мешает её изменить. Не бойтесь оставить что-то недоделанным. Главное — ваша способность двигаться вперёд. В индустрии выживают не те, кто создаёт идеальное с первой попытки, а те, кто выпускает работающие проекты и готов улучшать их с каждым новым обновлением.
Рефакторинг не ждёт особого момента
Тут важно подчеркнуть ещё одну мысль — важность постоянного рефакторинга. Часто мы мысленно отделяем разработку от рефакторинга. Есть мол, этап "мы быстро кодим для MVP", а потом — этап "мы возвращаемся, чтобы всё зачистить". Однако этот подход в корне ошибочен: бизнес не даст денег на "этап рефакторинга", если только проект уже не горит, как свечка.
Я, честно говоря, больше никогда не разделяю эти вещи. Рефакторинг — не отдельная стадия проекта, это часть процесса разработки. Это то, что вы делаете каждый день, с каждым коммитом, чтобы сделать код хотя бы чуточку лучше, чем он был вчера.
Много лет назад я принимал участие в проекте, который на старте был классическим примером плохой разработки. Кодовая база была хаотичной: множество "хотфиксов", дублирования, отсутствия тестов и полной каши в структуре директорий. Понятие "архитектура" здесь не применялось совсем, а куча логики была расфокусирована по всему проекту. Несмотря на то, что продукт выполнял свои задачи, работать с ним было настолько сложно, что каждый новый функционал превращался в кошмар даже для опытных разработчиков.
Проект уже приносил какие-то деньги, но любые добавления функций или исправления багов занимали в 2-3 раза больше времени, чем изначальные оценки. Компанию поставили перед выбором: взять и полностью переписать проект (что может занять до года) или постепенно улучшать его без остановок для бизнеса.
Первое, что было сделано, — команда договорилась о практическом подходе: "каждый коммит должен оставлять код чуть лучше, чем он был до этого". Это не значило, что мы сразу перепишем всё. План включал следующие шаги:
При добавлении нового функционала — рефакторим окружающую часть кода. Например: если добавлялся метод в устаревший класс, этот класс приводился в более аккуратное состояние, а сам метод мы старались реализовать в соответствии с лучшими практиками.
Постепенно внедряли тестирование. Каждый новый или изменённый кусок кода покрывался тестами, чтобы минимизировать риск поломок.
Писали себе маленькие задачи "по ходу". Например, если мы находили сложный нечитаемый метод, мы редко перерабатывали его сразу, но заводили тикет "Оптимизировать X".
Приводили к единому стилю кодирования. Даже порядок импортов и форматирование стали для нас важными.
После года постепенного улучшения проект прошёл внешний аудит, так как компания привлекла крупного клиента. Внешний аудитор отметил, что, хотя в глубине оставалось наследие старого плохо написанного кода, текущая структура и сопровождение уже соответствовали стандартам. Что самое главное, бизнес увидел разницу — добавление новых функций занимало меньше времени и стоило компании гораздо дешевле. Благодаря этому продукт удалось вырастить до уровня, при котором он приносил заметный доход.
Этот случай показал мне и команде, что постепенное улучшение — это реально работающая стратегия. Нет необходимости переписывать проект с нуля, если есть желание и дисциплина улучшать его шаг за шагом. Главное — системность и желание двигаться к порядку.
Никто не помнит, с чего началось, но все увидят результат
Когда проект завершён (или хотя бы работает), никто уже не заботится о том, какой хаос был на старте. Пользователи будут пользоваться, клиенты подпишут контракт, ваш темлид похвалит (или не похвалит — зато проект жил).
Давайте вспомним, как создавалась большая часть успешных продуктов. Facebook начинался с локальной сети университетов. Amazon — как книжный магазин. А первый код популярного языка Python, по воспоминаниям создателя Гвидо ван Россума, выглядел далеко не лучше вашего студенческого проекта.
Если самые известные компании мира начинались с функциональности, работающей "как-нибудь", то откуда у нас уверенность, что наш код с первой попытки должен быть идеальным?
Заключение
Идеального старта не бывает. Точка.
Код пишется, а не рождается. Он живёт в постоянных итерациях. Не нужно писать идеальную структуру с первых дней разработки — это иллюзия. Задача разработчика — не сотворить произведение искусства на старте, а двигаться вперёд, чтобы в каждый конкретный момент улучшать то, что уже создано.
Красивый код — это миф. А хороший код — это тот, который работает. Выбирайте второе.
P.S. Можно почитать:
«Continuous Delivery»/Джез Хамбл, Дэвид Фарли. Старенькая, но фундаментальные концепции живут в веках)
«Refactoring»/Мартин Фаулер (ну вдруг кто‑то еще не читал)
«Release It!»/Майкл Нигард. Про реальную жизнь и разочарования, которые подстерегают нас в реальных проектах.