В прод идут не знания, а предположения
Содержание
TL;DR: Главный враг проекта – не плохой код, а предположения (assumptions), незаметно ставшие архитектурными решениями. Знания фрагментированы между людьми, мозг достраивает недостающие смыслы, а когнитивные ловушки делают нас слепыми к собственному незнанию. Первый шаг – признать, что ты не понимаешь домен. Второй – создать механизмы, чтобы выяснять.
“В прод идут не знания экспертов в предметной области, а предположения разработчиков.” – Альберто Брандолини, автор техники Event Storming
Я пришёл на проект бронирования отелей, открыл код – и за 15 минут понял всю архитектуру. Не потому что она была хорошо спроектирована, а потому что это была 1:1 копия API одного GDS-провайдера – HotelBook, с которым я уже был знаком. Названия сущностей, структура моделей, сценарии взаимодействия – всё один к одному. Приложение было тонкой прослойкой с кэшем между WebMoney Travel и единственным поставщиком.
Продакт-менеджер считала, что строит универсальный шлюз для нескольких GDS. Код говорил другое.
Когда подключили второго провайдера – АкадемСервис – выяснилось, что у него практически всё по-другому: модели данных, сценарии бронирования, взаиморасчёты. Почти никаких пересечений с HotelBook. АкадемСервис пришлось прикручивать как пристройку к зданию, спроектированному под совсем другого жильца.
Никто не принимал решение “у нас всегда будет один провайдер”. Первые разработчики просто скопировали модель HotelBook, и невысказанное предположение стало архитектурным фактом через код. Переписать с нуля нельзя – бизнес на это никогда не пойдёт, и я это уже знал по опыту. Пришлось городить адаптеры, постепенно выделять абстракции, создавать отдельные таблицы где абстракция не ложилась.
В итоге мы интегрировали три GDS, четыре уникальных сценария бронирования, два вида взаиморасчётов. Но цена одного невысказанного предположения – месяцы работы, которой могло не быть.
В прошлой статье мы говорили о том, как compound interest сложности превращает задачу на полдня в двухмесячный кошмар. Но откуда берутся те “маленькие решения”, которые запускают этот процесс? Чаще всего – из предположений, которые мы даже не осознаём как предположения.
Почему “мега-архитектор, который всё знает” – фантазия
С ростом проекта объём знаний перестаёт умещаться в одной голове. Домен, бизнес-правила, ограничения, исключения, исторические причины текущих решений – всё это распределено между десятками людей. Продакт знает про пользователей, бухгалтерия про взаиморасчёты, поддержка про реальные сценарии использования, а разработчик – про то, как оно устроено внутри. Полной картины нет ни у кого.
И это нормально. Проблема не в том, что знания фрагментированы – а в том, что мы этого не замечаем.
Когда информации не хватает, мозг не останавливается и не говорит: “Подожди, тут пробел”. Он достраивает недостающее сам. Молча, без предупреждения. Ты читаешь ТЗ, и у тебя возникает целостная картина. Только это не картина домена. Это картина, которую мозг склеил из написанного, прошлого опыта и собственных допущений.
Каждый такой домысел – потенциальное архитектурное решение, которое ты примешь, не осознавая его как решение.
И нет мега-архитектора, который всё это агрегирует, спроектирует и раздаст правильные указания. В реальности множество людей работают над проектом, который они не до конца понимают. Не потому что глупые – потому что объём знаний объективно превышает пропускную способность одной головы. Это реальность любого проекта крупнее todo-приложения.
Окей, фрагментация – данность. Что с ней делает наш мозг?
Когнитивные ловушки: почему ты не знаешь, что ты не знаешь
Иллюзия полноты
Ты прочитал ТЗ. У тебя есть user stories. Провёл встречу с продактом. Кажется, всё понятно. Садишься писать код.
Проблема: избыточность информации порождает иллюзию её полноты. Ты видишь много деталей – и мозг воспринимает это как “я знаю достаточно”. Но ТЗ описывает happy path. User stories покрывают основной сценарий. А 80% сложности живёт в edge cases, исключениях и пересечениях между контекстами, которые ни один документ не описывает – потому что о них даже заказчик пока не думал.
Проклятие знания
Зеркальная ловушка. Доменный эксперт не может объяснить очевидное – для него это “ну это же понятно”. Разработчик не переспрашивает – ему кажется, что он понял. Два человека расходятся с разными картинами в голове, оба уверены, что договорились.
Confirmation bias: проектируем под свой опыт
В CloudPayments я это видел в масштабе целой компании.
Вся организация годами работала с карточными платежами. Синхронный флоу: запрос – авторизация – ответ. Одна цельная транзакция от инициации до результата. Архитектура, инструменты, ментальные модели – всё заточено под этот сценарий.
Я пришёл в самом начале нового проекта альтернативных способов оплаты: QR-коды, токены, рассрочки. Спроектировал и реализовал MVP с нуля за три с половиной месяца. И именно на этапе проектирования стало видно, насколько глубоко сидит карточное мышление.
Альтернативные платежи по природе асинхронные. Плательщик сканирует QR, уходит в приложение своего банка, и через какое-то время хуком прилетает результат. Между инициацией и завершением – gap неизвестной длительности. Это принципиально другой процесс с другим жизненным циклом.
Но на уровне компании QR-платёж воспринимался как “рваная карточная транзакция”. Привычная модель – молоток, и все новые способы оплаты выглядели как гвозди. Вместо того чтобы признать: это другой домен с другими правилами – команда и архитектура пытались натянуть новую реальность на старую модель.
Когда confirmation bias живёт в одной голове, его можно поймать на код-ревью. Когда он прошит в архитектуру целой компании, ты сталкиваешься с ним на каждом шаге проектирования. Каждое обсуждение начинается с “а как это ляжет на транзакцию?” – даже когда транзакции в привычном смысле нет. Инструменты, API, мониторинг, даже терминология внутри команды – всё пропитано предположением “платёж = синхронная операция”. Менять пришлось не код, а способ думать о домене.
Как это работает вместе
Ловушки не ждут очереди – они работают одновременно и усиливают друг друга. Ты читаешь ТЗ и чувствуешь, что всё понял. Идёшь уточнять к эксперту, а тот пропускает детали, которые для него самоочевидны. Садишься проектировать – и опираешься на то, что работало на прошлом проекте, даже не задумываясь, подходит ли оно здесь. А оставшиеся пробелы мозг достроит сам. Аккуратно, без швов. И вот эти домыслы попадают в код, код превращается в архитектуру, а архитектура уже определяет, что можно изменить за день, а что “требует месяц рефакторинга, и то не факт”.
YAGNI наоборот
Есть ещё одна ловушка, о которой редко говорят. Классический YAGNI учит не добавлять лишнее. Но бывает наоборот: ты не добавляешь нужное, потому что даже не знаешь, что оно нужно. В WebMoney Travel никто не добавил абстракцию провайдера не из лени – в картине мира первых разработчиков второго провайдера просто не существовало.
Буду честен: я тоже не сразу научился это замечать. На WebMoney Travel я увидел чужое предположение за 15 минут – но только потому, что оно было грубым, на уровне архитектуры. А сколько моих собственных предположений, тоньше и незаметнее, просочились в код на тех же проектах? Я не знаю. В этом и ловушка: свои предположения ты не видишь, пока кто-то не ткнёт носом.
А теперь представь, что рядом с тобой сидит AI-ассистент, который предположения не видит в принципе. В прошлой статье я упоминал, как AI-агент уверенно наращивает код вокруг god-таблицы. Тут механизм тот же, только тоньше: ты даёшь контекст, AI достраивает. Он не спросит “а точно ли это одна транзакция?” – он возьмёт твою ментальную модель и превратит её в код быстрее, чем ты успеешь усомниться.
Итого: мы не просто не знаем домен – мы не знаем, что мы его не знаем, и наш мозг активно мешает это осознать. Но предположения живут не только в голове отдельного разработчика. Они живут в структуре организации.
Silo mindset – архитектурная проблема, не менеджерская
“Организации, проектирующие системы, ограничены дизайном, который копирует структуру коммуникаций этой организации.” – Мелвин Конвей, 1968
Обычно закон Конвея воспринимают как наблюдение про менеджмент. Мол, “отделы не общаются, поэтому системы не интегрируются”. На деле всё интереснее: это про то, как знания (и их дыры) прошиваются в архитектуру напрямую.
На проекте Andrews в EdTech-компании, где я работал техлидом – мы проводили Event Storming сессии с разными отделами. Полноценные оффлайн-воркшопы: ватман на стене, стикеры, продажи и методологи в одной комнате.
Одна из находок: после бесплатного вебинара лид попадал в CRM к продажникам. Параллельно методологи отправляли тому же человеку автоматическую рассылку с другим оффером. Два отдела работали с одним и тем же человеком, не зная друг о друге.
Мёртвый этап воронки. Не в системе, а между людьми. Продажи видели свой кусок, методологи свой. Ни в одном ТЗ этого пересечения не было. Откуда бы ему взяться? Каждый описывал только то, что видел.
Ещё находка: “Кто отвечает за возврат?” Бухгалтерия: “Продажи инициируют”. Продажи: “Это поддержка”. Поддержка пересылала обратно в бухгалтерию. В системе процесса возврата просто не было. Были разрозненные действия в трёх отделах, и человек с заявкой на возврат ходил по кругу.
А вспомни CRM из первой статьи? Один разработчик строил систему 2.5 года. Закон Конвея наоборот: одна голова – один неделимый монолит. Структура коммуникаций из одного человека породила архитектуру, которую невозможно разделить.
Бункер (silo) – это не “отделы не общаются”. Это разрыв в потоке знаний, который зеркалится в архитектуре. Менеджерскими инструментами его не починишь – нужно менять то, как команда понимает домен.
Предположения в голове, когнитивные ловушки, организационные силосы – три слоя одной проблемы. Серебряной пули нет, но есть точка входа, с которой стоит начать: договориться о словах.
Ubiquitous Language как инженерная гигиена
Звучит академически. На практике всё проще: если два человека называют одно и то же разными словами (или разные вещи одним словом), в коде будет бардак.
На тех же ES-сессиях в Andrews обнаружилось: слово “студент” использовалось для всего подряд. Для продаж студент – это лид, потенциальный покупатель. Для платформы – зарегистрированный пользователь. Для методологов – человек, проходящий конкретный курс. Для бухгалтерии – плательщик. Каждый отдел использовал одно слово, подразумевая разное.
В коде это была одна сущность. С полями, которые имели смысл только в одном контексте и были null в остальных:
// Одна сущность на все контексты
public class Student
{
public string Phone { get; set; } // продажи
public string UtmSource { get; set; } // маркетинг
public int? CurrentModuleId { get; set; } // методология (null, если ещё не начал)
public decimal? AmountPaid { get; set; } // бухгалтерия (null, если лид)
// ... ещё 40 полей
}
Когда мы разложили “студента” по контекстам, код начал отражать реальность:
// Каждый контекст знает только то, что ему нужно
namespace Sales { public record Lead(string Phone, string UtmSource); }
namespace Learning { public record Learner(int CurrentModuleId, Progress Progress); }
namespace Billing { public record Payer(decimal AmountPaid, PaymentMethod Method); }
“Тариф” – ещё один пример. Для продаж тариф – это цена пакета (базовый, премиум, VIP с наставником). Для методологов – учебный план (модули, домашние задания, аттестация). В коде – одна таблица, два несовместимых смысла.
Классический пример из DDD-литературы – слово “Пользователь”. В зависимости от контекста это Лид, Клиент, Заказчик, Контактное лицо, Плательщик, Получатель – каждый со своим набором атрибутов, поведением и жизненным циклом. Попытка уместить всё в одну сущность User – это god object в вашей голове, который превращается в god object в коде.
Когда мы на ES-сессиях настояли на том, чтобы называть вещи своими именами, стало видно то, что раньше было невидимым. “Студент начал учиться” для продаж означало оплату. Для методологов – первый вход на платформу. Метрики конверсии из-за этого врали – и никто не замечал, потому что все использовали одно слово и думали, что говорят об одном и том же.
Ubiquitous Language – не абстрактный принцип из книги Эванса. Это инженерная гигиена. Как нейминг в коде, только для всей команды. Обсуждаешь на русском, код на английском, документация на третьем языке – и удивляешься, почему разработчик построил не то. Он построил ровно то, что понял. Проблема в “понял”. (Подробнее про то, как одно слово ломает целую архитектуру, разбираю в Telegram)
Как выстроить единый язык на практике
Не нужен формальный “глоссарий” на 40 страниц, который никто не прочитает. Нужны три вещи:
-
Собрать людей в одну комнату. Буквально. Продакт, разработчик, доменный эксперт. Взять конкретный процесс и пройти его шаг за шагом. На ES-сессиях мы делали это с ватманом и стикерами – и за 3-4 часа находили больше расхождений, чем за месяц переписки в Jira. Да, это стоит времени. Да, для распределённых команд сложнее (но Miro неплохо работает как замена ватману). Стоимость такой сессии – несколько часов. Стоимость невыявленного assumption – месяцы.
-
Фиксировать терминологию в коде. Если бизнес говорит “бронирование”, а в коде
Order– кто-то врёт. Код должен говорить на языке домена. -
Переспрашивать. Каждый раз, когда кажется “ну это и так понятно” – именно в этот момент стоит уточнить. “Когда ты говоришь «студент» – ты имеешь в виду того, кто оплатил, или того, кто начал проходить курс?”
Как перестать проектировать на предположениях?
Окей, предположения повсюду, когнитивные ловушки работают круглосуточно, силосы плодятся. Что конкретно с этим делать?
Event Storming
Все примеры из Andrews, которые ты видел выше – мёртвый этап воронки, круговой возврат, “студент” как god object – мы нашли за одну оффлайн-сессию Event Storming. Ватман, стикеры, 3-4 часа, люди из разных отделов в одной комнате.
Event Storming – техника, которая за 4 часа показывает команде то, чего она не знает о собственном домене. Подробно разберём в отдельной статье: как готовить, как фасилитировать, что делать с результатами.
Парное программирование
Не про менторство джунов и не про формальный code review. Когда два разработчика пишут код вместе, предположения выплывают в реальном времени. “Подожди, а почему ты считаешь, что тут всегда будет один провайдер?” Вот этот вопрос мог бы сэкономить нам на WebMoney Travel пару месяцев работы.
Продуктовые команды вместо проектных
Проектная команда: собрали, сделали, разошлись. Знания задокументировали ушли вместе с людьми. Вспомни того единственного разработчика CRM из первой статьи. Продуктовая команда живёт с доменом, накапливает понимание и, что важнее, сталкивается с последствиями собственных решений. Когда ты сам будешь поддерживать то, что написал, мотивация разобраться в домене резко возрастает.
ADR: Architecture Decision Records
Код говорит “что”. Коммит говорит “когда”. ADR говорит “почему”. Одна страница: контекст, решение, последствия, альтернативы. Через полгода новый разработчик откроет ADR и поймёт не только что сделано, но и какие предположения стояли за решением. И главное: актуальны ли они до сих пор.
Честное предупреждение: ADR требуют дисциплины. Если их не обновлять, они превращаются в ещё один слой устаревшей документации. Но даже устаревший ADR полезнее, чем ничего, потому что фиксирует контекст решения на момент принятия.
Главный враг – не плохой код
Плохой код – симптом. Причина глубже.
“У нас один провайдер.” “Все платежи синхронные.” “‘Студент’ значит одно и то же для всех.” Три предположения с трёх разных проектов. Ни одно не было произнесено вслух. Ни одно не было записано. Все три просочились в код и стали архитектурными фактами, определяющими стоимость каждого следующего изменения.
Можно бесконечно бороться с техдолгом, рефакторить код, внедрять паттерны. Но если не чинить источник – предположения будут просачиваться снова, только в другие места.
Начать можно с малого. Перестать считать, что ты понял домен после прочтения ТЗ. Переспрашивать, когда “и так понятно”. Собирать людей в одну комнату и проговаривать процессы вслух, со стикерами на стене. Называть вещи своими именами – и в разговорах, и в коде.
В следующей статье перейдём от диагностики к конкретике: как выделять границы в системе, чтобы предположения не расползались по всему коду. Bounded Contexts, агрегаты и ответственность модулей.
А Event Storming, который за 4 часа вскрывает предположения целой команды, заслуживает отдельной статьи с пошаговым руководством. Мы до неё доберёмся.
Обсуждение – в Telegram-канале. Там короче, острее и чаще.
Что почитать по теме
- Domain-Driven Design – Eric Evans. Первоисточник: Ubiquitous Language, Bounded Context и стратегическое проектирование. Читать тяжело, но оно того стоит.
- Introducing EventStorming – Alberto Brandolini. Книга автора техники (доступна на Leanpub). Показывает, как ES решает именно проблему assumptions.
- Team Topologies – Matthew Skelton, Manuel Pais. Как организационная структура влияет на архитектуру (закон Конвея на практике) и что с этим делать.
- Thinking, Fast and Slow – Daniel Kahneman. Если хочешь глубже понять когнитивные ловушки, которые описаны в этой статье, – это первоисточник.
- Collaborative Software Design – Evelyn van Kelle, Gien Verschatse, Kenny Baas-Schwegler. Практическое руководство по совместному проектированию и техникам обмена знаниями в команде.