🏆 Рейтинг ІТ-работодателей 2019: уже собрано более 5000 анкет. Оцените свою компанию!
×Закрыть

Выплачиваем технический долг с пользой для бизнеса

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

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

Митинг

Менеджмент (радостно): мы согласовали все детали и запускаем новую фичу!

Девелопмент (недовольно): у нас устарела либа для отрисовки графиков, ужасная сортировка в старом модуле. И мы уже полгода не можем привести в порядок структуру проекта, после того как маркетингу срочно понадобилось переименовать тему.

QA (озабоченно): не работает апгрейд с AmEx картами и не применяются кастомные темы, это нужно забрать в следующий milestone.

На таких митингах кажется, что у всех разные цели. Бизнес уже наступал на грабли инвестиций в девелопмент без видимого результата. Тимлид знает, что накопленные костыли рано или поздно выстрелят ему в ногу. А QA на прошлой неделе 10 минут рассказывал на ретроспективе «what we should start doing», после того как ключевые юзеры зарепортили баги напрямую CEO.

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

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

— Я так не могу, мне стыдно, что в продукте такой код. У меня в субботу будет время, пойду наведу порядок, хотя бы где успею.

— Тима очень настаивала, мы выделили один спринт под рефакторинг, смогли согласовать с клиентом, но вчера после демо он остался недоволен и попросил сфокусироваться на business priorities.

Мы за все хорошее

То, что кажется разными проблемами, на самом деле является одной и той же. И бизнес, и QA хотят, чтобы как можно меньше значимых юзеров встретили баги. Бизнес заинтересован развивать продукт с наименьшими затратами и как можно быстрее. Девелопмент заинтересован иметь качественный codebase, который легко поддерживать и куда можно наращивать фичи без опасения наткнуться на хрупкий, загадочный и немасштабируемый legacy. Что соответствует бизнес-целям тоже:

  • меньше значимых юзеров — значит, что баг становится проблемой, когда касается большого процента target audience (в основном это те, кто приносит прибыль или потенциально может ее принести);
  • наименьшими затратами = легко поддерживать = наращивать фичи, только речь идет не об абстрактной фиче, а о том, что уйдет в работу в ближайшее время.

На примере одного беклога

Давайте посмотрим на практике (баги техническим долгом не считаются, но часто фигурируют где-то рядом, поэтому глянем и на них). В беклоге лежит:

IssueDescription
update PrettyCharts libraryЛиба, которую используют для отрисовки графиков, устарела на две версии.
an error when trying to setup custom themeУ всех юзеров, которые пытаются настроить кастомный UI, прилетает 500 с сервера, и настройки не сохраняются.
payment fails when upgrading with AmEx cardНе проходит апгрейд, если карточка — AmEx.
apply new sorting logic to the old moduleВ новых модулях сделали элегантную сортировку, а в старом оставили изначальное корявое решение, сделанное наспех.
duplicate folders theme and theme_goldenКогда-то тема в приложении была одна, потом добавились другие темы, и дефолтную переименовали в Golden. В коде часть файлов от нее осталась лежать в папке theme, часть перенеслась в theme_golden.

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

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

Добавляем к нашим айтемам контекст и описываем, почему это проблема.

IssueDescriptionContext
update PrettyCharts libraryЛиба, которую используют для отрисовки графиков, устарела на две версии.В последней версии либы лучше производительность и есть два новых типа графиков.
Если нужен будет экспорт ежедневных отчетов, старая либа с ним не справится.
an error when trying to setup custom themeУ всех юзеров, которые пытаются настроить кастомный UI, прилетает 500 с сервера, и настройки не сохраняются.Фича доступна только платным юзерам. По статистике, из платных юзеров 10% пробуют ставить кастомный UI.
payment fails when upgrading with AmEx cardНе проходит апгрейд, если карточка — AmEx.Применимо для платных юзеров, чьи подписки созданы до 2019 года. Таких 1%.
apply new sorting logic to the old moduleВ новых модулях сделали элегантную сортировку, а в старом модуле оставили изначальное корявое решение, сделанное наспех.В старом модуле сортировка работает очень медленно, если записей больше 5000.
Добавить сортировку по еще одному столбцу очень сложно.
duplicate folders theme and theme_goldenКогда-то тема в приложении была одна, потом добавились другие темы, и дефолтную переименовали в Golden. В коде часть файлов от нее осталась лежать в папке theme, часть перенеслась в theme_golden.В эти папки приходится заглядывать в среднем два раза в неделю каждому фронтэндщику, и уходит лишних 20 минут, потому что непонятно, что где искать.

Дальше узнаем, что по роадмапе нужно делать:

  • onboarding for corporate clients;
  • generate annual reports in the old module, use CoolAPI.

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

IssueDescriptionContextDecision
update PrettyCharts libraryЛиба, которую используют для отрисовки графиков, устарела на две версии.В последней версии либы лучше производительность и есть два новых типа графиков.
Если нужен будет экспорт ежедневных отчетов, старая либа с ним не справится.
С текущей производительностью проблем нет, новые типы графиков и ежедневные отчеты пока не нужны. Старая либа не будет ни для кого головной болью. (Конечно, если апдейт — дело получаса, он просто берется и делается, но, допустим, это не наш случай)
an error when trying to setup custom themeУ всех юзеров, которые пытаются настроить кастомный UI, прилетает 500 с сервера, и настройки не сохраняются.Фича доступна только платным юзерам. По статистике, из платных юзеров 10% пробуют ставить кастомный UI.10% платных юзеров — повод задуматься, но это не core feature, юзерам работать баг не мешает, продукт они не забросят, разочарованные в саппорт писать не будут, и QA тоже может быть спокоен. Однако в роадмапе есть onboarding for corporate clients, а все корпоративные клиенты будут использовать кастомные темы, поэтому нужно чинить.
payment fails when upgrading with AmEx cardНе проходит апгрейд, если карточка — AmEx.Применимо для платных юзеров, чьи подписки созданы до 2019 года. Таких 1%.Очень маленькая вероятность, что кто-то попадет на этот баг, и даже если это произойдет, саппорт может сделать апгрейд вручную. Можно оставлять как есть.
apply new sorting logic to the old moduleВ новых модулях сделали элегантную сортировку, а в старом модуле оставили изначальное корявое решение, сделанное наспех.В старом модуле сортировка работает очень медленно, если записей больше 5000.
Добавить сортировку по еще одному столбцу очень сложно.
Максимальное количество записей у текущих пользователей — 3000, добавления сортировки по еще одному полю в планах нет. Фича generate annual reports с сортировкой не связана. Значит, с проблемой тоже никто не столкнется.
duplicate folders theme and theme_goldenКогда-то тема в приложении была одна, потом добавились другие темы, и дефолтную переименовали в Golden. В коде часть файлов от нее осталась лежать в папке theme, часть перенеслась в theme_golden.В эти папки приходится заглядывать в среднем два раза в неделю каждому фронтэндщику, и уходит лишних 20 минут, потому что непонятно, что где искать.С проблемой сталкиваются регулярно, поэтому здесь можно явно выиграть в скорости и поддерживаемости (если наведение порядка не потребует месяца, конечно).

По закрытии этих айтемов бизнес получает свой профит, потому что починили критический баг для corporate clients onboarding и сэкономили 5 часов девелопмента ежемесячно.

Контекст для рефакторинга/багфикса

При обсуждении важности айтемов спрашивайте себя (и других):

  • каких компонентов/фичей касается;
  • какую конкретно проблему создает текущая реализация;
  • какое преимущество дает переделка (например, будет легче вносить изменения, или можно будет переиспользовать код, или вынести в отдельный компонент, и тогда добавление новой такой же сущности будет занимать минуту);
  • планируется ли в ближайшее время разработка в этих компонентах;
  • какого сегмента пользователей коснется (скажем, нужна оптимизация под мобильные устройства, а с мобильных сидят только пользователи из региона Х, и они не являются target audience);
  • для багов — как часто встречается, для какого use case, есть ли workaround.

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

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

«Продаем» рефакторинг

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

Например, «продадим» апдейт фреймворка, который займет существенное время (если апдейт быстрый, то он обычно делается без вопросов):

А. We need to update the framework because the longer we use the old one, the harder it will be to update it in the future. Ценности для бизнеса нет.

В. We need to update the framework. The new version has the methods to receive data in X format and this will allow us to sent instant messages to the customers. Also the performance of pdf exports will improve by 20%. Если бизнесу важны мгновенные сообщения и скорость экспорта, то апдейт фреймворка у вас «купят».

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

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

TL;DR

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

  • Само наличие legacy или багов не страшно: важно понимать, что из этого является реальной проблемой на данный момент.
  • Понимая бизнес-приоритеты и направление развития продукта, можно «продать» нужный рефакторинг/багфикс, от чего выиграют обе стороны. Бизнес получит быструю разработку, девелопмент — легко поддерживаемый продукт, а ключевые кастомеры будут иметь smooth experience.
  • Возвращайтесь к тому, что именно улучшится предлагаемым изменением. Важно ли это?
  • Если вы владелец бизнеса, делитесь своими планами с командой как можно чаще. Это поможет принимать более подходящие архитектурные решения и избежать накопления технического долга, а при его наличии — выплатить самый критический.
  • Если вы менеджер, добывайте и делитесь информацией о планах и о контексте рефакторинга/багфикса, но сначала сами убедитесь, что поняли, о чем речь =)
  • Если вы девелопер, спрашивайте о том, что сейчас важно продукту, помогайте сделать технический долг видимым и облегчайте понимание правильного контекста.
LinkedIn

41 комментарий

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

Согласен с автором, что анализ технического долга и реализация наиболее приоритетных задолженностей является важной составляющей развития продукта.
Мы у себя в компании к концепции технического долга подошли шире — совокупное несоответствие продукта техническим требованиям и бизнес целям, негативно влияющее на результаты бизнеса. Мы рассматриваем его в контексте 8 наиболее приоритетных на наш взгляд параметров: Source code quality; Usability, UI & Documentation; Security; Performance; Business logic; Architecture quality; Data quality; Open Source code use. Эти измерения стали основой нашей платформы оценки качества программных продуктов, которую мы назвали TETRA (Technical Debt Reduction Platform).
Данный подход позволяет выполнить всесторонний анализ продукта, найти его слабые стороны, запланировать критические изменения на ранней стадии, что в итоге зачастую упрощает развитие продукта. Конечной целью TETRA является количественная оценка состояния продукта для достижения лучшего результата, так как без всестороннего измерения невозможно понять качество и контролировать его.

Как Вы количественно оцениваете

Architecture quality

?)

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

Тесты программные (юнит-тесты), или как в психологических опросниках?

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

excellent

Хто може визначити це «гарний код» або «не дуже», або потребує «перепиши з нуля»? Це питання складне, але я для себе особисто можу сказати так: хороший код — це такий, який готовий для розробки юніт-тестів для нього. Це звичайно, ніяк не перевірити, доки їх не почнеш писати. Так що... якщо навіть вам здається що код у вас «окей», це тільки здається. Тести написані ? Покриття коду ? Якщо ні — про надійність такої розробки можна навіть не говорити — все легко руйнується, а дізнаєтесь потім, коли у замовника на сервері щось здохне.

Ну от в нас нема юніт тестів. І автотести покривають дуже трохи.
Але повно асертів + мануальщик.
В продакшн вийшли з досить стабільною версією.
Ідея в тому, щоб замість постійної витрати часу девелоперами на розробку юніт тестів та їх постійні зміни, коли міняється функціонал, найняти тестувальника.
Правда, в нас макрорелізи, між якими можна довго тестувати й фіксити.

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

Та ви такий не один, у мене на великому проекті ситуація аналогічна — тести є, але їх явно не достатньо. Мануал-тестер це добре, але згодом він почне нудьгувати «Що... ще 10й раз проходити отой тест план, та це мені роботи на тиждень...а ви не можете автоматизувати?» — і знаєте, він правий... Якщо щось можна автоматизувати — так і треба. А людина — це для того щоб сказати наприклад «Тут не зручно, тут треба інакше» , для порад.

Тут сумісність з чужим залізом, і був хороший шанс потрапити в ситуації (і потрапляли частенько), коли ми усе робимо за стандартом, а залізки не працюють. Тому треба очима дивитись, що відбувається.
Та й не було ресурсів на тести.
Тестувальнику, звичайно, сумно — але коли в нього натхнення, то щось таке зробить, що котрийсь ассерт зловиться. А може — то карма. Тести такого збочення не придумають.

Ну от в нас нема юніт тестів. І автотести покривають дуже трохи.

"Та ну нафиг такие ужасы" сказал Стивен Кинг и закрыл эту страницу.

А вот у меня без юнит-тестов писать не получается, а вот ассерты и т.п. не люблю очень.

Юнит-тесты хороши для алгоритмов, когда небольшой кусок кода, и он не будет меняться каждую неделю. При этом результат сильно странно зависит от входных данных.
Я вот жалел, что не написал юнит-тестов на загрузку и сортировку телефонной книги, истории звонков и на регексп.
А вот когда в коде куча логики if-else-пойдитуда — и оно постоянно меняется — тут юнит тесты или на каждый чих переписывать, или не будет нормального покрытия, потому что новая бизнес-логика выползет из-под старых тестов.

когда продукт без хозяина

Мені подобається правило бойскаута — якщо ти міняєш якийсь код, то після цих змін він має стати краще, ніж до. Дозволяє поступово позбуватися технічного боргу без окремих рефакторинг сторі, які важко продавати.

Воно діє, допоки не прийшов дедлайн (в телеграмі така іконка є в сеті dark souls)

Воно діє завжди. Не варто погоджуватись на дедлайни, які не дозволяють робити роботу нормально.

Є бізнес.
Бізнес платить гроші.
Програмісти за ці гроші роблять те, що хоче бізнес.

Код заради коду нафіг не потрібен нікому.
Навіть якщо він красивий та чистий.
І рефакторинг заради краси теж не надає користувачам чи замовникам жодної цінності — навпаки, збільшує імовірність багів.
І красивий код на мертвому проекті нічого не вартий.
А некрасивий код на живому проекті коштує суму, порівняну з ціною самого проекта.

І як раз бізнес займається тим, щоб проект був живим, і продавався краще за конкурентів.
І бізнес знає, коли йому робити релізи чи дедлайни, і чого буде коштувати затримка.

Коли бізнес ставить неадекватні дедлайни, треба погоджуватись?

Треба сказати, що в результаті буде. Якщо маєте зв’язок з бізнесом.
Одна справа — неможливий дедлайн.
Інша справа — коли код буде некрасивим, але фічу зробить можна.

А ще можна зробити фічу чуть за довше, але нормально. І якщо так буде робити постійно вся команда, то буде і код нормальний, і фічі заделіверені.

Тут питання не про «чуть», а про те, закостиляти фічу чи рефакторить архітектуру модуля.

В статье много воды, a tl;Dr содержит выводы вместо краткого изложения основной мысли.

и когда им в очередной раз отказывают выделить время на рефакторинг
мы выделили один спринт под рефакторинг, смогли согласовать с клиентом

Це про аутсорс?

я с такими ситуациями сталкивалась и в аутсорсе, и в продукте

Мне на мелких проектах помогал lazy refactoring — откладываем до тех пор, пока откладывается. В результате 2-3 рефакторинга могут смержиться в один, потому что степень протухания можно достичь большую. Когда видишь, что плохо — говоришь заказчику «следующее изменение этой функциональности займет много времени, потому что сильно часто меняли, там каша, и надо переписать». Прилетает такая задача — уже проблемы заказчика, выделять кучу времени на рефакторинг модуля в рамках мелкой фичи (когда это время есть) или замораживать модуль и откладывать фичу на потом.

Возможно, это специфика эмбедеда — заказчики обычно сами технари, и небалованые.

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

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

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

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

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

Да, видел
Хороший код можно и сразу написать. Многие об этом просто не знают, и думают что первое что пришло в голову — самое быстрое решение

Я видел изменения требований, которые ставят все с головы на ноги. И какой бы код тут ни был — ему не поздоровится, если, конечно, с самого начала не угадал, или не вычитал какой-то редкий контринтуитивный паттерн.
Пример из жизни:
Начальные требования: вот есть звонок, у него есть инициатор и адресат. Адресату показывается номер звонящего от инициатора.
Архитектура: делаем класс звонка с полем «номер звонящего» и переменной, которая показывает стадию жизненного цикла (набор-рингтон-соединение-разговор-занято-умерли).
Обновление требований: если при разговоре звонок был поставлен на удержание, и потом пользователь положил трубку, нужно перезвонить этому пользователю, так как он мог забыть про звонок на удержании, либо ему проще положить трубку, чем набирать код возврата к старому звонку.
Упс. Архитектура нафиг, потому что теперь стадия жизненного цикла из «разговор» откатывается в «набор», номер звонящего, который у нас есть, мы не можем показать, так как это может быть наш собственный номер (исходящий вызов на удержании положили), и еще всякая фигня.
И че делать? костылять или переписывать всю систему, с учетом новых требований? перепишешь — еще раз что-то веселое придумают.

Переписывать и брать за это деньги. Тут костыли не помогут

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

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

Надо сразу предупреждать, что +время на перепил (сами виноваты что сразу требования не сформулировали) или работать не будет.

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

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

откладываем до тех пор, пока откладывается

— прям как макконнелл говорил в совершенном коде

ой, давно было, уже все повылетало...

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