Общество

Технический долг — это не ошибка в коде, а ошибка в мышлении

В массовом представлении «технический долг» — это плохо написанный код, устаревшие библиотеки или отсутствие тестов. Но в крупных системах, где работают сотни инженеров и миллионы пользователей, настоящая проблема проявляется иначе: небольшая продуктовая правка требует изменений в десятках мест, а команда тратит недели на синхронизацию того, что должно работать автоматически.

Почему система, которая стабильно работала год, вдруг начинает «ломаться» при каждом релизе? Где проходит граница между разумным упрощением на старте и архитектурной ошибкой, которая через два года обойдётся компании в миллионы? И как отличить «быстрое решение», которое ускорит развитие, от «костыля», который замедлит всё?

На эти вопросы отвечает Илья Детинкин — software-инженер, специализирующийся на поиске и устранении структурных узких мест в сложных серверных системах. Он работал над архитектурой системы аватаров, использовавшейся сотнями миллионов пользователей, и был единственным инженером, отвечавшим за платформу локализации одного из крупнейших приложений в своей категории. Его решения неоднократно выходили за рамки непосредственной задачи: внутренние инструменты, созданные им, принимали другие команды, а продуманный слой совместимости предотвратил масштабный технический долг, который одновременно лёг бы на несколько команд.

В этом интервью Илья на конкретном примере показывает, как «безобидное» архитектурное решение, принятое на старте, привело к тому, что одно изменение продукта стало требовать ручной правки около ста файлов. И как перестройка архитектуры свела эту работу к нулю, сделав систему дешевле и безопаснее в развитии.

— Илья, «технический долг» — одна из самых популярных тем в IT. Вокруг неё много шума, но часто кажется, что это просто синоним «плохого кода». Вы утверждаете, что самый дорогой долг — архитектурный. В чём принципиальная разница?

— Плохой код — это, условно, проблема, которую можно решить локально. Кривая абстракция, неоптимальный алгоритм, отсутствие тестов. Да, это замедляет разработку и повышает риск ошибок. Но в крупных системах есть другой, гораздо более дорогой тип долга. Он не про качество кода в одном файле, а про то, как система в целом устроена на уровне связей между данными и конфигурациями.
Представьте, что код — это дороги. Плохой код — это ямы на дороге, которые можно залатать. Архитектурный долг — это когда через всю страну ведёт только одна дорога и при строительстве нового района все машины упираются в этот перекрёсток и создают пробку. Ты не можешь починить это, просто залив асфальт. Нужно менять саму схему движения.
В моей практике архитектурный долг чаще всего проявляется через дублирование знания. Одно и то же правило или значение существует в нескольких местах, и при каждом изменении инженеры вынуждены вручную поддерживать их согласованность. Это огромная трата времени и главный источник ошибок в крупных проектах.

— Вы приводите конкретный кейс, где одна простая правка требовала изменения около ста файлов. Расскажите, как такое вообще могло произойти? Это ведь не было ошибкой одного разработчика, это системная проблема.

— Системная, конечно. Я работал над проектом, где существовал визуальный редактор сложного пользовательского объекта — условно, это был конструктор аватара пользователя. Редактор работал в нескольких клиентских приложениях, на разных продуктовых поверхностях, плюс были экспериментальные версии для разных групп пользователей.
Сервер отдавал клиенту конфигурацию редактора: что показывать, какие параметры доступны и где брать допустимые значения. На раннем этапе разработки часть этих путей к данным была жёстко прописана в самой конфигурации. Это было быстро и работало. Но модель данных развивалась, и при запуске новой версии продукта её внутренняя структура изменилась.
И тут выяснилось, что прежний подход требует колоссальных ручных усилий: чтобы поддержать новую версию, нужно было обновить около сотни файлов и конфигураций. Причина — дублирование знания. Конфигурация редактора хранила информацию о том, где лежат данные, хотя модель данных уже содержала эти сведения.

— То есть проблема была не в том, что разработчики ошиблись, когда писали этот код?

— Именно. В каждом отдельном файле код был рабочим. Это было сознательное решение на старте: вписать пути в конфигурацию, чтобы быстрее запустить продукт и проверить идею. Это разумный, прагматичный шаг на ранней стадии.
Проблема возникла, когда продукт вырос и это временное решение осталось в архитектуре. Оно начало работать против команды. Мы не могли позволить себе тратить столько времени на ручную синхронизацию при каждом изменении модели. Это была классическая ситуация, когда «временное» становится постоянным и начинает пожирать ресурсы.

— И каким было ваше решение? Вы переписали код?

— Нет, переписывание кода здесь не помогло бы. Мы изменили архитектуру взаимодействия сервера и данных. Решение заключалось в переходе к архитектуре, управляемой данными.
Мы ввели стабильные логические ключи — идентификаторы, которые не зависят от конкретной версии модели данных. А внутри каждой версии модели создали механизм, который сопоставляет эти ключи с реальными структурами данных для конкретного пользователя.
Вместо того чтобы конфигурация хранила вручную прописанные пути, она начала строиться на основе текущей версии модели данных. Клиент по-прежнему получал привычную конфигурацию, но источник истины переместился туда, где ему и место, — в саму модель данных.

— И что изменилось на практике? Стало действительно легче?

— Резко сократился масштаб изменений. Раньше при изменении модели требовалось обновлять около ста файлов. После переработки необходимость ручных изменений была сведена к нулю.
Но даже не столько в количестве правок дело, сколько в снижении риска. Когда одно значение продублировано в десятках файлов, очень легко забыть обновить один экспериментальный вариант, одну конфигурацию, одну версию редактора. Если же конфигурация строится из актуальной модели данных, риск ошибки становится исчезающе малым.
Это классический пример системного мышления: вместо того чтобы тратить время на ручной поиск и исправление ошибок, я создал механизм, который делает ошибки этого класса невозможными в принципе.

— Если обобщить: какой главный признак того, что архитектура больна и скоро начнёт тормозить разработку?

— Самый надёжный симптом — это когда небольшая продуктовая правка требует изменений в десятках мест. Один новый параметр — и нужно править десяток конфигураций, писать миграции, пересматривать тесты. Это верный признак того, что система заставляет разработчиков повторять одно и то же знание вручную.
Зрелая архитектура делает изменения дешёвыми и безопасными. Хороший тест для архитектуры — не «как быстро мы написали первую версию», а «как дёшево будет сделать следующий шаг». Если каждая правка заставляет вас пугаться — скорее всего, проблема в архитектуре, а не в коде.

— И последний вопрос: как бизнесу отличить архитектурный долг от обычного «кода, который надо подправить»?

— По масштабу. Если страдает один сервис — это локальная проблема. Если одна правка тянет за собой десятки изменений в разных командах — это архитектурная проблема. И она будет только дорожать.
Бизнесу стоит обращать внимание на то, как меняется скорость разработки со временем. Есть известный паттерн: в начале проекта всё быстро, а потом скорость резко падает, даже когда команда растёт. Это почти всегда говорит о том, что накопился архитектурный долг. Правильное решение здесь — не «ускорять команду», а вкладываться в упорядочивание архитектуры, чтобы остановить рост энтропии.
К сожалению, это кажется неочевидным — ведь деньги тратятся не на новые функции, а на «перекладывание кирпичей». Но именно это и позволяет в будущем выпускать новые функции с той же скоростью, не впадая в кризис, когда даже простая доработка занимает недели.

Поделиться

Поделиться

Источник

Похожие статьи

Добавить комментарий

Кнопка «Наверх»