Технический долг — тихий убийца

568f473e3ce1ed96acb97c994d902598.jpg

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

Определимся с терминами

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

Что вызывает технический долг?

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

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

Развитие aka Обновление

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

Пример: Банк отказывается от монолитной CRM системы, западного вендора и переходит на микросервисную архитектуру собственной разработки. Бизнес-процессы переводятся по очередно, отключая старые интеграции. Клиентские данные, по правилам безопасности хранятся в другой legacy-системе, которая отдает ID другим сервисам. Каждая интеграция имеет свои особенности и плохо описана, таким образом корректно заложить объем работ и срок реализации не представляется возможным. В рамках pipeline проекта, эта особенность сильно увеличивает технический долг, так как команды, запускающие новые интеграции, фактически идут по минному полю.

Отсутствие Definition of Ready (DoR) и Definition of Done (DoD)

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

Оба этих определения играют важную роль, помогают обеспечить чёткое понимание задач и ожиданий по их выполнению. Если DoR и DoD не определены или не достаточно четко согласованы с принимающей разработку стороной, риск появления Технического долга существенно вырастает. В своей карьере я сталкивался со случаями, когда разработка начиналась до начала проектирования, а требования менялись и дополнялись в процессе разработки. Если не настаивать на формализации требований и прописывании четких критериев приемки, скорее всего, то что логику, которая была заложена в начале проекта, придется перепиливать к его концу. Конечно, бизнес всегда давит и требует максимальной скорости разработки, в современном мире скорость изменений крайне важна, но нужно стараться находить компромисс между быстрым выводом в ПРОМ новых функциональностей и сохранением качества.

Отсутствие или недостаточное покрытие тестами

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

Для минимизации рисков, необходимо закладывать ресурсы и проводить комплексное тестирование, включая:

  • Функциональное тестирование — проверка соответствия функциональности программы её требованиям и спецификациям.

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

  • Юзабилити-тестирование (тестирование удобства использования) — оценка удобства интерфейса и лёгкости использования программы конечными пользователями.

  • Интеграционное тестирование — проверка взаимодействия между различными модулями или компонентами программы.

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

  • Регрессионное тестирование — проверка того, что внесение изменений в одну часть программы не приводит к ошибкам в других её частях.

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

Недостаток коммуникации

В крупных компаниях, разработка даже одного модуля или функции проводится несколькими командами одновременно. Для совместной работы команд даже созданы специализированные фреймворки, которые помогают координировать усилия множества участников, обеспечивать согласованность целей и процессов, а также поддерживать эффективное взаимодействие между различными подразделениями организации: SAFe (Scaled Agile Framework), LeSS (Large-Scale Scrum), DAD (Disciplined Agile Delivery). Когда коммуникация разрозненна, команды сосредоточены исключительно на своих задачах и ветвях разработки, сильно вырастает риск, что кто-то из вне (из другой команды) «идет своим путем» и делает ту же функцию, либо интегрируется с тобой через другой стек / технологию / архитектуру.

Пример: В ходе реализации продукта (модуль колл-центра), команде потребовалось переносить и хранить данные по мониторингу и аналитике звонков в реплике базы данных и далее использовать их при мониторинге. Была заведена заявка по стандартному шаблону и команда управления репликами реализовала поток данных по TFS — метод организации и хранения данных в формате, который оптимизирован для быстрого доступа и анализа. После того, как доделали выгрузку и запустили поток, выяснилось, что данный поток по методу TFS не предусматривает вычитку данных раз в сутки, а это не подходит для системы мониторинга, так как доступ к данным требуется с периодичностью каждые 30 минут. Потребовалось проектировать и строить новый поток NRT (Near Real-Time), а также вносить изменения в интеграционный шлюз.

Отложенный рефакторинг и аутсорсинг разработка

Цель рефакторинга — улучшить структуры кода без изменения его желаемого поведения. Требования к разработке меняются, написанный ранее код может устареть, либо его трудно будет обновить и работать с ним. Чем дальше откладывается рефакторинг, тем больше становится технологический долг. Никому не хочется разбираться со старым кодом, нужно тратить много усилий и времени разбираясь в работе, которую делал другой специалист. Я участвовал в проекте международного автопроизводителя, на котором внедрялась ERP-система на базе SAP. На ранней стадии проекта, многие функциональности разрабатывали индийская TATA Group. Часть бизнес-процессов, которые были заложены в функциональность системы и которые можно было реализовать просто настройками, были сделаны через hard-code (опустим причины, в этом контексте они нас мало интересуют). Индийские коллеги писали код по своим правилам, рефакторить код пришлось на поздних стадиях уже российской команде и это вызвало сильный негатив и потери времени, которое изначально не закладывалось.

Изменения на поздних этапах

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

Типы технического долга

Я нашел большое разнообразие типов и определений технического долга. на основе перечисленных выше причин и собранной информации, я понял, что можно классифицировать технический долг по 3 м основным категориям: преднамеренный, непреднамеренный, и вызванный внешними факторами.

Преднамеренный

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

Непреднамеренный

Факторы, о которых мы не знаем, вследствие недостатка информации, экспертизы, коммуникаций, тестирования и проверок. Увеличивается сложность проекта, добавляются комплексные взаимодействия, появляются случайные ошибки. Главная опасность этого вида технического долга в его отложенном эффекте, проблемы могут долго не проявлять себя и появится (по закону Мерфи) в самый неподходящий момент.

Внешние факторы

Вызван внешними факторами. Например, обновлением операционной системы или сторонних API, в результате чего что-то ломается или работает неправильно. Даже если мы сделали все правильно, предусмотрели все возможные и известные нам риски, мы не можем на 100% застраховаться от внешнего влияния.

Плох ли Технический долг и как им управлять?

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

Как работают с техническим долгом на разных проектах?

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

Остаточный принцип

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

Начались проблемы

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

Выделенное время

Такой подход применяется очень часто, суть его в том, чтобы выделять время команды (обычно 20% от задач на бизнес-разработку) на работу с задачами из технического долга. Проблема этого подхода в том, что для заказчиков это время не видно, они не понимают, почему такой маленький прогресс по бизнес-задачам («Ведь вы же закладывали 15 md, когда закончите?!»). Требуется объяснять технические детали, что также отнимает время и ресурсы. В одном из банков, на проекте которого я работал, официально накидывали 20% в ресурсы при оценке любых работ.

И что же делать?

Я опишу свое видение этого вопроса. Я предлагаю подходить к нему комплексно.

Первое, чтобы чем-то управлять, это нужно найти и измерить. Нужны метрики. Но на что смотреть? Набор и наполнение параметров сильно зависит от специфики проекта. Не нужно что-то собирать, просто для того чтобы собирать, этими параметрами нужно пользоваться, они должны помогать. Все метрики, необходимо обсудить с командой и явно зафиксировать их. На мой взгляд основными могут быть: процент покрытия кода тестами, bug rate — количество ошибок, cycle time — время перехода задачи от начала до завершения, velocity — количество выполненных задач за спринт. Собирая и контролируя метрики, можно например снижать количество багов, введя дополнительные проверки, таким образом высвобождая время команды, в которое можно взять задачу из техдолга.

Втрое это документация и ее согласование. Написание документации важный процесс и она должна быть хотя бы в минимальном виде. Всегда стараюсь в явном виде прописывать 2 пункта в Definition of Done: задача покрыта тестами, написана документация.

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

Заключение

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

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

© Habrahabr.ru