После реализации первого этапа улучшений в компании и положительной обратной связи от заказчика, Дмитрий перешёл к осуществлению глобального плана: работе с техническими долгами. Перед командой проекта стояли следующие задачи:
- Внедрение автоматизированного тестирования (Unit-, UI- и интеграционного тестирования).
- Внедрение непрерывной интеграции и поставки (CI/CD).
- Рефакторинг legacy-кода.
- Улучшение архитектуры продукта.
Так как система имела сложную архитектуру и нетипичный стек технологий, задача по внедрению CI/CD и автоматизации тестирования была действительно нетривиальной и требующей исследований. Продукт представлял собой десктопное решение с установленной аппаратно-программной защитой, а также защиту через удалённые сервера. При запуске автотестов напрямую постоянно требовалось наличие USB-ключа защиты, поэтому было необходимо найти и применить подходящий фреймворк UI-автотестирования. Осложняло ситуацию и то, что ранее инженеры не имели опыта работы с UI-автотестами. В команде установилось высокое сопротивление к их написанию: у сотрудников не было мотивации работать над сложной, абсолютно новой для них задачей. Поэтому было принято решение начать ежедневные исследования (1 час в день) по автоматизации тестирования и внедрению CI/CD и постоянно отслеживать прогресс. Такой подход постепенно приучил инженеров к работе с техническими долгами и позволил более эффективно проводить исследование: не зацикливаясь на одной задаче, решение удавалось находить быстрее. Кроме того, теперь инженеры не могли отказаться от работы по техническому долгу под предлогом наличия более приоритетных задач.
Спустя несколько месяца с начала работы над техническими долгами удалось добиться значительных улучшений. Для автоматизированного UI-тестирования критических функций продукта был успешно применён выбранный по результатам исследования фреймворк. Инженеры начали регулярно писать UI и Unit-тесты. В Unit-тестировании использован подход Data Driven Testing, упрощающий ввод входных данных, что особенно важно для системы с большим количеством расчетных модулей.
Несмотря на высокое сопротивление команды, были внедрены непрерывная интеграция и поставка (CI/CD). Спустя 2 месяца данная практика начала приносить первые результаты: процесс создания дистрибутивов продукта стал быстрым и безопасным, что позволило существенно высвободить время и увеличить производительность команды. Постоянная работа с техническим долгом стала неотъемлемой частью спринтовых задач.
Одним из важных решений для системного улучшения продукта стало введение архитектурных часов. Еженедельно в команде разработки проводились встречи, на которых обсуждалась стратегия развития продукта с учётом его актуального технического состояния. В рамках архитектурных часов также было инициировано обсуждение по стратегическому рефакторингу. По результатам таких мероприятий технический директор (СТО) составлял актуальный план, описывающий, каким образом будет выполняться рефакторинг кода в контексте общей стратегии развития продукта.
В рамках рефакторинга инженеры начали работу по изолированию модулей кода, покрыв тестами входные и выходные данные. Так только за первый год в фоновом режиме была изолирована треть от нескольких десятков основных модулей системы. Данная практика позволила достичь значительного прогресса в борьбе с высокой связностью кода и позволила минимизировать влияние модулей друг на друга.