Введение
«Сделать быстро, а потом переделать правильно» — знакомая мантра для каждого разработчика. Но что делать, если «потом» наступает слишком рано, и фичу приходится переписывать с нуля буквально через несколько месяцев? В свежей статье на Habr один из разработчиков поделился историей о том, как создавал одну и ту же функциональность дважды. Spoiler: второй раз получилось не только быстрее, но и надежнее.
Сегодня разберем этот кейс с точки зрения инженерных практик: какие грабли собрала команда на первом этапе, какие архитектурные решения позволили избежать их во второй раз, и какие выводы можно сделать для собственных проектов. Статья будет полезна как опытным разработчикам, так и тем, кто только начинает разбираться с управлением техническим долгом.
Контекст и исходная ситуация
Как отмечается в статье Источник, речь идет о внутреннем инструменте, который должен был выполнять конкретную бизнес-задачу — агрегацию данных из нескольких источников с последующей визуализацией на дашборде. Первая версия фичи создавалась в условиях жесткого дедлайна: у команды было всего две недели на реализацию, и приоритетом был «хоть какой-то выкат в прод».
Характерная деталь: на момент первой итерации не был проведен полноценный анализ требований. Бизнес-заказчик сформулировал задачу в общих чертах, а разработчики, следуя принципу «сделаем MVP, потом уточним», заложили избыточную сложность в тех местах, где она была не нужна, и наоборот — упростили критически важные модули.
Технический стек первой версии
Автор статьи рассказывает, что для быстрой реализации были выбраны инструменты, уже знакомые команде, но не всегда оптимальные для данной задачи:
| Компонент | Первая версия | Вторая версия |
|---|---|---|
| Бэкенд | Монолит на Express.js | Модульная архитектура с разделением на микросервисы |
| База данных | Единая PostgreSQL с кучей джойнов | Отдельная read-реплика для отчетов + кэш Redis |
| Взаимодействие с внешними API | Прямые HTTP-вызовы в коде | Асинхронная очередь через RabbitMQ |
| UI | React-компонент с глобальным стейт-менеджером | Локальный стейт + подгрузка данных через хуки |
Уже на этом этапе видны классические признаки технического долга: монолит, синхронные вызовы, отсутствие изоляции слоев. Команда знала, что так делать неправильно, но время поджимало.
Проблема: первая реализация оказалась «нежизнеспособной»
Через месяц после релиза начали проявляться симптомы, которые в статье названы «хроническим воспалением кода»:
- Производительность упала на 40% при загрузке дашборда с данными за месяц. Запросы к PostgreSQL с 8 джойнами зависали на 3–5 секунд.
- Баги при каждом третьем обновлении данных из внешних API. Синхронные вызовы не справлялись с пиковыми нагрузками, и при ошибке одного источника блокировался весь поток.
- Сложность поддержки — любой новый разработчик тратил не менее 3 дней на понимание логики первого варианта. Функции были раздутыми, а названия переменных — бессмысленными (dataObj1, tempVar и т.д.).
- Нарастание технического долга. Команда тратила около 60% времени на фиксы багов и только 40% на новую функциональность.
Кульминация наступила, когда бизнес-заказчик попросил добавить фильтр по датам. Оценка трудозатрат для первой версии показала: 3 недели разработки. Для второй версии (если бы её переделать с умом) — всего 1 неделя. Команда приняла решение: «дубль два».
Решение: архитектурная перестройка и изменение подхода
Во второй раз команда подошла к задаче системно. В статье выделяются три ключевых отличия:
1. Проектирование «от бизнеса»
Вместо того чтобы сразу писать код, разработчики провели серию встреч с заказчиком и построили event storming — карту доменных событий. Это позволило выявить, что на самом деле нужно: не просто «показать данные», а «получать агрегаты с возможностью среза по времени, с гарантией свежести данных и возможностью отката при ошибке».
Результат: архитектура стала событийно-ориентированной. Каждое изменение источника данных генерирует событие, которое ставится в очередь. Бэкенд подписывается на события и обновляет кэш. Пользователь видит данные, которые не старше 15 секунд.
2. Разделение ответственности
Вместо одного монолита появились три микросервиса:
- Aggregator — собирает данные из внешних источников, нормализует их и кладет в Redis.
- API Gateway — отдает данные для фронта, используя кэш и fallback-механизмы.
- Admin Panel — позволяет конфигурировать правила агрегации без перезапуска системы.
Каждый микросервис тестируется отдельно, а интеграционные тесты запускаются в CI/CD.
3. Code Review и стандарты кода
Во второй версии была введена жесткая дисциплина:
- Каждое изменение проходит code review двумя разработчиками.
- Обязательное использование линтеров (ESLint + Prettier) и статический анализ (SonarQube).
- Написаны юнит-тесты с покрытием >80%.
- Документация API ведется в OpenAPI формате сразу во время разработки.
«В первой версии мы code review проводили “на словах” — просто говорили “ок”, но не проверяли реально. Во второй раз потратили на ревью дополнительные 20% времени, но количество багов на прод упало в 4 раза», — пишет автор.
Результаты: цифры и факты
Команда сравнила показатели после запуска второй версии. Данные взяты из статьи и представлены в таблице:
| Параметр | Первая версия | Вторая версия |
|---|---|---|
| Время загрузки дашборда (среднее) | 3,2 сек | 0,8 сек |
| Количество критических багов за месяц | 12 | 2 |
| Время на добавление нового фильтра | 3 недели | 4 дня |
| Покрытие кода тестами | 12% | 82% |
| Среднее время онбординга нового разработчика | 3 дня | 1 день |
Эти цифры подтверждают, что «дубль два» не был бесполезной работой. Напротив, инвестиция в качество окупилась уже через пару месяцев — команда начала быстрее доставлять новые фичи.
Выводы: что вынести из этого кейса
История, описанная на Habr, — классический пример того, как не надо делать MVP, но при этом надо исправлять ошибки, если они неизбежно произошли. Вот несколько уроков, которые можно вынести:
1. Сопротивляйтесь «ложной экономии» времени
Первый вариант фичи делался быстро, но в итоге занял больше времени на поддержку, чем планировалось. Если бы команда сразу потратила неделю на проектирование и выбрала правильную архитектуру, общая стоимость фичи оказалась бы ниже.
2. Вовлекайте заказчика в процесс
В первом случае требования были размыты. Во втором — команда и бизнес совместно строили модель предметной области. Это позволило избежать переделок на этапе приёмки.
3. Не бойтесь признавать ошибку и переписывать код
Часто разработчики не решаются на рефакторинг, потому что «работает — не трогай». Но когда «работает» — это через пень-колоду, лучше потратить время на второй дубль. Как показано в статье, команда сэкономила более 60% времени на будущие доработки.
4. Внедряйте инженерные практики с самого начала
Code review, тесты, статический анализ — это не бюрократия, а защита от глупых ошибок. Во второй версии их внедрили, и это сразу сказалось на качестве.
Заключение
Статья «Дубль один, дубль два: как я делала одну фичу дважды» — честный и полезный разбор реального опыта. Она показывает, что даже неудачная первая итерация может стать фундаментом для сильного решения, если команда готова учиться на ошибках и менять подход.
Для тех, кто читает подобные кейсы, важно не просто кивать головой, а внедрять описанные практики в свои проекты. Начните с малого: проведите ретроспективу последней фичи, которую вы доделали с трудом. Возможно, она тоже требует «дубля два».
Исходный материал можно прочитать здесь: Источник.
Читайте также другие материалы блога ASI Biont — мы регулярно публикуем разборы real‑world кейсов и инженерные статьи.
Комментарии