Выбор «либо технический долг, либо дедлайн» ложный. Так же как ложный выбор «либо фичи, либо рефакторинг». В этой статье я расскажу, какими инструментами я пользуюсь, когда решаю, брать ли долг ради срока: возврат на инвестиции, проверка дедлайна на реальность и оценка обратимости. А ещё поделюсь тем, что я забрал по этой теме из книги Майка Барроуза «Канбан Метод».
Техдолг работает как финансовый долг
Уорд Каннингем сформулировал очень точную метафору. Технический долг устроен так же, как финансовый. И проблема финансового долга не в том, что он существует. Кредит сам по себе нормальный инструмент.
Проблемы начинаются в двух случаях.
Первый: долг невидимый. Мы его не видим, но проценты по нему платим. Каждый обход костыля и каждый лишний час на разбор запутанного кода. Это и есть выплата процентов, которую никто не считает.
Второй: долг нецелесообразный. Мы взяли большую сумму, а финансовой отдачи от неё нет. Просто теряем деньги.
Поэтому при работе с техдолгом я всегда держу в голове показатель ROI, возврат на инвестиции. Если выплата долга целесообразнее фичи, и мы можем это взвесить и посчитать, значит идём выплачивать долг. Работа с техдолгом не выбрасывается. Это точно такая же инвестиция, как и фича. Есть момент, когда её целесообразно делать. И есть момент, когда нецелесообразно.
Мы умеем оценивать ценность фичей до начала разработки. В общих чертах понимаем, что принесёт пользу, а что нет. С техдолгом всё то же самое. Его тоже можно оценивать. И для начала полезно понять, какой именно долг перед вами.
Квадрант Фаулера
Мартин Фаулер предложил матрицу, которая классифицирует технический долг по двум осям: осознанность решения и его благоразумие.
Осознанный и благоразумный. Мы целенаправленно срезаем углы, чтобы получить скорость. Но идём туда не просто осознанно. Мы фиксируем, что и когда сделаем для устранения. Прорабатываем наперёд: «выпускаем сейчас, рефакторим в следующем спринте».
Осознанный и безрассудный. Мы сознательно выбираем быстрое, но некачественное решение и игнорируем возможные последствия. Не прорабатываем, что будем делать с этим долгом. Не думаем, что будет в негативном сценарии. Без каталогизации и накопления данных такой подход со временем приводит к деградации качества проекта и усложнению поддержки кода.
Непреднамеренный и благоразумный. При разработке не всегда достаточно информации для качественных решений. В момент реализации нужных данных не было, или команда была неопытна в этой области. Мы действовали с лучшими намерениями. Со временем информация появилась, и мы поняли, что могли сделать лучше. Тогда мы этот долг каталогизируем и планируем выплату.
Непреднамеренный и безрассудный. Из такого долга вырастают системы, к которым люди боятся прикоснуться. Команда не обладает нужными знаниями и опытом. Причём как с точки зрения профессиональных навыков, так и с точки зрения понимания предметной области. И самое страшное: команда этого не осознаёт. Такой долг в конечном счёте приводит к уязвимостям и серьёзным проблемам с обслуживанием.
Квадрант помогает честно назвать то, что вы собираетесь сделать. Фразы «срежем углы к релизу и вернёмся через спринт» и «напишем как получится, потом разберёмся» лежат в разных квадрантах. И последствия у них разные.
Как я решаю
Ситуации бывают разные, поэтому у меня нет одного правила. Есть набор инструментов, которые я применяю в зависимости от контекста. Их три.
Какой возврат на инвестиции
Первый вопрос: какая процентная ставка по этому долгу? И нет ли задачи, которая при тех же затратах даст больший выхлоп?
Приведу пример с одним из самых измеримых видов техдолга: производительностью. Есть сервис, который потребляет условно 10 единиц вычислительных ресурсов. Это стоит нам денег, и мы понимаем, что можем его оптимизировать. Вопрос: действительно ли оптимизация выгодна именно сейчас? Нет ли задач, которые принесут гораздо больше, прямо или косвенно?
То же самое с качеством кода. Допустим, мы хотим включить линтер. В Go есть rowserrcheck, который проверяет, что вы не забыли проверить ошибку при чтении строк из SQL-запроса. Или bodyclose, который проверяет, что тело HTTP-ответа закрыто. Внедрение потребует прогнать линтер по всей кодовой базе и поправить найденное. Стоит ли оно того?
Зависит от цены ошибки. Если сервис работает по подписке, косяк может стоить условно 200 рублей. Придёт пользователь, скажет «у вас ничего не работает», мы извинимся и подарим месяц подписки. Ставки небольшие, ошибка не приводит к катастрофе.
А теперь возьмём рекламную систему, через которую проходят огромные объёмы денег. В adtech есть порог: когда через систему проходит много денег, даже маленькая проблема создаёт ситуацию, которую мы будем месяцами разбирать. Плюс репутационный счёт, который может оказаться невероятным. Тот же самый линтер, та же самая ошибка, а цена риска отличается на порядки.
Дальше простая арифметика. Вложим эквивалент 100 тысяч рублей рабочего времени в оптимизацию. Получим экономию тысячу рублей в месяц? Окупимся через восемь лет, забудьте. Двадцать тысяч в месяц? Окупимся за пять месяцев, уже интересно. А может, за те же 100 тысяч мы сделаем фичу, которая принесёт 20 миллионов? Тогда очевидно, что делать сначала.
Считать математически точно не всегда целесообразно. Но общий порядок цифр можно понять всегда. Сколько человеко-часов мы тратим на обход долга. Сколько стоит простой. Сколько принесёт фича. Надо всё считать, хотя бы грубо.
Дедлайн настоящий или выдуманный
Второй инструмент: проверить сам дедлайн. А он вообще реальный?
Настоящие дедлайны существуют. Регуляторные требования от государства. Фича, которую компания полгода назад презентовала на конференции. Такие даты очень сложно или невозможно сдвинуть. А если и сдвигаем, то платим деньгами или репутацией.
А есть выдуманные дедлайны. «Очень хочется закрыть квартал». «Очень хочется успеть к концу года и получить премию». В желании закрыть цели нет ничего плохого. Но надо понимать: сиюминутная выгода отдельного человека уничтожает дальнейший потенциал. Сейчас мы выйдем красивые, мы успели. А всю грязь и все проблемы спрятали под ковёр. И об этом никто не узнает. До поры.
Когда возникает выбор «или-или», не надо делать этот выбор молча. Надо идти и вести переговоры. Прийти к руководителю и сказать честно: к концу квартала не успеваем. Можем успеть, если срежем углы, но потом придётся переделывать. Можем сдвинуть срок. Можем убрать часть объёма и успеть без долга. Давай обсудим, как выходим из ситуации.
В моей практике неоднократно были случаи, когда мы по формальным признакам не выполнили квартальную цель. Осознанно, для того что бы выиграть долгосрочно, а не только в этом квартале. Мы вышли к руководству и сказали: реальную сложность мы недооценили. Видим вариант срезать углы и успеть, но наберём долг, с которым потом будет сложнее жить. Либо двигаем дедлайн и смотрим, от чего можем отказаться.
Когда вопрос поставлен адекватно и объективно, всем участникам комфортно. Вышестоящий руководитель способен сам посчитать ROI в голове и решить, что важнее. Может быть, задача в этом квартале принесёт больше денег, чем стоимость долга. Тогда берём долг. А может, задача подождёт, но стратегически важно, чтобы она работала хорошо. Тогда двигаем срок.
Про то, откуда вообще берутся нереалистичные сроки и кто как планирует, я писал в статье про округление оценок.
Обратим ли долг
Третий вопрос: это дорога с возможностью разворота или путь в один конец?
Долг на уровне метода или интерфейсов внутри кода обратим. Это всё можно переделать. Можно переделать структуру базы данных. Можно переехать с одной технологии на другую. Дорого, но реально.
А вот если мы берём долг в публичном API, он остаётся с нами навсегда. Если долг находится на стыке взаимодействия систем, это по большому счёту дорога в один конец. Другие команды и системы завяжутся на нашу реализацию. Мы этот долг либо никогда не выплатим, либо стоимость выплаты будет настолько велика, что за неё никто не возьмётся.
Поэтому правило простое. Если это путь в один конец, проценты по такому долгу будут огромными. И вы должны чётко себе это осознавать до того, как взяли долг, а не после.
Что я забрал из Канбан-метода
Последний месяц я не спеша читал книгу Майка Барроуза «Канбан Метод». И нашёл там рамку, которая хорошо объясняет, почему техдолг систематически проигрывает фичам в приоритизации.
В канбане задачи делят на классы обслуживания. Идея в том, чтобы не просто расставлять задачи по важности, а анализировать стоимость задержки: какие потери несёт каждая единица времени промедления. Классов четыре.
Ускоренный (Expedite). Задачи с очень высокой стоимостью задержки. Их берут в работу немедленно, даже приостанавливая остальные дела.
С фиксированной датой (Fixed Delivery Date). Есть чёткий срок. Стоимость задержки резко возрастает после конкретной даты.
Стандартный (Standard). Основная масса задач с умеренной стоимостью задержки. Обрабатываются в порядке очереди.
Нематериальный (Intangible). Поддерживающие задачи, у которых в моменте нет явного финансового результата. Задержка почти не несёт потерь сегодня.
Техдолг попадает прямиком в класс Нематериальный (Intangible). Стоимость его задержки сегодня незаметна. Именно поэтому в лобовой приоритизации техдолг всегда проигрывает: его цену в моменте сложно увидеть, а цена фичи очевидна. Само сравнение нечестное по построению.
Но у нематериальных задач есть коварное свойство. Стоимость их задержки растёт нелинейно. Если долго игнорировать нематериальные задачи, в какой-то момент задача перейдёт в класс Ускоренный (Expedite). Игнорируемое обновление зависимостей превратится в срочное закрытие уязвимости. Копившиеся проблемы с производительностью превратятся в инцидент, который тушат всей командой, остановив остальную работу. Вы всё равно заплатите. Только уже по тарифу «Ускоренный», самому дорогому из существующих.
Что с этим делать, канбан тоже подсказывает.
Выделяйте ресурсы на уровне политики. В канбане есть принцип аллокации ресурсов: такой-то объём пропускной способности мы выделяем на нематериальные задачи, такой-то на фичи. Спор «фича или рефакторинг» решается один раз, при настройке процесса, а не в каждом спринте заново.
Делайте долг прозрачным. Нематериальные задачи не должны существовать в виде заметок в головах двух разработчиков. Это такие же задачи, как бизнес-фичи. Они должны быть частью рабочего процесса: на доске, с явной работой, взятием в спринт и отслеживанием прогресса.
Работайте итерационно и эволюционно. Брать квартал на рефакторинг нецелесообразно. Задача сегодня, задача завтра, чуть тут, чуть там. Большими работами по переписыванию не стоит заниматься никогда. Почему я так считаю, я подробно разбирал в статье про переписывания.
Правила гигиены
Несколько коротких правил, которые я стараюсь соблюдать независимо от того, какое решение принято.
Фиксируйте долг в момент взятия. Если вы берёте долг осознанно, записывайте его сразу. Смотрим выше на квадрант Фаулера: благоразумный долг отличается от безрассудного тем, что есть план. Вы должны чётко понимать, что сделаете для устранения, когда и как конкретно. Незаписанный долг превращается в тот самый невидимый кредит, по которому вы платите проценты, не замечая этого.
Маленький рефакторинг это часть работы. Такая же, как все остальные. Выделяйте объём времени на нематериальные улучшения, которые помогают в работе. Это не только про код. Это и удобство команды, и улучшение процессов, и замена инструментов. Включайте это в полноценную работу. Не превращайте технические улучшения в процесс, который идёт где-то сбоку и по остаточному принципу.
Говорите на языке денег. Не пытайтесь разговаривать с бизнес-заказчиком на языке красивого кода или элегантной архитектуры. Бизнес отлично понимает, что такое возврат на инвестиции. И совсем не понимает, что даст более качественная абстракция.
Скажу честно: я как технический руководитель тоже не всегда понимаю. Я сам разрабатывал системы, я понимаю плюсы и минусы подходов. Но я уже достаточно оторван от конкретного кода. И фраза «код будет красивый» для меня пустая. Что нам это даст? Производительность? Надёжность? Скорость ревью?
Это работает и с техническим руководителем. «У меня тесты будут проходить быстрее». Насколько? На 3%? Это ощутимо ускорит цикл разработки? Нет? Тогда какой аргумент настоящий? Всегда разговаривайте на языке результатов.
Итог
Технический долг развивает бизнес так же, как фичи. Просто не так явно. Поэтому выбор «долг или дедлайн» я всегда раскладываю на три вопроса. Какой возврат на инвестиции у выплаты этого долга. Настоящий ли дедлайн, ради которого долг предлагают взять. Обратим ли долг, или это путь в один конец.
Канбан-метод добавляет к этому системную рамку. Техдолг живёт в классе Нематериальный (Intangible), поэтому в честной драке за приоритет он не выиграет никогда. Не заставляйте его драться. Выделите ресурсы на уровне политики, сделайте долг видимым на доске и выплачивайте его маленькими порциями. Иначе однажды он сам сменит класс на Ускоренный (Expedite), и тогда решать будете уже не вы.
Теги: