Почему ваш проект стоит дороже, чем должен
Содержание
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 месяцев обгоняет её и уходит вверх по экспоненте.
А теперь самое интересное – ключевые точки на красной кривой:
- Месяц 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 | ”Простая” задача требует… | только кода | археологии | совещания |
| 6 | Bus factor | 4+ | 2-3 | 1 |
| 7 | Код, который боятся трогать | нет | пара мест | полсистемы |
| 8 | Lead 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. Оригинальная статья про антипаттерн, который вы узнаете.