GraphQL у свiтi компонентiв від Катерини Поршнєвої — React fwdays онлайн | 27 березня
×Закрыть

Технический долг: профилактика и погашение

Image via Shutterstock.

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

Для начала разберемся, что такое технический долг (ТД). Под ТД я имею в виду:
— Откровенно плохую архитектуру;
— Не-модульность компонентов;
— Плохое качество кода отдельных компонентов;
— «Костыли»;
— Изменения, которые нужно сделать в отдельных модулях кода, вызванные другими изменениями.

Причины возникновения и последствия

ТД может быть вызван такими вещами, как:

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

 Плохое взаимодействие внутри команды, которое приводит к тому, что на этапе отладки приходиться тратить времени больше, чем было запланировано. Это, в свою очередь, возвращает к предыдущему пункту.

— Непонимание заказчиком того, как устроен его продукт. В таком случае разработчикам приходиться добавлять функционал, который противоречит первоначальной задумке. Либо успевать к определенным датам, когда продукт должен быть представлен инвесторам\тестировщикам\пользователям. В результате костыли в коде растут, как грибы.

— Отсутствие рефакторинга. Как правило, случается из-за невозможности донести до заказчика необходимость его проведения. И, как следствие, происходит потеря качества и производительности кода.

— Низкая квалификация сотрудников. Они вроде и хотели бы написать код лучше, но как-то не выходит.

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

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

А как все это выглядит со стороны проектного менеджера? Приблизительно вот так. Сроки подходят к концу, бюджет подходит к концу, резервов уже нет — а дело не сделано.

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

Документируем и оцениваем

Проектный менеджер должен работать с ТД приблизительно таким же образом, как и с тасками во время формирования плана на спринт. Обязательно следует документировать ТД: делать векселя и планировать время для выплаты долгов в каждом спринте, а также распознавать и устранять риски, которые могут вызывать появление такого долга.

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

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

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

Если вы знаете/имеете практику работы с ТД, пожалуйста, пишите в комментариях. Давайте обсудим вместе.

👍НравитсяПонравилось0
В избранноеВ избранном0
Подписаться на автора
LinkedIn

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




Підписуйтесь: SoundCloud | Google Podcast | YouTube


67 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Денис, спасибо за статью. Вы предлагаете, чтобы разработчики документировали ТД. Но как разработчику понять, что это ТД, особенно если он новичок в ИТ? Он может подумать, что так и надо делать.

К новичку, как правило, приставлен более опытный разработчик, который ревьюит его код и технические решения, разве нет? Альтернативно, на проекте может быть принята практика cross-review, но, по сути, это то же самое — всегда есть кто-то более опытный, который укажет новичку на «плохо пахнущий» код.

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

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

Непонимание заказчиком того, как устроен его продукт
Тут проблема не только в том, что он не понимает. А еще и в том, что его «понимание» меняется в зависимости от фазы луны =(

Кроме перечисленных причин TD, ещё 1 причина — это низкоприоритетные баги, о которых заказчик сказал «и так пойдёт». Как и любой другой элемент TD, имеют мерзкое свойство накапливаться и в результате ухудшать стабильность системы.
В бытность мою тимлидом, я выделял 20% времени спринта на TD. Помогало сдерживать неконтролируемый рост TD.

Мне кажется, что в большинстве случаев, можно сразу обозначить заказчику ограничения принимаемого технического решения для будущего развития проекта и разделить с ним (или даже переложить на него) ответственность по ТД, особенно в условиях сжатых сроков и изменчивых требований.

Если организационное решение об использовании костылей принимается без заказчика, то и обосновывать затраты становится сложно.

Тогда бы команда обращала внимание на возможные последствия
Т.е. в проекте не было того, кто держал бы «в уме» общую картину... и мог дать прогноз по эволюции проекта (продукта) с учетом перспективы.

Жить здесь и сейчас.
Прошлого — нет.
Будущее — не существует.
))

В чем еще таски явные помогают, так это в постепенном вовлечении заказчика (PO) в понимание реальности tech debt. Проговаривание на планировании. Упоминание в связанных бизнес задачах как «а вот с тем импрувментом было бы так-то и так-то дешевле». По проведенным рефакторингам очень хорошо отмечать в оценке насколько стало наоборот легче. Для свеже-неприрученного PO все эти технические мумбаюмбы — это отвлечение от главного, от business value. Если он сможет увидеть положительное влияние, то можно с удивлением обнаружить больше tech тикетов в итерации чем сам бы положил.

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

у технического долга разные источники. Если разработчик сознательно пишет «костыль», то почему бы сразу не создать таску по его рефакторингу?

Если разработчик сознательно пишет «костыль»
А если не сознательно?

То, нужно задокументировать ТД как только будет осознано, что это был костыль.

Например, как продукт Code Review.

когда есть архитектура А, а потом накидали кучу новых тасков, из-за которых нужно переписывать на архитектуру Б — ваше утверждение справедливо. Но если человек пишет костыль, как уже сказили тут, то всем понятно что нужно будет переписать по нормальному. Мне кажется как то так.

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

всем понятно
а по факту оказывается что «не всем» и вообще «ну забыли». Сознание человека работает довольно хитро и у каждого по своему, имея «сходную структуру процессов» и/или «сходный контекст» лишь «приблизительно». И если просуммировать все накопленные «неточности», то в итоге может быть получена приличная ошибка.

Из практики запомнилось больше всего волевое решение нашего CTO часть пары спринтов части команды выделить на погашение тех. долга. Но не всегда такое работает.
Из хороших подходов вот некоторые: youtu.be/vqEg37e4Mkw?t=124 я, честно, пользуюсь только правилом бойскаута. Нужно вводить Red-Green-Refactor.
И в любом случае, считаю, нужно следовать Kent Beck’s rule: Make it work. Make it right. Make it fast.

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

Нужно понимать зачем гасить ТД и тогда нет проблем со впихиваем в спринты. Если нужно обьяснить что-то заказчику — то просто говоришь, что погашение ТД дает лучшую скорость выполнения новых задач.

Проблема как раз в том, что «новые задачи» могут измениться пока вы до них доберетесь и тогда вы просто сделали код лучше, а могли сделать больше полезной работы либо сделать какие-то улучшения продукта быстрее
Плюс невозможно проверить будет ли быстрее если закроете ТД.

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

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

К сожалению, заказчик не всегда это понимает. Были такие случаи с одним и тем же заказчиками на легаси продукте:

Разговор с CTO:
Я: — Проекту уже много лет, на текущий момент тех долг накоплен с таком количистве что выкатка новой функциональности происходит крайне медленно из-за копания в дебрях старого кода, попытках ничего не сломать. Отсутствие модульности в архитектуре не позволяет добавлять новую функциональность изолированно. Постоянные статические зависимости и связанность не позволяют писать юнит тесты. Вот функциональные тесты, что позволяют посмотреть как работает сборка. Давайте на их основе

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

Разговор с CEO (после договорёности о том что за овертаймы тоже нужно бы платить):
CEO: — У вас много овертаймов. Почему?
Я: — Из-за постоянного добавления ASAP’ов, неприрывно растущего спринта и состояния системы в котором её трудно поддерживать. И при этом постоянно сжимающихся сроках.
— У нас на горизонте большие фичи, которые нужны в такой-то срок.
— Если это и возможно то малореально. Необходимо как минимум убрать асапы из спринтов на время разработки фич, или получится как в старой задаче о Ахиллесе и черепахе. Ещё нужно дать команде возможность привести продукт в поддерживаемое состояние — я уже предоставлял план базовых действий вашему CTO.
— Да забудь ты о той %@#... черепахе. Асапы это производственная необходимость, а фичи мне нужны к такому-то времени.

а что заставляет вас работать с такими заказчиками?

Уже ничего — не работаю больше на том проекте. Раньше — команда хорошая. Мне действительно нравилось работать с теми людьми в одной команде.
Сейчас повезло — нашёл и хорошую команду и то что технические задачи тоже нужно решать продкут овнеры понимают.

Коли проект довготривалий і постійно розвивається, то раз в кілька років потрібно переглядати архітектуру.

Раз в несколько лет? Это шутка типа? :)

Ніколи не бачив 10-15 літніх проектів?

раз в кілька років
дуже повільно IMHO

А как в этом убедить заказчика ?

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

А вот это интересно. Было бы хорошо, если бы ты привел методику или хотя бы пример.

Да точно так же как команда дает оценку обычным задачам. Нужно приготовить эстимейт после рефактора и без него.

Так вот я не представляю, как подготовить эстимэйт для этого. Рефакторинг может оказаться сильно дороже. например, поддержки старого и написания параллельно нового продукта с похожим функционалом. И понятно станет что дороже, только после проведения как одной так и другой работы.
По сути ты предлагаешь за эстимэйт выдать фантазию.

Я б еще добавил в пункты лень и низкую мотивацию. У разраба может быть и много времени, и все возможности на то чтобы сделать более-менее адекватно изменения, но он выберет вариант в котором меньше нужно думать и сделать чтобы побыстрее от него отстали. В итоге появляются костыли и затычки в несколько слоев.

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

для таких случаев есть code review.

Не всегда спасает. вот сделал чувак откровенное гавно, приходит на ревью, ему говорят что совсем плохо и надо переделывать. Хорошо если это переделывается за час, а если уже потратил на задачу пару дней и пулреквест заходит на овер 30 файлов, то уже никто ничего переделывать не будет. Позиция манагеров в этом случае «да, плохой код это плохо, но уже фича сделана и нам нужна/разбирайтесь сами/потом переделаем абищаю!»

Именно, и в этот момент нужно задокументировать косяк, что бы потом не забыть.

А как вы будете мотивировать разработчиков документировать ТД? Ведь чем больше они документируют, тем больше им потом переделывать?

Тут, конечно, выработать культуру. От хорошего кода и разработчики тоже выигрывают, у них в будущем потом меньше непредвиденных проблем при добавлении новых функци, а как следствие никаких работ по ночам перед релизом.

При грамотном подходе, декомпозиции задачи и требованиями к PR — в большинстве случаев спасает. Конечно, если фичу надо на вчера, то тут ничего не поможет.

Лично я стороник continuous refactoring, в рамках каждой задачи закладываю необходимые изменения для рефакторинга, чтобы решение гармонично вписывалось. Если у заказчика совсем горит — то рефакторинг переносится.

В итоге тех долга почти нет.

Абсолютно верно, но в данном случае рассматриваться случаи когда времени на рефакторинг нету.

времени на него нет, если вы изначально его туда не заложили.

это то о чем я написал (перечитал коммент и понял что забыл написать саму мысль).

то есть я знаю что просто сделать задачу у меня займет 2 часа — я говорю что сделаю ее за 4-6 часа, так как проверил код, и видно что там надо поменять архитектуру, и это съест еще 2-4 часа.

в итоге задача делается 4-6 часов, и тех долг отсутствует, заказчик доволен так как задача сделанна в срок, и команде хорошо.

или же, часто когда думаю что задача не сложная и рефакторинг не нужен, но моя 5 точка говорит что дело пахнет керосином. говорю как думаю, пусть будет 4 часа, но также говоря что возможно большие риски, которые всплывут под конец. так вот, в рамках этих рисков можно решать те не предвиденные проблемы ввиде тех долга.

как по мне проблема тех долга, это непонимание разработчиков что такое оценка задачи, и страх вносит изменения в систему

Такое чутье и понимание вопроса доступно не всем разработчикам к сожалению. Но даже имея такой подход никто не застрахован от ошибки во время эстимейта и вот уже в этом случае работа с ТД поможет проектному менеджеру удержать все под контролем.

если профакапил, то просто заложу рефакторинг в следующею таску которая будет затрагивать профакапленный модуль.

для меня эта система работает.

хотя есть конечно случаи когда у заказчика всегда все на вчера, тогда надо уменьшать скоуп рефакторинга за раз.

есть один такой проект у меня, за полгода функционала накидали немерянно — все на вчера, изначально было полное УГ, и память текла жутко в итоге часто все крешилось. спустя полгода такого безрпрерывного небольшого рефакторинга нет мемори ликов, почти вся система зарефакторина, осталась только маленький модуль.

хотя есть конечно случаи когда у заказчика всегда все на вчера, тогда надо уменьшать скоуп рефакторинга за раз.

Вот в таком случае и хорошо создать таску с техническим долгом.

я оставляю @TODO комменты в коде

Часто не работает. Или нужно писать очень большой и детальный коммент.

На что заказчик тебе говорит «не гони, мои собственные разработчики закрывают такие задачи за два часа, с чего бы я тебе платил за 4-6?»

Причем, он даже не врёт — действительно, закрывают, в духе чик-чик и в продакшен.

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

заказчик не заинтересован в качестве продукта, но последнее крайне редко бывает

Уж поверьте моему опыту — гораздо чаще, чем вы думаете. Просто есть специфические рынки, где на высокое качество всем насрать: ПО навязывается конечным пользователям административными методами, и их недовольство никого не волнует.

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

Это только один пример. Стартапы, где нужно быстро и дешево запилить MVP и получить еще один раунд финансирования, или быстро застолбить рыночную нишу — второй. Продуктовые компании, где в силу тех или иных причин их продукт занимает практически монопольное положение (и это точно не изменится в ближайшие годы) — третий.

Проблема «good enough software» волновала индустрию еще с конца 90х годов, когда многие из нас были еще школьниками или студентами:
www.satisfice.com/...s/good_enough_quality.pdf

Собственно, пресловутый Microsoft пользуется такими приёмчиками и по сей день, вспомнить хотя бы откровенно багливые первые официальные (!) сборки Windows 10. А уж багливость Windows 9x стала легендой — и, одновременно, именно Windows в конце 90х прочно и надолго заняла нишу desktop OS.

Или те же покемоны — по Сети кочуют целые списки глюков: www.ign.com/...tches,_Freezes,_and_Fixes, www.techradar.com/...kemon-go-problems-1325007, что не мешает игре иметь бешеную популярность.

И, заметьте, это мы говорим о видимом снаружи «пользовательском» качестве. Примеров, где заказчику откровенно насрать на технический долг при вполне приличном «видимом» качестве — в голову приходит намного больше.

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

Mvp — там о качестве вообще глупо думать, ибо слишком много рисков, и тех долг начинают закрывать как продукт себя показал — и это правильно.

А вот насчет монополистов и их багов — это недалекость менеджеров, ибо если сегодня вы монополия это не значит что так будет завтра. ИЕ такой пример кстати.

Далее, как правило внешная глючность и есть результат тех долга, так как он что называется creates room for bugs.

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

Это просто вариант того что предложил автор статьи.

Просто пробывал с тикетами, и это почти никогда не работало.

Да никто не спорит с твоим (точнее это не столько твой, сколько разумный и корректный) подходом. Больше обсуждается вопросы, как его применять в различных условиях.
Как насиловать подчиненных, чтобы следовали ему, как насиловать заказчика, чтоб осознавал, что ему это в первую очередь надо.

Один раз я смог внедрить TDD подход только с помощью жены генерального (а да она была каким-то там директором то же). Толпы програмеров на той конторе меня до сих пор не навидят, им же работы подвалило.

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

Если что, я вовсе не критикую ваш подход! Отнюдь — там, где позволяет ситуация, мы и сами стараемся так делать.

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

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

что бы заложить необходимое время для рефакторинга нужно знать что именно нужно переделать / доделать, по моему мнению это относится к самой задачи
ну вы же планируете как будете имлементит, сразу видно где оно не вписывается в архитектуру, возможные ботлнеки и так далее, а соотвественно понимаете/видете как изменить систему чтобы ваши изменения гармонично вписывались

Також пишу todo в коді і стараюсь робити continuous refactoring. Але є один нюанс. Це не завжди можливо. Ось вам приклад. Ви заюзали якусь бібліотеку, все ок, все працює. Через якийсь час у вас трошки міняється умова а бібліотека не дозволяє її виконати. Відповідно вам треба викинути цю лібу, переписати клієнтських код і тести і аж потім робити фічу. Це може зайняти багато часу. Або варіант 2: написати костиль/хак і відкласти рефакторінг. От тут і починається проблема. Рефакторінг займе багато часу відповідно на коли його відкласти хз. У цих випадках я користуюсь цією самою технікою що описана в статті. Думаю тікети з поміткою тд норм штука ;)

архитектура вам в помощь.

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

далее, скорее всего, это хак можно завернуть в еще один адаптер над адаптером и теперь ваш хак живет в отдельном объекте — задача которого реализовать только этот хак, и проксировать все остальное к первому адаптеру, правда такой хак уже нихрена не выглядит как хак, а гармонично вписался в систему, не поломав ровным счетом ничего.

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

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

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

BarChart buildBarChart(OurDataType data);

где BarChart и OurDataType определенны в нашем приложении, а не типы из стороней либы. реализация этого на таск не займет много времени.

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

но еще раз не все заказчики готовы идти на такие затраты
ну тут уже зависит от способноси договариватся.

я потому и за то чтобы делать это в рамках связанных бизнес задач — так легче покупают.

обычная програмерская лень
еще, часто слабая квалификация, но это вообще отдельная и огромная тема выходящая за вопрос тех долга

Вот по этому мне надоело менеджерить и тимлидить, так все и всё задрали.
Да по правильному нужно делать нормальную декомпозицию задачи, делить на слои, писать врапперы, но из 10 програмеров 6-7 будут на оное забивать и их надо пасти и насиловать. Плюс к этому заказчик, у когорого «просрали все полимеры» часто.

Подписаться на комментарии