Перейти к содержимому

Почему ваш проект стоит дороже, чем должен

· 13 мин
Проектирование, которое работаетЧасть 1 из 4
Содержание

TL;DR: Каждое “потом поправим” – кредит под сложный процент. Пять источников сложности превращают задачу на 4 часа в двухмесячный кошмар. Диагностика: 10 вопросов покажут, на какой стадии ваш проект. Рецепт: Technical Excellence + Good Design = способность вносить изменения быстро и дёшево.


Ситуация:

Бизнес: Нужно добавить подвид платежа. Не новую платёжную систему, не интеграцию со SWIFT – подвид уже существующего способа оплаты.

Ответ команды: “Невозможно. Нужен месяц на рефакторинг, и только потом сама фича”.

По факту - два месяца.

Я наблюдал это вживую, когда пришёл техлидом в небольшую EdTech-компанию. CRM-система, которую один разработчик строил 2.5 года. PHP, PostgreSQL, ноль тестов. Система управляла всем: платежами, курсами, воронками, рассылками, отчётами, зарплатами – буквально всем, что нужно онлайн-школе на 20-30 тысяч пользователей. Между собой мы прозвали этот проект Andrews.

В PostgreSQL жила таблица users. Не таблица пользователей – свалка всего, что можно знать о человеке: логин, контакты, UTM-метки, данные последней покупки, последний пройденный курс, настройки уведомлений. Под сотню колонок. И таких “божественных” таблиц было несколько.

Бизнесу срочно нужен новый способ оплаты к конкретной дате – к запуску курса, уже на низком старте маркетинговая кампания. А система буквально рассыпается в руках: тронул логику платежей – отвалились отчёты по продажам, потому что они напрямую читали те же поля из users. Поправил отчёты – сломалась синхронизация с GetCourse. Каждое действие порождало каскад поломок в 3-4 местах.

Как задача на полдня превратилась в два месяца? И почему это происходит на каждом втором проекте?

Стоимость изменений – конкретная строка в P&L

Стоимость изменений – конкретная строка в бюджете вашей компании. Реальная разница между “фича за день” и “фича за квартал” при тех же зарплатах, тех же людях, том же стеке.

Вот как это выглядело на том проекте в цифрах:

  • 60% рабочего дня – хотфиксы. Не новые фичи, не улучшения, а латание дыр, которые появились вчера от латания позавчерашних дыр.
  • Задача на 4-6 часов занимала 3-10 рабочих дней стабильно.
  • Релизы хотфиксов – несколько в день, хаотично, без системы. Релиз фич – раз в 1-2 недели, если повезёт.

Если перевести в деньги: разработчик на фулл-тайме, из которого 60% – тушение пожаров, а оставшиеся 40% – работа в 3-10 раз медленнее нормы. Эффективность – процентов пять-десять от возможного. Умножь на типичную команду из пяти-десяти человек. А если на год. На два.

Есть три метрики, которые показывают здоровье проекта лучше любых ощущений и субъективных оценок:

  • Lead time – от “задача взята в работу” до “фича на проде”. На здоровом проекте это часы. В той CRM – дни и недели.
  • Deployment frequency – как часто деплоите осмысленно. Хаотичные хотфиксы несколько раз в день это не “частый деплой”, а сигнал SOS вашему руководителю.
  • Change failure rate – процент изменений, которые ломают прод. Когда каждый второй хотфикс порождает ещё два, ты в красной зоне.

Исследования DORA подтверждают контринтуитивное: команды, которые деплоят чаще, ломают систему реже. Получается, что скорость и стабильность не враги, а наши союзники.

Окей, одна CRM, один разработчик – может это крайний случай?

На другом проекте – LMS для языковой школы, Ruby on Rails, другая команда, другая компания – те же паттерны. Система развивалась больше 10 лет. Монолитное ядро, которое боялись трогать. Бизнес-логика везде, кроме места, где ей стоило бы жить.

На первый взгляд кажется, что условия разные: PHP и Ruby, EdTech и языки, 2.5 года и 10 лет – а грабли одни и те же. Стек не при чём, сценарий повторяется с пугающей точностью.

Каждое “потом поправим” – кредит под сложный процент

Вот что неочевидно: на старте проекта срезать углы действительно быстрее.

Нет тестов – не тратишь время на тесты. Всё в одной таблице – не думаешь про нормализацию. Один человек знает всё – нет накладных расходов на коммуникации. Ранний проект без архитектуры обгоняет проект с архитектурой. И в этом нет никакого мифа – это чистая математика, и именно поэтому все туда попадают.

Проблема в том, что “потом поправим” это кредит, а его ставка – сложный процент.

Каждое “потом поправим” не просто добавляет долг – оно удорожает следующее изменение, которое удорожает следующее за ним, но уже не линейно, а экспоненциально.

Чтобы это увидеть наглядно, представь две кривые на графике:

  • Зелёная – проект с осознанным управлением сложностью. Почти горизонтальная, с лёгким подъёмом. Фича стоит примерно одинаково в январе и в декабре.
  • Красная – проект без управления сложностью. Начинается ниже зелёной (быстрый старт!), но после 4-6 месяцев обгоняет её и уходит вверх по экспоненте.
График: compound interest сложности — стоимость фичи растёт экспоненциально без управления сложностью
Compound interest сложности: две кривые стоимости изменений

А теперь самое интересное – ключевые точки на красной кривой:

  • Месяц 4-6: пересечение с зелёной. Тут ещё кажется, что всё ОК. “Ну да, чуть замедлились, но это потому что фичи стали сложнее.”
  • Месяц 8-12: “Почему всё так медленно?” Менеджмент нервничает. Команда оправдывается: “Нам не давали передышек!”
  • Месяц 18+: Команда констатирует болезненный факт: “Проще переписать”.

В нашей CRM эта точка наступила на 2.5-м году. В языковой LMS – через 10 лет, но наступила и там. Все эти десять лет “давайте пока так” – и в итоге десятки документов в Confluence, обосновывающих невозможность очередной фичи.

Сложность не появляется внезапно – это результат тысячи маленьких решений, каждое из которых казалось разумным в момент принятия.

AI-ассистенты, кстати, делают этот процесс интереснее. Ваш AI-агент уверенно генерирует код на основе твоих предположений о домене. Он не спрашивает “а точно ли users – это одна сущность?”. Он берёт god-таблицу и уверенно строит вокруг неё ещё больше кода. Compound interest ускоряется.

Так откуда же берётся эта сложность? Она не появляется из воздуха – у неё вполне конкретные источники, и я насчитал пять основных.

Откуда берётся сложность?

Toil: когда большая часть дня – тушение пожаров

Те самые 60% хотфиксов. Несколько хаотичных релизов в день, без плана, без системы. Как и что перезапускать, какие скрипты прогнать, какие кэши сбросить – знал один человек, и он был не просто разработчиком, а ходячей инфраструктурой проекта. Когда он в отпуске – всё стоит.

Toil пожирает ресурсы незаметно. Никто не планирует в бэклоге “потратить 60% спринта на хотфиксы” – это просто происходит, и потом менеджмент смотрит на velocity и спрашивает: “Почему так мало фич за прошедший квартал?”

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

Database-first: хвост виляет собакой

God-таблицу users ты уже видел, но дело не в конкретной таблице, а в подходе: сначала подумай как хранить, а потом – зачем.

В языковой школе было иначе технически, но тот же паттерн. Задания хранились в jsonb со свободной структурой, в коде читались в динамический тип и по некоторым косвенным признакам парсились в конкретный. У каждого вида задания своя бизнес-логика, размазанная по всем слоям, и когда бизнес попросил принципиально новый тип задания, выяснилось, что архитектура этого просто не позволяет – не “сложно”, а невозможно без масштабного переписывания.

Database-first быстр на старте – думаешь таблицами, и таблицы появляются. Но через полгода-год схема БД незаметно становится главным ограничителем, и уже не бизнес-требования диктуют архитектуру, а структура таблиц диктует какие требования в принципе реализуемы практически.

High coupling: “не трогай, там драконы”

Изменил логику платежей – отвалились отчёты. Поправил отчёты – сломалась синхронизация. Поправил синхронизацию – слетели воронки. Каждое касание порождает каскад в трёх-четырёх местах, и это далеко не уникальная ситуация одной CRM.

В языковой LMS монолитное ядро развивалось 10+ лет: доступы, контент, баллы, прогресс, сертификаты, статистика – всё в одном месте. Реакция команды на любое предложение что-то поменять: “Работает, не трогай! Уйди!” Выглядит как разумная осторожность, пока не осознаёшь, что это паралич – команда не может менять собственную систему, потому что никто не знает, что именно сломается.

Когда один модуль знает о пятидесяти других, ни один разработчик не удержит в голове все последствия своего изменения, даже если он же сам этот модуль писал.


Когда я предложил разделить ответственность между модулями и ввести контракты, реакция была: “Это не мы говнокодеры – это условия такие, нам не давали передышек!” Знакомо? Легче объяснить сложность обстоятельствами, чем признать: мы не управляли сложностью, когда добавляли её в проект.

Буду честен: и я на своих проектах грешил избыточными абстракциями и слоями там, где хватило бы простого сервиса. Сложность ведь складывается не только из грязного кода – любой код, который дороже менять, чем нужно, вносит свой вклад в compound interest.


Silo: знания за семью замками

Как устроена система, знали 2-3 человека, а документация либо отсутствовала, либо устарела настолько, что приносила больше вреда, чем пользы.

В языковой LMS до нашей команды работала целая группа аналитиков. Знаешь, чем они занимались? Писали обоснования, почему очередную фичу нельзя реализовать, видел десятки таких отчётов в Confluence. Не потому что фичи были невозможные, а потому что никто не мог собрать полную картину. Каждый разработчик “креативил” по-своему: общего языка нет, общего понимания нет. Фича проходит от аналитика к разработчику и на этом переходе теряет половину смысла.

Появляется бункер – но не в менеджерском смысле “отделы не общаются”, а в архитектурном: знания фрагментированы, контекст теряется на каждом переходе, и система зеркалит эту фрагментацию в своей структуре.

Хаотичный бэклог: бесконечный поток без берегов

“Некогда продумывать. Некогда рефакторить. Бизнесу нужно вчера.”

Фичи добавлялись без остановки два с половиной года. Ни одного спринта на рефакторинг. Ни одной попытки остановиться и подумать: что мы строим и куда. Запилить, отправить в прод, и так сойдет, следующая. Неудачные фичи не удалялись полностью, а висели полумёртвым грузом, отравляя кодовую базу.

Результат – Big Ball of Mud. Система, в которой невозможно провести границу между одной ответственностью и другой.

Большой ком грязи – это беспорядочно структурированный, растянутый, неряшливый, словно перемотанный изоляционной лентой джунгли спагетти-кода. – Brian Foote, Joseph Yoder

“Пока накидаем костылей, а позже никогда выделим спринт на рефакторинг.”

Два с половиной года всех пяти источников, работающих одновременно и без какого-либо противодействия, – и система выдаёт вердикт: “подвид платежа – 2 месяца”. Дело не в людях, а в том, что compound interest достиг точки, где простое изменение объективно невозможно без серьёзной перестройки всего.

На какой стадии ваш проект?

Я собрал десять вопросов, которые помогают определить стадию. Отвечай на каждый первой реакцией – она самая честная.

#Вопрос🟢🟡🔴
1Новый человек продуктивен через…1-2 неделимесяц+“спроси Васю”
2Оценки попадают в реальностьобычночерез разникогда
3Деплой – это…рутинасобытиемолитва
4Доля хотфиксов в спринте< 20%20-50%> 50%
5”Простая” задача требует…только кодаархеологиисовещания
6Bus factor4+2-31
7Код, который боятся трогатьнетпара местполсистемы
8Lead time за годстабиленвыросвырос кратно
9Документацияактуальнаустарела”какая?“
10”Проще переписать” звучит как…шуткаобсуждениеплан

А теперь посчитай, какого цвета у тебя больше:

🟢 Управляемая сложность. Фичи доставляются предсказуемо. Инвестиции в качество окупаются. Не расслабляйся – следи за трендом. Сложность растёт каждый день, даже если сегодня всё хорошо.

🟡 Нарастающее торможение. Ещё не катастрофа, но тренд плохой. Это лучший момент для действий: ты ещё можешь менять систему без героизма. Через полгода будет значительно дороже.

🔴 Паралич. Система диктует условия команде, а не наоборот. Каждое изменение – риск. Люди выгорают. Это состояние, в котором была та CRM. Но даже отсюда можно выбраться: у нас это заняло 4-5 месяцев целенаправленной работы “на горячую”, без остановки текущего функционала.

И что с этим делать на каждой стадии?

Если ты в красной зоне – нужно набраться смелости и перестать добавлять фичи, вложившись в стабилизацию. Я знаю, бизнес будет сопротивляться, и это нормально. Но два месяца на подвид платежа – это цена, которую ты уже платишь. Систематический рефакторинг – способ вернуть разработку к адекватным срокам.

Если в жёлтой – можно начать с малого: тесты на критичные пути, документация на модули, которые “знает только Вася”, постепенное разделение god-объектов. Каждый такой маленький шаг снижает ставку сложного процента.

Если в зелёной – просто продолжай делать то, что делаешь. Управление сложностью – не проект с дедлайном, а скорее привычка, как чистка зубов: перестанешь – и через полгода обнаружишь себя в жёлтой зоне.

Как вернуть управляемость?

9-й принцип Agile Manifesto:

Постоянное внимание к техническому совершенству и качеству проектирования повышает гибкость проекта.

Или короче, словами Кента Бека:

Сначала сделай изменение простым. Затем сделай простое изменение.

Technical Excellence + Good Design = Technical Agility – способность вносить изменения быстро и дёшево.

Technical Excellence – CI/CD, тесты, мониторинг, infrastructure as code, совместное владение кодом. Всё то, что разработчики делают, когда им “дают время работать нормально”. (Спойлер: это время не дают, его берут!)

Good Design – осознанное проектирование бизнес-логики. Модель предметной области вместо transaction script. Границы между контекстами вместо одной таблицы на всё. Решения, продиктованные бизнесом, а не структурой хранения.

Мы прошли этот путь вместе с Andrews за 6-7 месяцев. Из красной зоны в запланированные релизы раз в 1-3 дня. Из “невозможно добавить подвид платежа” в предсказуемую доставку фич. Каждое изменение стало дешевле – и скорость выросла как следствие.

Когда это не работает

Формула не универсальна. Прототип, который проверяет гипотезу и может быть выброшен через месяц, не нуждается в границах контекстов. Стартап, который ещё не нашёл product-market fit, погибнет от over-engineering быстрее, чем от техдолга. Одноразовый скрипт миграции данных не требует тестов.

Инвестиции в Technical Excellence окупаются на дистанции. Если ты уверен, что дистанции не будет, можно срезать. Проблема в том, что ты почти никогда не уверен. И “временное решение” живёт годами. Помнишь Andrews? Он тоже начинался как “пока так, потом доделаем”.

Что дальше

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

А ещё есть техника, которая за 4 часа показывает команде всё, что она не знает о собственной системе. Называется Event Storming. На Andrews за один такой воркшоп мы нашли 12 скрытых бизнес-процессов, о которых не знала команда, работавшая с системой два года. До неё мы доберёмся.

Обсуждение – в Telegram-канале. Там короче, острее и чаще.


Что почитать по теме

  • Accelerate – Nicole Forsgren, Jez Humble, Gene Kim. Исследование DORA: научная связь между метриками доставки и успехом бизнеса.
  • A Philosophy of Software Design – John Ousterhout. Лучшая книга о борьбе со сложностью на уровне кода и интерфейсов.
  • Clean Agile – Robert C. Martin. Что такое Technical Excellence и почему без него Agile не работает.
  • Domain-Driven Design – Eric Evans. Первоисточник подхода, к которому мы придём в этом цикле.
  • Big Ball of Mud – Brian Foote, Joseph Yoder. Оригинальная статья про антипаттерн, который вы узнаете.
Поделиться: Telegram X