Любой программист, вне зависимости от уровня, когда-то сталкивался с этим. Ты садишься за проект, полный планов и идей, предвкушаешь, как создашь нечто совершенное, но... проходит час, второй, а на экране только десяток папок, пара заглушек и файл 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!»/Майкл Нигард. Про реальную жизнь и разочарования, которые подстерегают нас в реальных проектах.
