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

Природа технического долга


Само понятие технического долга впервые ввел Уорд Каннингем (Ward Cunningham), разработчик технологии wiki и один из создателей экстремального программирования. В 1992 г. в своей статье он сформулировал это понятие в виде метафоры финансового долга: так же, как и при финансовом займе, разработчики могут получить дополнительные средства здесь и сейчас за счет использования быстрых и не оптимальных технических решений, но неизбежно будут расплачиваться за них при дальнейшей разработке, в независимости от того, как будет выплачен этот долг — постепенными выплатами процентов или одним платежом.

image

Но если проблема технического долга была описана еще 25 лет назад и встречается практически на любом проекте, почему еще нет методики управления проектами, которая позволяла бы избежать само появление технического долга? Ответ кроется в самом понятии проекта. Одним из ключевых отличий проекта от других видов деятельности является уникальность конечного продукта. Там где уникальность, там и непредсказуемость, и именно она порождает изменения на проекте и вносит трудности в первоначальное проектирование системы.

Конечно, можно попытаться построить архитектуру, предусматривая возможные изменения, но здесь команда столкнется с таким понятием как “кошелёк Миллера”: правилом, при котором в кратковременную память человека можно «положить» одновременно не более девяти «монет». А если количество элементов превышает это значение, то мозг пытается сгруппировать информацию так, чтобы их количество было от пяти до девяти.
Можно попытаться делить компоненты на более мелкие, чтобы уложиться в этот “кошелек”, но сложности от этого меньше не станет, да и количество абстракций при таком подходе будет расти с катастрофической скоростью. А как известно, любую проблему можно решить путём введения дополнительного уровня абстракции, кроме проблемы слишком большого количества уровней абстракции.

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

Так или иначе, неизбежность изменений на проекте провоцирует появление технического долга.

Как определить, что на проекте есть проблема технического долга


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

image

На что следует обратить внимание:

  • Участки кода, которые часто подвергаются изменениям. Скорее всего эти участки требуют серьезной доработки.
  • Большое количество пометок FIXME и TODO и малое количество комментариев в коде должно насторожить. Современные IDE имеют встроенную поддержку работы с такими метками, так что это один из самых быстрых и надежных способов распознавания слабых мест в системе.
  • Отсутствие тестов. Без них у разработчиков нет уверенности в том, что их изменения не нарушат работу системы. Команда относится к коду, не покрытому тестами как к карточному домику, стараясь избегать лишних, а порой необходимых, изменений.
  • Отсутствие документации. Ситуация схожа с предыдущем пунктом. Отсутствие регламентированной информации заставляет разработчика самому додумывать картину происходящего, и есть вероятность, что у него сложится неверное представление о системе. Ситуация усугубляется тем, что в подавляющем большинстве случаев на проекте работают разработчики разного уровня подготовки и компетенции. А различие в понимании кода еще сильнее усложняют процессы чтения и написания кода.
  • Устаревшие технологии и инструменты разработки. Любая библиотека или модуль обновляется, а поддерживать старые версии со временем становится все сложнее и затратнее.
  • Отсутствие стандартов разработки. Каждый разработчик делает что и как он хочет.

Методы устранения технического долга


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

    image

  • Используйте гибкие методологии разработки (Agile). Основная суть методик состоит в разбиении разработки на небольшие, изолированные итерации длиной от одной до четырех недель. Таким образом обходится ограничение “кошелька Миллера”, ведь теперь в каждой итерации нужно работать только с ограниченным потоком информации, в котором легче управлять сложностью, и соответственно, техническим долгом.
  • Введите общий и понятный всем регламент по работе с техническим долгом. Создайте бэклог, в котором будут находиться задачи на его выплату. И при добавлении нового функционала или при планировании следующей итерации уделяйте время на выполнение этих задач. Можно попробовать выделить время только на выполнение этих задач, но тогда разработка нового функционала зависнет, и скорее всего, вы столкнетесь с непониманием со стороны бизнеса. В своем проекте мы решили выделять 20%-30% времени от очередной итерации на погашение технического долга.
  • Создайте метрики, по которым будет удобно следить за выполнением технического долга. Такие метрики индивидуальны для каждого проекта. Например, для своего проекта мы выделили такие метрики, как процент покрытия тестами и документацией, соответствие кода с принятыми Style Guides и сложность разворачивания системы на локальном окружении разработчика. Некоторые из метрик собираются автоматически, некоторые только субъективно каждым разработчиком. Важно, чтобы эти метрики были доступны всем, каждый участник видел прогресс и был согласен с ним.

Заключение


Проблема технического долга весьма актуальна на данный момент, и для каждого проекта требуется индивидуальный подход к проблеме его устранения.

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

Комментарии (11)


  1. DnV
    25.04.2018 14:35

    > Большое количество пометок FIXME и TODO и малое количество комментариев в коде должно насторожить.
    По моему опыту, комментарии в большинстве случаев наоборот свидетельствуют об архитектурных ошибках и костылях. Исключением являются лишь пояснения к сложным алгоритмам и документирование интерфейсов.


    1. kalashnikovisme
      25.04.2018 14:54
      +1

      Как по мне, комментарии в коде напрямую не свидетельствуют о том, что есть архитектурные ошибки. Они свидетельствуют о ценностях команды и принципах работы на проекте.
      Сами костыли свидетельствуют об этом — это факт :) а комментарии не при чём.


      1. DnV
        25.04.2018 16:02

        Так а какой правильный костыль обходится без комментария? ;)


        1. kalashnikovisme
          25.04.2018 16:05

          Если уже писать костыль, то незаметный :) а то пропадает вся романтика поддержки проекта в дальнейшем)


    1. AstarothAst
      26.04.2018 10:27

      Да, если есть комментарий в коде, значит код не достаточно отвечает на вопрос о том, что в нем происходит, так что идеальный код — это код, который можно понять без единого комментария. Исключение — когда коммент отвечает не на вопрос «что здесь происходит?», а на вопрос «почему происходит именно это?». В бизнес-логике сплошь и рядом приходится писать комменты вида: «Вот этот БРЕД тут происходит потому, что приходится учитывать условия маркетинговой акции, которая завязана на фазу луны и первую букву отчества пользователя».


  1. WizardryIB
    25.04.2018 14:45

    Земляки, хотелось бы больше конкретики и примеров.


  1. romas1982
    26.04.2018 18:09

    Спасибо за материал, тема, безусловно необъятная, и в одну статью не влезет, есть пара вопросов:


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

    Спасибо!


    1. romas1982
      26.04.2018 18:11

      В п.2 меняющийся, вместо «отменяющийся». Прошу прощения.


    1. Neikist
      26.04.2018 19:36

      Хоть и не автор, но выскажу свои соображения.
      1) Тут мне тоже интересно мнение автора, но на мой взгляд достаточно просто выделять под технический долг часть итерации. Аджайл это же не про то что стелимся всегда под желания заказчика и тут же откладываем что то важное если новая хотелка появилась.
      2) Часто меняющийся код может означать две вещи: в нем много ошибок, тогда технический долг очевиден, либо же просто важную часть решения, но тогда это повод обратить внимание на архитектуру этого места и подумать над улучшением, чтобы в дальнейшем еще меньше проблем было. В случае же не меняющегося кода ситуация такова: код пусть и костыльный, но рабочий, а значит его можно и не трогать, поскольку наша же цель сделать архитектуру и продукт достаточно хорошими, а не идеальными. А если код недостаточно хороший, то возвращаемся к тому что в нем будут ошибки, а соответственно он перейдет в категорию кода который трогают часто.
      3) Комментарии и документация это не маркер тех. долга, а маркер что на эти места следует обратить внимание, и возможно они являются техническим долгом.
      P.S. Ну и не забываем что аджайл на то и аджайл, что здравый смысл превыше формальных вещей каких то, так что если все видят что маркера тех. долга нет, но считают что он есть — значит считаем что тех. долг есть на самом деле и плюем на формальные признаки.
      P.P.S. Сам не сварщик, маску на свалке нашел (ни разу не менеджер, просто немало про аджайл на хабре пишут и в подкастах говорят, хочешь не хочешь наслушаешься)


      1. romas1982
        28.04.2018 03:57

        1. По опыту в большинстве проектов фиг кто даст выделить время и отдать часть итерации под техдолг. Почему-то считается, что это вообще не проблемы бизнеса. Мы пробовали разные сценарии: парко-хозяйственный день, когда 20% итерации идёт на техдолг (не очень работает), технологические итерации, когда команда между проектами целую итерацию делает только техдолг и инженерные задачи ( реально работает, но крайне непросто убедить бизнес пойти на это). Аджайл в идеальном мире действительно не про это, но в реальном мире часто все идёт именно по пути замены требований по пути. Тоже самое касается внесения новых задач в уже стартанувшую итерацию. Интересен именно нехороший сценарий.
        2. Выходит, что признание долгом долга вещь сугубо субъективная, я верно понял? Т.е. в задах системы живет пачка овнокода, но поскольку она работает и жить не мешает — она признается нормой?
        3. Вспомнилось «highly likely”:))) Документация часто сама является техдолгом, потому как написать-написали, а поддерживать забыли:))

        Про аджайл спорить не буду, до не тот топик.


        1. AstarothAst
          28.04.2018 10:11

          технологические итерации, когда команда между проектами целую итерацию делает только техдолг и инженерные задачи ( реально работает, но крайне непросто убедить бизнес пойти на это)

          У нас не сработало. В частности потому, что если три итерации делается «бизнес» из говна и спичек, при экономии не только на спичках, но и на говне, с аргументацией «надо дать бизнесу ценность СЕЙЧАС, а на тех-итерации поправите и сделаете по уму», то к тех-итерации подходишь со слоем костылей, который вырос еще на одном слое костылей, под которыми тоже не все слава богу. И переделать за одну итерацию то, что говняколось три нет никакой возможности, в лучшем случае причесать слегка, что б самый страшный ужас наружу не лез.