Технический долг: что это такое и как с ним жить
Быть или не быть регулярному рефакторингу? Мы попытались разобраться с этой холиварной темой — не без помощи людей, которые знают врага в лицо.
Технический долг — это модули, написанные на старых фреймворках, «костыли», мелкие недоработки, нарушенные стандарты и прочий мусор в коде, который в будущем усложняет жизнь. То самое чувство, когда читаешь код и думаешь: «Ну что за нехороший человек это всё писал». А потом оказывается, что этот «редиска» — ты сам из прошлого. Сам термин — метафора пионера паттернов и изобретателя технологии Wiki Уорда Каннингема.
Недоработки постоянно накапливаются. Чем их больше, тем сложнее добавить в код новую классную функцию. Необходимые усилия, которые придётся приложить в будущем, — как проценты по кредиту. Расплаты всё равно не избежать, а долг со временем всё растёт и растёт.
Согласно исследованию Stripe, разработчики тратят на технический долг треть своего времени, а компании «сливают» на это примерно 85 млрд долларов ежегодно. Быстро вывести на рынок жизнеспособный продукт, приносящий пользу бизнесу, — неплохое решение. Но надо помнить, что технический долг рано или поздно начнёт вредить проекту.
«Нельзя прийти и создать сразу идеальный проект. Каждый день мы будем что-то узнавать о предметной области, и эти новые знания нужно сразу внедрять. Вначале это будут костыли, из которых копится технический долг, — и это нормально. Ведь мы не можем, получая новую информацию, каждый раз переписывать продукт заново. Бизнес нас не поймёт».
Алексей Некрасов,
лидер направления Python в МТС, программный директор направления Python в Skillbox
Технический долг в жизни: два примера
Если вовремя не подумать о техническом долге, приложение может подхватить хроническую болезнь: многочисленные уступки «надо прямо сейчас» так въедаются в код, что рефакторинг провести просто невозможно. Вот две истории — одна об успешной оплате долга, вторая — о запущенной «болезни», справиться с которой так и не получилось.
Пример №1
Локализация приложения
Канадская компания разработала хороший продукт для местных клиентов и решила повторить успех, расширив рынок сбыта на ту часть страны, где говорят в основном по-французски. Разработчики управились за неделю, добавив кучу if-then-else. Решение с оператором условий было быстрым и грязным, но на тот момент — оправданным. Продукт смог заработать ещё денег.
Месяц спустя в Японии фаундер похвастался, что поддержку японского они могут добавить за неделю. Но одно дело — по-быстрому накодить один дополнительный язык, и совсем другое — добавлять так же криво все последующие. Тем более что для японского решение с новым слоем if-then-else вообще не подходило — мешали иероглифы, да и текст японцы пишут вертикально. В итоге разработчикам пришлось писать нормальную систему локализации.
Пример №2
Бесконечные «если» и неудавшийся рефакторинг
Разработчики написали приложение на устаревших версиях Python, PHP и Java с жёсткой структурой из бесконечных операторов if. В какой-то момент они решили полностью переписать ядро на свежих версиях языков, но на каждом шагу объёмы работы возрастали — постоянно обнаруживались какие-то новые зависимости. Да и бизнес не хотел ждать — требовал новые функции, чтобы оставаться конкурентоспособным. В итоге за год рефакторинга команда практически не продвинулась вперёд.
Как оценить технический долг
Американский программист, автор книг и статей по архитектуре ПО Мартин Фаулер определял размер техдолга простой формулой: чем больше усилий программистов тратится на какой-то недоработанный в прошлом фрагмент кода, тем больше наш технический долг. При этом консультант по разработке ПО Роберт Мартин считает, что технический долг в прототипах и краткосрочных проектах можно и нужно игнорировать.
Есть разные классификации технического долга.
Например, CTO производителя весов для угарного газа Tapad Даг Лиодден выделяет три его типа:
- Умышленный. Когда долг берётся, чтобы быстрее выпустить продукт. Его необходимо отслеживать в бэклоге и регулярно отдавать — иначе велик риск никогда его не вернуть.
- Случайно взятый долг или устаревший код. Чтобы вовремя его выявить, необходимо регулярно проводить аудит. Такой тип долга возникает из-за плохой коммуникации внутри компании.
- Критичный. Самый опасный тип долга — откровенно плохой и некачественный код. Его надо исправлять в первую очередь.
Как правило, самый неприятный техдолг связан с архитектурными проблемами: отсутствием инкапсуляции и модульности, плохим применением шаблонов, несоответствием типов данных модели данных. Из-за этого тормозится разработка, трудно вносить изменения в код, исправлять ошибки. Такой долг трудно или вообще невозможно обнаружить инструментами вроде IDE, PMD или Checkstyle.
Чтобы понять, как победить техдолг, нужно тщательно его проанализировать.
- Оцените опасность технического долга — в этом помогут рекомендации спикеров конференции GOTO Berlin 2017. Прикиньте, сколько усилий приходится тратить на его поддержку: как часто меняется конкретный файл, насколько сложен и объёмен код. Уберите из этого списка простые проблемы вроде неиспользуемого импортированного кода. Обычно их легко обнаружить с помощью специальных инструментов — того же SonarQube.
- Приоритизируйте работы над опасным техдолгом (оставшийся после первого шага список), определите и оцените зависимости между масштабами изменений: бизнес-ценность компонентов с долгом, историю, возраст и планы на блоки кода, вероятность того, что техдолг именно в этом месте будет вредить. Например, бессмысленно сразу исправлять проблемы, которые обнаружились в дублированных блоках кода, — сначала нужно устранить причину их появления, бездумную «копипасту». Также не стоит рефакторить непопулярную и устаревшую фичу, которую наверняка уберут из продукта.
Как объяснить бизнесу, что рефакторинг необходим
Владельцам софта нужна прибыль, поэтому они не хотят затягивать процесс разработки и бороться с техдолгом — особенно если близок старт продаж. Однако долг всегда приходится возвращать — иначе он создаёт проблемы в будущем продукте.
Конфликт разработчиков и бизнеса из-за рефакторинга — один из самых запутанных и сложных. Если игнорировать и копить техдолг, то разработчики потеряют мотивацию, а компания станет «техническим банкротом». Если же включить режим перфекциониста и слишком сильно фокусироваться на долге, конкуренты получат преимущество в скорости, быстрее выпустят новые фичи и захватят рынок. А полученную в ходе экспансии прибыль они вложат в погашение критичного долга — и останутся в выигрыше.
Вообще, Уорд Каннингем придумал концепцию техдолга именно для того, чтобы в понятных для бизнеса терминах аргументировать необходимость рефакторинга. Метафора простая: первая версия программы — как заём в банке. А каждая минута, которая тратится на исправление «костылей» в коде, — как проценты по кредиту. Если проценты долго не гасить, банк истребует весь долг и компания может закрыться. Однако небольшой долг, как и разумно взятый кредит, ускоряет разработку и помогает расти, главное — выплачивать его вовремя.
«Представьте платформу для нескольких независимых друг от друга клиентов (из терминологии «клиент — сервер». — Прим. ред.). У каждого есть собственный сервис, весьма требовательный к аппаратным ресурсам. Пока клиентов мало, эта схема работает. Когда же количество клиентов резко увеличивается, возникает необходимость менять архитектуру и запускать один сервис, который будет обслуживать всех, вместо нескольких копий, работающих параллельно. А это уже экономия в чистом виде и вполне убедительный аргумент для бизнеса.
Пока критической ситуации не возникло, многие воспринимают технический долг как нечто абстрактное. К сожалению, часто осознание приходит, только когда бизнес теряет деньги или несёт репутационные потери. Но после этого он сразу понимает важность рефакторинга».
Николай Мельников,
руководитель компании Sebbia
Важный момент: договариваться о ресурсах и времени команды, которые будут тратиться на рефакторинг, лучше до начала работ. Потому что вовремя гасить технический долг — критично для любого программного продукта. С заказчиком необходимо говорить на его языке: объяснять финансовые риски и приводить примеры из жизни.
«У нас в BestDoctor есть договорённость, что один день в неделю каждый программист выделяет на работу с техническим долгом и автоматизацию, которая улучшает жизнь всей команде разработки. Этот день полностью посвящён не продуктовым, а инженерным задачам. Я рекомендую ввести обязательный процесс отдачи технического долга раз в неделю или раз в две недели. Это позволит не запускать его».
Михаил Корнеев,
тимлид в BestDoctor, автор YouTube-канала «Хитрый питон»
Качественный рефакторинг возможен, только если вы «брали» техдолг осознанно, понимали, что перфекционизм в этот момент навредил бы бизнесу, и фиксировали момент создания долга. Поэтому небрежно писать код и думать, что исправишь всё когда-нибудь потом, — плохая идея.
«Если уже сложилась ситуация, когда надо немедленно реагировать и что-то делать с техдолгом, значит, что-то не так с процессами. Нужно сразу планировать работу так, чтобы оставалось время на рефакторинг — от 5% до 33% рабочей недели. Команда должна знать, что у неё есть выделенное время для таких задач».
Николай Мельников,
руководитель компании Sebbia
Чек-лист: как не накапливать технический долг
- Соблюдать баланс между размером техдолга и скоростью разработки.
- Стараться писать максимально качественный код.
- Регулярно проводить аудит и ревью кода.
- Включить рефакторинг в план работы на регулярной основе.
- Объяснять бизнесу, как технический долг сказывается на экономике продукта.
- Оперативно ликвидировать наиболее критические моменты технического долга.
- Вовремя обновлять инструменты, фреймворки и библиотеки.
- Регулярно обновлять документацию, фиксировать все изменения.
- Тщательно тестировать код.
Хотите изучить новый язык программирования или неизвестный фреймворк? Выбирайте подходящий среди курсов Skillbox.