Каждый раз, когда мы выкатываем обновление платформы быстрой разработки корпоративных приложений Jmix, мы популярно рассказываем о том, что изменилось и как перейти на новую версию. Несмотря на доступность технических материалов и удобных инструментов, аспект накопления технического долга как был, так и остается сильно недоинвестированным со стороны команд разработки. Проценты капают, а через пару лет вопросы к нам — «Почему так сложно на новую версию переходить!? Что‑то вы тут недоработали».
Мы разрабатываем Jmix уже более 10 лет, и за это время видели много случаев игнорирования командами работы с техническим долгом. Предлагаем вашему вниманию статью, которая поможет диагностировать операции по работе с техническим долгом и понять, где образовались разрывы. Не благодарите - просто скачайте Jmix и попробуйте ;-)

Введение
«Технический долг» — это метафора, введенная в оборот Уордом Каннингемом для обозначения предполагаемой стоимости будущих доработок, вызванных «быстрым и грязным» подходом к написанию кода (Технический долг — Википедия).
В своей классической форме понятие «технический долг» относится к «не очень правильному коду, который мы исправим когда‑нибудь потом» — то есть, к срезанию углов с целью ускорить разработку сегодня за счет дополнительных усилий, которые придется применить в будущем (Technical Debt: From Metaphor to Theory and Practice). Ранние дискуссии на эту тему (например, инициированные Каннингемом в 1992-м году и более поздние на базе Института программной инженерии) фокусировались на проблемах в коде: неряшливый, чрезмерно сложный код, который «накапливает задолженность», затрудняя последующие изменения (Статья Мартина Фоулера о техническом долге).
Однако, ограничивать определение технического долга плохим кодом — это очень узкий подход. В реальном мире IT системы накапливают долг в разных формах, не только в исходном коде.
Архитектура может утратить гибкость, инфраструктура может состариться, тесты могут пропускаться — и все это будет замедлять будущую разработку точно так же, как и неопрятный код. По факту, многие эксперты утверждают, что любой долгоживущий программный комплекс со временем накапливает определенный долг. При этом долг на уровне кода (хотя его проще обнаружить) на самом деле является лишь частью общей картины. (Kruchten-20).
Цель этой статьи состоит в том, чтобы расширить понимание технического долга за пределы качества кода, рассмотрев разные виды долга, которые могут возникнуть в IT компании, и разобравшись в том, какой вклад они вносят в типичный набор проблем, с которыми сталкивается компания, занимающаяся разработкой ПО.

Обзор понятия долга в IT системах
Когда мы говорим о долге в IT, мы должны принимать во внимание не только неопрятный код. Весь IT стэк — от требований, архитектуры и инфраструктуры до тестирования и непосредственной работы — может аккумулировать «долг», который ложится бременем на будущую работу. Так же, как финансовый долг, который может иметь разные формы, IT долг простирается на несколько сфер. Например, устаревшие серверные платформы или непропатченные библиотеки формируют инфраструктурный долг, а отсутствие автоматизации деплоймента является операционным долгом. И на любой вид подобного долга накапливаются «проценты», что означает, что использование, поддержка и развитие системы со временем становится все медленнее и рискованнее.
Полезная аналогия: сравнить формы технического долга с типами финансового долга. Некоторые виды технического долга похожи на долг по кредитной карте — небольшие срезания углов и заметания под коврик, (скажем, захардкоженные фиксы или пропуск тестов), который дают сиюминутную выгоду, но приводят к накоплению высоких процентов, если не выплатить их немедленно.
Другие долги больше похожи на долговременный кредит или ипотеку — осознанный компромисс, такой как выбор более простой архитектуры, чтобы вписаться в дедлайн, при этом вы понимаете, что в дальнейшем придется много инвестировать в масштабирование.
В обоих случаях сравнение с финансовым долгом работает: вы либо выплачиваете его сейчас (и правильно), либо платите больше в дальнейшем.
Главное здесь — понять, что технический долг выходит за рамки кода, он как минимум включает требования, архитектуру, инфраструктуру, процессы, тестирование, безопасность и операции, и все это можно принести в жертву на короткий срок, но на долгую перспективу это приведет к большим затратам или усилиям.
В отличие от реальной жизни, вы можете выбросить весь программный комплекс целиком, и тогда ваш долг исчезнет, целиком или почти целиком. Жаль, что этот принцип не работает с вашим банком и с только что купленной квартирой.
Категории долга в IT системах
Давайте разобьем технический и другие виды долга в IT системах на категории.

Долг по требованиям
Определение: Долг по требованиям менее очевидный, но эта категория технического долга крайне важна. Такой долг возникает на почве недоработок, допущенных при сборе требований или заключенных в самих требованиях. Такой долг часто приводит к несоответствию между тем, что нужно сторонам и тем, что команда(ы) разработчиков создают на самом деле. (см. Why Product Owners Must Prioritize Managing Technical Debt?).
Это может произойти, когда требования неполны, нечетко сформулированы или слишком часто меняются (например, в случае изменения направления продукта или стремительного расширения сферы применения). Если вы слишком быстро проскочите стадию выявления требований — например, чтобы быстро выпустить минимальный жизнеспособный продукт — вы можете разработать функциональность, которая не вполне соответствует потребностям реального пользователя или не принимает во внимание будущее развитие продукта.
Эти «пробелы» или дефицит требований превращаются в долг. В дальнейшем вам придется переделывать или приспосабливать продукт (иногда весьма значительно), чтобы привести его в соответствие с реальными требованиями.
Классические примеры долга по требованиям следующие:
Отсутствуют требования по качеству (производительность, номинальная нагрузка и т. д., например, время ответа < 200мс, см. Q42, чтобы посмотреть больше примеров).
Описания особых случаев отсутствуют или неполны: когда ваш сценарий использования описывает только счастливый путь (happy path), но пропускает пограничные или особые случаи.
Отсутствует мнение всех заинтересованных сторон: у некоторых людей
иили организаций просто никто не спросил об их потребностях в рамках рассматриваемой системы.Неполная информация, часть отсутствует совсем или сосредоточена только на одном домене.
Ожидания заинтересованных сторон противоречат друг другу (одному человеку необходим высокий уровень безопасности со строгими легальными процедурами получения разрешений, другие требуют высокой скорости доставки)
Мой персональный наихудший случай долга по требованиям — это отсутствие «общего видения», что также называется «северной звездой» или «желаемым результатом»: когда команда разработчиков теряется в море мелких требований, не понимая общей цели или общего видения проекта, разработчики больше не видят леса за деревьями.
Долг по коду и архитектурному дизайну
Определение: Долг по коду и архитектурному дизайну — это классическая форма технического долга. Это понятие относится к плохому коду и срезанию углов при реализации проекта, например, к так называемому спагетти‑коду, отсутствию модульности, дублированию логики или чрезмерно связанным компонентам. Такие конструкции приводят к тому, что кодовую базу становится трудно понимать и поддерживать.
Эти проблемы часто возникают из‑за того, что реализация функционала осуществляется второпях, а общепринятые лучшие практики обходятся стороной. Со временем внутреннее захламление кода (Мартин Фаулер ввел термин «cruft», что значит хлам, захламленность) накапливается, повышая количество усилий, необходимых для добавления или изменения функциональности. (См. пост Мартина про технический долг).
По существу, дополнительное время, которое разработчики тратят на работу с плохим кодом, и есть те самые проценты на долг по коду. Например, если добавление новой возможности заняло два дня на чистом коде и четыре дня на захламленном, эти лишние два дня и являются процентами, которые мы платим за то, что не сделали рефакторинг раньше.
Оригинальная метафора Уорда Каннингема брала начало от этой идеи:
«Поставка первого временного кода (имеется в виду максимально быстро написанный неопрятный код — прим. пер.) — это как взятие денег в долг … Опасность возникает, когда долг не отдается вовремя». Это приводит к сложному проценту в форме медленных, болезненных исправлений в будущем. (Википедия).
Я в достаточной степени уверен, что большинство из вас/нас бывали в такой ситуации. Многие (если не все) легаси системы содержат многочисленные формы такого долга по коду.
Представьте себе приложение, которому десятки лет от роду, в котором тысячи костылей и «временных» фиксов. Небольшие изменения в одном модуле неожиданно ломают функциональность в другом по причине сильной связности. Разработчики боятся трогать определенные «хрупкие» части кода. Такая хрупкость и нежелание разработчиков трогать определенные места в кодовой базе может огорчать менеджмент.
Иллюстрацией из реального мира является вездесущий «большой ком грязи», то есть легаси код, с которым борются многие компании. Например, ранние версии браузера Netscape пришлось существенно переписывать, поскольку код стал слишком запутанным для поддержки.

Архитектурный или структурный долг
Определение: архитектурный долг (или структурный долг) относится к слишком сильной или неоправданной связанности (coupling) или избыточным зависимостям, отсутствию согласованности (cohesion) или модульности, а также другим структурным недостаткам.
Это эквивалент долга по коду, но на более глобальном уровне. Структурный долг накапливается, когда команды разработчиков игнорируют разумные архитектурные принципы ради быстрой выгоды – например, монолитный дизайн с сильной связностью, который работает для малых приложений, но не позволяет масштабироваться, или архитектура для микросервисов с оверинжинирингом, которая становится кошмарным сном при поддержке.
В отличие от долга по коду, архитектурный долг часто бывает довольно сложно обнаружить с использованием статических аналитических инструментов на уровне кода; при этом, он может иметь более дорогостоящие последствия. (См. статью Филиппа Крухтена Technical Debt: From Metaphor to Theory and Practice)
Филипп Крухтен отмечает, что хотя долг на уровне кода легче обнаружить, архитектурный долг часто несет в себе более высокую стоимость в долгой перспективе (Kruchten-2020), потому что он проникает во все компоненты системы (имеет глобальный скоуп), и его рефакторинг после внедрения приложения стоит дороже.
Анализируя зависимости в коде, можно обнаружить определенные типы таких структурных недостатков (например, можно поискать циркулярные зависимости). Однако не следует забывать и о том, что зависимости могут принимать множество (!) различных форм:
Прямые зависимости времени компиляции, когда одна функция или сервис вызывает другую функцию или сервис. Такие зависимости обычно видны в исходном коде.
Зависимости рантайма, инжектированные зависимости. В какой‑то момент считалось хорошей практикой заменять зависимости времени компиляции на зависимости рантайма, однако такие зависимости может быть труднее обнаружить.
Зависимости рантайма типа «другая программа должна отработать первой» и другая джоба должна завершить работу, прежде чем нашей будет разрешено стартовать.
Зависимости, разрешаемые в рантайме с помощью посредников (брокеры, нейм‑сервера, реестр, файлы конфигураций или даже переменные окружения).
Зависимости с невидимым вызовом, такие как ваши другие программы, вызывающие ваш публичный API.
Зависимости общей инфраструктуры, такие как общая база данных, общая оперативная/дисковая память, а также другие типы инфраструктуры, которую делят между собой системы, независимые во всем остальном.
Организационные зависимости по типу «определенный человек должен подписать разрешение на изменение или смержить публичный релиз».
Другой формой структурного долга являются неправильные или неподходящие модели данных: модели, которые не соответствуют текущим требованиям, имена таблиц или колонок, не соответствующие их содержимому (Я помню имена колонок вроде col-1 и до col-10 в базе данных на продакшен).
Давайте рассмотрим несколько примеров архитектурного долга из реальной жизни, которые привели к полномасштабному краху в бизнесе.
Friendster, одна из первых социальных сетей, была основана в 2002-м году и за несколько месяцев выросла до 3-х миллионов пользователей. Она была построена как монолитное PHP приложение с ограниченными механизмами по масштабированию. Монолитный бекенд не смог справиться с ростом пользовательской базы, архитектура базы данных на смогла масштабироваться под приток пользователей. Friendster попыталась осуществить полную переработку архитектуры, но это заняло слишком много времени. К тому времени, как все было готово, критическая масса была потеряна.
«Friendster обрушилась под весом своей собственной архитектуры… Они не строили под масштабирование, и их код и база данных не смогли справиться с таким успехом.» — Джон Адамс, бывший SRE инженер, Facebook
Долг по базе знаний
Определение: Долг по базе знаний относится к недостаточно хорошо распространяемой, передаваемой или хранимой базе знаний. Другое его название — недостаточная или устаревшая документация.
Ходят слухи, что некоторые IT проекты не имеют адекватной документации. По контрасту с ними, во многих других инженерных дисциплинах (например, механике, электрике или гражданской инженерии) принято полностью полагаться на структурированную и стандартизованную документацию.

В своей наихудшей форме долг по базе знаний означает, что единственные люди, обладавшие познаниями в определенных аспектах системы, таинственно исчезли и больше не отзываются. Другая форма, довольно беспощадно называемая «truck factor of one» (фактор грузовика равен единице), означает, что только один человек может выполнять определенные задачи или знает некоторые части кода. Если этот человек уйдет (или, как следует из названия, его переедет грузовик), разработка или использование системы могут мгновенно остановиться.
Третья форма долга по базе знаний — это чрезмерная и устаревшая документация. Это может случиться в крупной компании, где много бюрократии, особенно когда цели и преимущества наличия документации, коммуникаций или передачи знаний непонятны сотрудникам и нигде четко не описаны.
Другие примеры этого вида долга следующие:
Отсутствуют гайды и туториалы для использования при онбординге.
Никаких «посмертных» или ретроспективных записей: после инцидентов или существенных изменений не создается никаких записей о том, что пошло не так, как удалось исправить проблемы и какие выводы были сделаны из случившегося.
Заметка на полях: я являюсь соавтором и ответственным за поддержку arc42, open‑source фреймворка для эффективных и прагматичных коммуникаций по архитектуре. Если ваша система или команда страдает от долга по базе знаний, попробуйте начать с архитектурного холста, это буквально одностраничная документация по вашей системе.
Технологический долг
Определение: технологический долг относится к случаям использования неподходящих технологий, а также неверно подобранных, устаревших или получивших чрезмерный хайп технологий для некоей задачи. В области разработки ПО это может легко случиться, если зависимости или фундаментальные технологии (фреймворки, библиотеки или даже языки программирования) не обновляются регулярно.

Давайте начнем с устаревших технологий. Возьмем Java™ 6 (последнее официальное обновление в 2013-м году) или PHP 2 (поддержка закончилась в 1997-м) в качестве примеров. Во время ревью кода или архитектуры мы регулярно идентифицируем определенные зависимости как устаревшие. Но почему устаревшая технология является проблемой, даже если более старый диалект языка программирования или библиотека все еще работают?
Старые или неподдерживаемые технологии могут содержать в себе риски, связанные с безопасностью, поскольку определенные уязвимости или атаки могли быть еще не обнаружены в то время, когда эти технологии считались современными. Если этого недостаточно, старые технологии могут страдать от проблем с производительностью или стабильностью или оказаться несовместимыми с более новыми или используемыми в наше время операционными системами. И наконец, более старые технологии часто не содержат определенные функции, доступные в более новых версиях. Представьте себе, что вы хотите использовать некую библиотеку или фреймворк, который в точности решает вашу проблему, но требует Java-20, а ваша система работает только на Java-6.
В целом, поддержание технологии на современном уровне снижает многие риски, но, очевидно, что новые технологии должны тщательно тестироваться.
Теперь давайте посмотрим на следующий вид технологического долга: неподходящие (inappropriate) технологии. Конечно, системы можно интегрировать через электронную почту, но такой подход как-то не кажется правильным в большинстве случаев. Другие примеры:
Использование реляционных баз данных для хранения и посылки запросов к структурам, основанным на графах, такие как мапы или сетевые топологии. Работает, но неудобно и медленно.
Использование Excel™ в качестве базы данных для многопользовательских приложений.
Использование факса для пересылки цифровых документов в другие системы.
Использование функций печати для логирования и трассировки в клиент/сервер окружениях.

Конечно, мы могли бы продолжать этот список сколько угодно долго, но мне нужно было всего несколько примеров.
Инфраструктурный долг
Определение: инфраструктурный долг относится к проблемам или рискам на уровне платформы и аппаратного обеспечения, на котором работает ваша система. Вы можете считать это частным случаем технологического долга из предыдущего раздела.
Сюда включаются устаревшие сервера или операционные системы, старое аппаратное обеспечение или сети.. Такой долг появляется, когда вы решаете «обойтись» старой или уставшей инфраструктурой вместо того, чтобы сделать апгрейд — зачастую потому, что апгрейды часто дорого стоят или несут в себе определенные риски. Однако, проценты на этот долг будут уплачены в виде постоянно повышающейся стоимости поддержки, низкой производительности и проблем с добавлением новой функциональности, которая полагается на современные технологии. Другими словами, если ваш фундамент (инфраструктура) стареет вместо того, чтобы обновляться, вы накапливаете инфраструктурный долг. Организации часто накапливают подобный долг, откладывая необходимые апгрейды и цепляясь за легаси системы по причине кратковременного удобства или боязни изменений.
Институт программной инженерии описывает случаи, когда приложения для мейнфреймов, насчитывающие десятки лет, «накопили существенный технический долг за десятилетия», используя устаревшие компоненты и паттерны, которые тянут вниз релизные циклы и техподдержку. (Demystifying mainframe technical debt).

Следует отметить и такой аспект: миграция в облако без учета требуемой производительности и последствий в части безопасности (также известное как «с lift‑and‑shift что‑то пошло не так»). Стратегия «lift and shift» (поднять и переместить) дает быструю дорогу к облакам, но она полна рисков, если ее используют без тщательного планирования и оценок последствий. Она может привести к эпичному провалу (появлению инфраструктурного долга), когда производительность, стоимость и безопасность не берутся в расчет, либо когда организации ожидают получения выгоды от перехода к облакам, не разобравшись с нативными возможностями облаков.
Все это — инфраструктурный долг. Реальные примеры из жизни обнаруживаются повсеместно: авиалинии, чьи старые системы покупки билетов вызывают истерики при регистрации, больницы, работающие на древних системах, которые не могут легко передавать нужные данные и т. д. Главный вывод из этого таков: пренебрежение апгрейдами инфраструктуры сродни игнорированию ржавеющего фундамента: чем дольше вы ждете, тем тяжелее (и дороже) будет в конце концов ремонт.
Заметка на полях: прагматизм (не) обновления технологии
Когда вы решаете, обновить ли технологию или инфраструктуру, на которой построена ваша система, примите во внимание следующие дополнительные аспекты:
Риск нарушения процессов может перевесить преимущества. «Если не сломано, не чини», особенно когда речь идет о безопасности людей или о серьезных финансовых последствиях.
Люди, которые изначально построили или поддерживали систему, могли уже уволиться. Без глубокого понимания модернизация может быть опасной или невозможной, прежде чем вы наработаете серьезный доменный опыт. См. раздел о долге по базе знаний выше.
Проблемы с качеством в рантайме
Определение: Проблемы с качеством в рантайме — это нежелательное поведение системы, которое наблюдается в рантайме либо при установке и конфигурировании. Примерами являются проблемы с производительностью и стабильностью, отсутствие юзабилити, чрезмерное использование ресурсов и т. д.
Такие проблемы качества в рантайме должны считаться особым видом технического долга. Они могут быть следствием любых сочетаний проблем с кодом, зависимостями, неподходящей технологией или инфраструктурой, а также непонятных или отсутствующих требований.
Давайте посмотрим на следующий тезис:
Можно написать чистый код, который в рантайме будет вести себя неэффективно или небезопасно.
Соответственно, мы приходим к выводу, что просто писать чистый и понятный код недостаточно, даже если у него слабая связность и высокая согласованность. Даже такой код может привести к нежелательному поведению в рантайме, содержать прорехи в безопасности и бесцельно расходовать ресурсы, такие как оперативная память или сетевой трафик.
Необходимое условие для того, чтобы избегать проблем в рантайме, кажется очевидным. Вам необходимо знать специфические требования по качеству. Мы говорили об этом в разделе про долг по требованиям. Другой способ противостоять некоторым проблемам в рантайме — это тестирование, что приводит нас к следующей категории долга.
Долг по тестированию
Определение: Долг по тестированию — это долг, который появляется в результате неадекватного тестирования или QA процессов.
Сюда включается недостаточное покрытие автоматизированными тестами, отсутствие юнит/интеграционных тестов, неадекватные или медленные наборы тестов или даже отсутствие тестовых окружений. В некоторых командах, практикующих быструю разработку, тестирование сокращается и не является приоритетом («потом протестируем») — это экономит им время в кратковременной перспективе, но приводит к накоплению существенных рисков и затрат в будущем. Процент на долг по тестированию платится в форме багов в продакшен, медленных релизов (потому что приходится прибегать к ручному тестированию или решать проблемы на лету), а также в форме страха перед любыми изменениями.
Когда кодовая база плохо покрыта тестами, каждое изменение приводит к неуверенности: разработчики не уверены, что оно не сломает что‑то еще. Со временем это может парализовать улучшения или привести к необходимости титанических усилий для исправления багов. Долг по тестированию также включает устаревшие тест‑кейсы (которые больше не ловят регрессионные баги) и отсутствие интеграционной консистентности, а значит баги будут просачиваться через дыры. По сумме сказанного, если ваша культура тестирования слаба, вы нарабатываете долг, стоимость которого проявится как падения в системах и головная боль, связанная с поддержкой.
Многие организации могут рассказать вам истории о проблемах: маленькое изменение остается не протестированным и вызывает масштабное отключение. Например, в 2012-м году в Royal Bank of Scotland (RBS) произошло массовое отключение, не позволившее миллионам клиентов получить доступ к своим счетам, и вызвано это было неудачным обновлением ПО, относящимся к системе обработки батчей. (См. 37 Epic Software Failures).
Корнем проблемы была признана комбинация факторов, включающая неадекватное тестирование процедуры обновления на легаси системе — по сути, долг по тестированию и операционный долг, приведший к простою в течение нескольких дней.
Долг по тестированию также часто встречается в проектах, которые начинаются с минимально жизнеспособного продукта (MVP). Команды могут написать несколько тестов на счастливый путь (happy path), чтобы побыстрее выкатить проект, и отложить всеобъемлющее тестирование. Это довольно распространенная практика в стартапах, например, некоторые владельцы продукта разрешают проводить только базовое тестирование для MVP, намеренно создавая долг по тестированию, чтобы добиться быстрого запуска. (Why Product Owners Must Prioritize Managing Technical Debt?).
Опасность приходит позже: по мере роста продукта эти отсутствующие тесты приводят к неожиданному нарастанию количества багов, а старые баги возвращаются вновь.
Реальным (и трагическим) примером оказались проблемы с ПО на Boeing 737 Max (MCAS): это очень сложный пример с трагическим исходом, но расследование пришло к выводу, что пробелы в тестировании и надзоре внесли свой вклад в ситуацию, когда критические недостатки остались незамеченными. (См. FAA report on Boeing MCAS)
Подобным образом, даже рутинные баги (например, падение мобильного приложения после обновления) могут говорить о долге по тестированию (возможно, команда не включила ту или иную операционную систему или размер экрана). Кумулятивный эффект долга по тестированию — это более медленный и более рискованный цикл разработки. Команды с большим долгом по тестированию иногда говорят «мы боимся трогать этот код» или «мы не гарантируем, что этот релиз ничего не сломает», что еще раз доказывает, что такой тип долга мешает прогрессу.
Выплата долга по тестированию включает в себя инвестиции в автоматизацию тестов, покрытие критических путей и постоянную интеграцию, и многие команды делают это только после того, как несколько болезненных инцидентов заставляют их сместить фокус в сторону качества. По существу, долг по тестированию напоминает нам, что «если вы не напишете тесты сейчас, потом придется дольше заниматься отладкой».
Долг по безопасности
Определение: долг по безопасности (подмножество долга по качеству) относится к недостаткам в мерах по безопасности, которые накапливаются со временем.
Сюда входят известные уязвимости, по которым не проведен патчинг, устаревшие протоколы шифрования, которые используются по сей день, несоответствие стандартам безопасности, слабый контроль доступа и, в целом, любая работа по безопасности, которую отложили на потом. Под это определение также подпадает игнорирование общепринятых практик по безопасности, обязательных для организаций, и необходимости уделять постоянное внимание всем аспектам безопасности.
Организации накапливают долг по безопасности, когда они игнорируют принятые практики в угоду скорости или удобству — например, пропускают обновления, которые исправляют уязвимости, так как они могут сломать совместимость, или откладывают реализацию аудита логов, потому что «мы сделаем это потом». В ближайшей перспективе ничего плохого не случается, и разработка идет своим чередом, но риск накапливается со временем. Процентом на долг по безопасности зачастую оплачивается взломами защит, утечками данных и экстренными патчами, когда кто‑то сумел воспользоваться имеющейся уязвимостью.

Другими словами, если вы все время откладываете рутинные задачи по поддержанию безопасности, вы, по сути, берете взаймы у надежности системы, а взломы будут безжалостными коллекторами.
В сфере безопасности данных хорошей эвристикой является исходить из предположения, что у атакующего вас хакера больше денег, времени и знаний, чем у вас, и технологии лучше ваших. Лучше считать, что ваш враг или тот, кто вас атакует, превосходит вас по всем параметрам. Другими словами: готовьтесь к худшему.
Одним из самых печально знаменитых примеров долга по безопасности стал взлом данных Equifax в 2017-м году. Equifax, крупное кредитное бюро, пострадало от взлома и упустило в общий доступ персональные данные примерно 147 миллионов людей.
Причина? В Equifax не установили патч на критическую известную уязвимость в веб фреймворке Apache Struts, который они использовали месяцами уже после того, как появился фикс. (См. Equifax»s patching blunder).
Если говорить конкретно, уязвимость в Apache Struts (CVE-2017–5638) была обнаружена в марте 2017-го года, и тогда же появился патч, но в Equifax его не установили. Хакеры воспользовались уязвимостью в Мае‑июле того же года, но в Equifax взлом обнаружили только в конце июля. Этот пример непропатченного ПО показывает, как работает долг на безопасность. Компания «сэкономила» на короткую перспективу, не обновив свою систему, но процент по этому долгу оказался огромным: случился один из самых крупных взломов в истории, на устранение последствий ушли сотни миллионов, многие руководители лишились работы, а ущерб репутации оказался невосполнимым.
Другой пример: атака «WannaCry ransomware» в 2017-м году ударила по многим организациям (от национальной службы здравоохранения Великобритании до глобальных компаний), которые не установили обновления на Windows; те, кто страдал прокрастинацией по поводу установки патчей, обнаружили, что их системы заблокированы вредоносным ПО. Это была очень высокая оплата по долгу на безопасность. Даже менее драматичные примеры не следует сбрасывать со счетов: использование слабых паролей или вставка логина и пароля непосредственно в код может не отразиться на вашей работе сразу, но это тикающая бомба (долг), ждущая своего часа, пока инсайдер или хакер не воспользуется уязвимостью.
Единственный способ избежать этого — это оплатить долг заранее, установив патчи и обновления и внедрив меры безопасности в жизненный цикл разработки, а не делать все в спешке в последний момент.
Операционный долг
Определение: операционный долг относится к неэффективной и ручной работе при деплойментах, мониторинге и техподдержке IT систем.
Это тот долг, который вы накапливаете, когда не инвестируете в оптимизацию операций — например, когда полагаетесь на ручные шаги при деплойменте, не создаете хороших плейбуков для реагирования на инциденты, когда у вас отсутствует мониторинг/observability или в целом не вкладываетесь в автоматизацию рутинных задач.
В течение небольшого периода времени вы, возможно, сможете продержаться на ad‑hoc, ручных операциях («если работает, не трогай»), но по мере роста системы эти ручный процессы будут становиться все медленнее, а количество ошибок все больше. Проценты на операционный долг выплачиваются в форме простоев, медленного восстановления после инцидентов, ошибок в конфигурации и чрезмерной затраты ресурсов на операции. По существу, если ваши практики в сфере операций не дотягивают до современных стандартов в DevOps/автоматизации, вы выплачиваете постоянный налог в форме нестабильности и напрасно потраченного времени. Эта категория также включает долг по документации и базе знаний в сфере операций — например, когда только один человек знает, как деплоить систему, это рискованно.
Несколько примеров: в 2017-м году простой на Amazon S3 был вызван тем, что инженер вручную выполнял рутинный скрипт и допустил опечатку, случайно отправив в оффлайн большее количество серверов, чем предполагалось. Отсутствие проверок безопасности и автоматического лимита на данный процесс превратило маленькую ошибку в масштабный сбой, который продлился несколько часов.
Подобный образом в июле 2023-го года в Microsoft Azure произошел сбой, продлившийся дольше 10 часов из‑за опечатки — неправильная команда в скрипте техподдержки привела к случайному удалению 17 баз данных в продакшен (TechRadar). Этот инцидент подчеркнул, как одна ошибка при ручной работе, в отсутствии надежной валидации и процессов восстановления, может натворить хаоса. Мы можем воспринимать это как операционный долг: возможно, у системы не было процедуры «undo» или нормального ревью для команды удаления, которое могло присутствовать в TODO листе, но не оказалось под рукой, когда была нужна.
Другой часто встречающийся пример — это деплойменты вручную. Компании, которые не автоматизировали свои релизные процессы, часто накапливают скрипты для деплойментов, подкрутки для серверных конфигов и т. д., о которых знают только определенные люди. Это может привести к сбоям по типу «мы задеплоили не ту версию» или «на одном сервере оказался не тот конфиг».
Классический случай произошел в 2012-м году в «Knight Capital» (биржевая фирма): ошибка в процессе деплоймента включила флаг на старую функциональность на некоторых серверах, что привело к неконтролируемому шторму в биржевых алгоритмах, и фирма потеряла 460 миллионов долларов за 45 минут. Первопричиной оказались плохие практики деплоймента — по сути, операционный долг в менеджменте релизов.
Отсутствие observability (логирования, мониторинга, системы предупреждения) тоже является формой операционного долга — вы экономите время, не устанавливая дашборды сейчас, но потом, когда что‑то пойдет не так, вы платите за то, что закрыли глаза на проблему. Например, представьте себе e‑commerce сайт без нормального мониторинга: если сервис чекаута замедлился, никто этого не заметит, пока не пожалуются пользователи, и потом придется потратить несколько часов на то, чтобы найти «бутылочное горлышко», типичный пример выплаты процентов по операционному долгу.

Промышленные исследования показывают, что главным источником сбоев является человеческий фактор при внесении изменений (OpsView.com).
Именно поэтому движение DevOps проповедует принцип «Инфраструктура как код» и автоматизацию: чтобы уменьшить количество ручных шагов и, соответственно, технологический долг. По факту, операционный долг накапливается в командах, которые пренебрегают автоматизацией, документацией и устойчивыми процессами. Это может укусить вас не сразу (возможно, все будет идти хорошо, пока система остается небольшой и один гуру‑админ всегда находится рядом), но по мере того, как система растет, а персонал меняется, этот долг начинает проявляться в долговременных сбоях и «тушении пожаров».
Долг по процессам
Исходя из моего профессионального опыта, особенно неприятный вид долга относится к процессам, особенно к управлению проектами, требованиям или методологии разработки, в частности, к пренебрежению ревью кода, церемониями Agile, а также к отсутствию эффективного процесса передачи знаний.
Такое срезание углов может ускорить работу в краткосрочной перспективе, но за это придется заплатить снижением эффективности работы команды и качества продукта в дальнейшем (проценты по такому долгу могут выглядеть как выгорание команды, проблемы с коммуникациями или переработка функциональности). По существу, если ваши процессы разработки или определения требований плохо отработаны, у вас накапливается организационный долг, который затруднит внесение изменений в продукт в будущем.
Такой долг по процессам приведет (!) к появлению некоторых других типов долга, таким как долг по коммуникациям, по требованиям или структурный долг.
Итоги и выводы
Технический долг — это нечто большее, чем просто проблемы с качеством кода; это целостная концепция, включающая в себя многие виды отягощающих факторов в IT системах. Мы ввели более широкое понимание термина, которое включает требования, код, архитектуру, технологию и инфраструктуру, процессы, тестирование, безопасность и операции.
Если смотреть на технический долг через такую линзу, мы увидим, что любое срезание углов или откладывание важных работ в IT системах может превратиться в «долг», который придется позднее выплачивать с процентами.
Осведомленность — первый шаг к управлению и снижению таких типов долга. Как только команды увидели и признали, что долг существует, они могут принять информированное решение о том, когда и как его следует выплатить.
Не забывайте о том, что не всякий долг обязательно плох: иногда стратегический подход к техническому долгу необходим для того, чтобы выполнить бизнес‑цели (так же, как взятие денег в долг может быть разумным бизнес‑решением). Ключ к успеху состоит в том, чтобы подходить к вопросу осознанно: берите в долг только тогда, когда у вас есть план по его погашению или списанию. Непреднамеренный или неконтролируемый долг — такой, который «случайно получился», когда команда работала в спешке или пренебрегла техобслуживанием — имеет тенденцию расти, как снежный ком.
Как предупреждает Уорд Каннингем, который ввел эту метафору в обращение, опасность состоит в накоплении процентов:
«Каждая минута, потраченная на написание не совсем правильного кода, засчитывается как проценты на долг» и со временем организация «зайдет в тупик под тяжестью накопившегося долга», если не принять мер.
(Технический долг — Википедия).
Рано или поздно организации, которые стратегически управляют своим техническим долгом и другими типами долгов, окажутся более гибкими, устойчивыми и успешными, чем те, кто не следит за своими долгами.
Поэтому, да прибудет с вами сила правильных архитектурных и технических решений!
Заключение
Довольно часто случается и так, что невозможно на месте определить, приведет ли то или иное решение к появлению долга, поскольку определить последствия в текущий момент невозможно. Мы (и наша IT система) могут жить счастливо с последствиями нашего решения и извлекать из него выгоду. Затем внезапно оно окажется причиной серьезной и дорогостоящей проблемы:
Например, катастрофа с безопасностью Log4j, известная как «Log4Shell» (CVE-2021–44 228), является типичным примером широко распространенной, критической и легко взламываемой уязвимости, о чем стало известно в декабре 2021-го года. Эта популярная библиотека для логирования в системах, написанных на Java, стала кошмаром в сфере безопасности, как только данные об уязвимости были опубликованы.
Источники иллюстраций
Иллюстрации были сгенерированы ChatGPT на базе промптов от автора. Русскоязычные подписи были наложены живым человеком вручную.

Подписывайтесь на наш телеграм канал
Jmix — платформа для комплексной разработки корпоративных информационных систем для Java‑разработчиков: https://t.me/jmixplatform