Как делают софт и карьера Поддержка, технический долг и эволюция продукта
0%

Поддержка, технический долг и эволюция продукта

Поддержка, технический долг и эволюция продукта

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

Это не преувеличение. Оценки доли сопровождения в полной стоимости владения ПО гуляют в диапазоне 60–90% — впервые их системно собрали Лиенц и Свонсон в конце 1970-х, потом подтверждали Боэм, Гласс и другие. Числа спорные и сильно зависят от того, что считать сопровождением, но порядок величины устойчив: писать код с нуля — меньшая часть работы индустрии. Большая часть — менять код, который уже работает и который написал кто-то другой (иногда вы полгода назад).

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

Сопровождение — это четыре разных занятия

Слово «поддержка» склеивает несколько видов работы, у которых разные заказчики, разные сроки и разная ценность. Классификация из ISO/IEC 14764 (и восходящая к тем же Лиенцу и Свонсону) делит сопровождение на четыре типа.

Тип Что это Пример Кто инициирует
Корректирующее чиним то, что сломано баг в расчёте скидки пользователь, мониторинг, тестировщик
Адаптивное подстраиваемся под изменившееся окружение банк поменял формат выписки; вышел новый iOS; изменился закон внешний мир
Совершенствующее улучшаем то, что работает ускорить отчёт с 40 до 4 секунд, добавить фильтр пользователи, продакт
Превентивное делаем будущие изменения дешевле обновить зависимости, вынести дублирующуюся логику, добавить тесты команда

Практический смысл этого деления не академический. Когда бизнес говорит «мы слишком много тратим на поддержку», почти всегда имеется в виду первый тип — исправление багов. Но в реальных замерах корректирующее сопровождение — это лишь около пятой части, а половина и больше приходится на совершенствующее, то есть на развитие продукта, просто называющееся другим словом. Умение показать это разделение — уже полезный навык: «мы не чиним поломки, мы 60% времени строим новое в старой системе» звучит совсем иначе, чем «поддержка съедает команду».

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

Как устроен поток обращений: линии поддержки

Между пользователем, у которого «ничего не работает», и разработчиком, который может починить, обычно стоит несколько фильтров. В классической модели (она пришла из ITIL и service desk-практик) их называют линиями.

  • L1 — первая линия. Операторы поддержки, чат, телефон, тикет-система. Не читают код. Задача — понять, что случилось, применить известное решение из базы знаний, отсеять «забыл пароль» и «не оплачен тариф».
  • L2 — вторая линия. Технически грамотные люди: инженеры поддержки, иногда выделенные аналитики. Умеют смотреть логи, дёргать API, воспроизводить, знают типовые сбои. Отсеивают всё, что не требует изменения кода.
  • L3 — третья линия. Команда разработки. Сюда доезжает то, что требует правки кода, миграции данных или архитектурного решения.

Ключевая деталь, которую стоит понять сразу: линии существуют не чтобы отгородить разработчиков от людей, а чтобы защитить дорогое и редкое время от того, что решается дешевле. Час разработчика стоит компании кратно дороже часа оператора L1, и если разработчик каждый день по десять раз объясняет, где в интерфейсе кнопка «скачать акт», компания платит консультанту зарплату сеньора.

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

Энтерпрайз и стартап: две разные реальности

Крупная компания Стартап
Линии формальные L1/L2/L3, отдельные подразделения, регламент эскалации линий нет: разработчик сам сидит в чате поддержки
Инструмент ServiceDesk/Jira Service Management, SLA в договоре Intercom, Telegram-чат, иногда личный телефон CTO
Скорость фикса недели: тикет, приоритизация, релизное окно, регресс часы: увидел — починил — выкатил
Что видит джун отфильтрованную формулировку задачи живого недовольного человека
Риск проблема живёт долго, все привыкли чинят громких, а не важных; выгорание от постоянных прерываний

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

Триаж: как решается, что чинить первым

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

Severity и priority — это разные шкалы

Их путают постоянно, и путаница дорого стоит.

  • Severity (критичность) — объективное свойство дефекта: насколько сильно ломается система. Оценивает тот, кто нашёл: тестировщик, инженер поддержки.
  • Priority (приоритет) — решение бизнеса: когда мы это чиним. Оценивает продакт или владелец продукта.

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

Типичная шкала severity:

Уровень Смысл Реакция
S1 / Blocker сервис недоступен, теряются деньги или данные, утечка немедленно, вне очереди, поднимают людей
S2 / Critical ключевой сценарий не работает, обходного пути нет в ближайший релиз или хотфиксом
S3 / Major функция работает неверно, есть обходной путь планово, в спринт
S4 / Minor косметика, редкий сценарий когда-нибудь / никогда, честно

Что должно быть в баг-репорте

Джуну важно уметь читать баг-репорты и писать их — вы будете и получателем, и отправителем. Минимальный набор, без которого тикет вернётся:

Заголовок: [Биллинг] Двойное списание при повторной отправке формы оплаты

Окружение: prod, версия API 4.18.1, веб, Chrome 138, пользователь id=88421
Время: 2026-07-14 11:42–11:44 MSK
traceId: 7f3c9a1e-...

Шаги:
1. Открыть /billing/upgrade, выбрать тариф Pro
2. Нажать «Оплатить», в течение 3 секунд нажать ещё раз
3. Дождаться редиректа

Ожидается: одна транзакция
Фактически: две транзакции, обе успешны, баланс списан дважды

Масштаб: 14 обращений за неделю, ~0.3% попыток оплаты
Обходной путь: возврат вручную через админку
Логи/скриншоты: приложены

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

Жизненный цикл бага

У задачи-фичи путь в трекере простой. У бага он ветвистее, потому что у него есть исходы, которых у фичи не бывает: «не воспроизводится», «не баг», «дубликат», «не будем чинить».

Несколько практических замечаний к этой схеме.

«Won’t fix» — честный и нужный статус. Команды, которые стесняются его ставить, накапливают бэклог из тысячи багов, который никто никогда не читает, и в котором тонут настоящие проблемы. Тикет, висящий три года со статусом «открыт», врёт сильнее, чем закрытый с формулировкой «редкий сценарий, стоимость фикса — неделя, ущерб — одно обращение в год, решение пересмотрим при росте обращений».

Переоткрытие — сигнал, а не позор. Высокий процент reopened означает одно из двух: чинят симптом вместо причины либо нет теста, фиксирующего исправление. Отсюда железное правило: каждый исправленный баг в проде получает автотест, воспроизводящий его. Не «хорошо бы», а часть Definition of Done — см. Разработка.

Пятикратное «почему». Если баг S1 — недостаточно починить код. Нужно понять, почему он доехал до прода: не было теста? не покрыли кейс в требованиях? не сработал алерт? Разбор инцидентов и постмортемы подробно разобраны в Релизе и эксплуатации; здесь важно другое — follow-up actions из постмортемов и есть самый качественный, осмысленный техдолг. Он уже обоснован реальной болью, и его проще всего защитить перед бизнесом.

Бюджет поддержки: как команда не тонет

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

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

Рабочие практики:

  1. Резерв ёмкости. 10–30% спринта закладывается под непредвиденное. Конкретный процент вычисляется из истории: посмотрите, сколько незапланированной работы реально было в последние 5–10 спринтов. Если по факту уходит 40%, а планируете вы 0% — проблема не в людях.
  2. Дежурный по поддержке. Один человек в спринте (ротацией) снимает весь входящий поток: разбирает тикеты, отвечает L2, чинит мелочь. Остальные защищены от прерываний. У джуна это лучшая неделя месяца с точки зрения обучения — вы за пять дней увидите больше кусков системы, чем за месяц работы над одной фичей.
  3. Отдельный класс обслуживания. В канбане это expedite-дорожка с жёстким лимитом: одновременно не больше одного «срочного». Механика лимитов и классов обслуживания разобрана в Kanban и поток.
  4. Правило «двух касаний». Если один и тот же вопрос от поддержки пришёл дважды — это не тикет, это дефект продукта или дыра в документации. Чините источник.

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

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

Теперь к главной теме. Термин придумал Уорд Каннингем в отчёте на OOPSLA'92 (оригинальный текст), и в народной интерпретации его смысл потеряли почти полностью.

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

Отсюда следует важное практическое разграничение:

  • Это долг: год назад мы сделали одну таблицу users под одну страну; теперь стран пять, у каждой свои требования к персональным данным, и модель им не соответствует. Мы понимаем, как должно быть, но код этого ещё не отражает.
  • Это не долг, это брак: разработчик не знал про SQL-инъекции и склеил запрос строками. Это дефект, чинится как дефект, а не планируется как «долг».
  • Это не долг, это вкусовщина: код написан в другом стиле, чем вы бы написали. Если он покрыт тестами и понятен — это просто чужой код.

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

Квадрант Фаулера

Мартин Фаулер предложил классификацию по двум осям: осознанность и безрассудность.

Практический вывод: только правый верхний квадрант — управляемая инженерная стратегия. Она допустима, если долг записан (тикет, комментарий, ADR) и есть условие его возврата («до 500 клиентов живём так, дальше переделываем»). Всё остальное — либо проблема с квалификацией (лечится обучением и ревью), либо проблема с процессом (лечится не рефакторингом, а разговором с руководством).

Нижний правый квадрант — самый частый и самый недооценённый: вы честно спроектировали как умели, а через год домен раскрылся иначе. Это не чья-то вина, это природа разработки. Ровно об этом писал Каннингем.

Как проявляются проценты

Проценты по техническому долгу: три сценария

Проценты платятся не деньгами со счёта, а незаметно — временем, которое уходит непонятно куда:

  • одна и та же по размеру фича делается уже не три дня, а полторы недели, и никто не может внятно объяснить почему;
  • перед релизом команда нервничает, тестируют «на всякий случай» всё подряд;
  • задачу может взять только один человек, потому что «только Петя знает, как там устроено» (bus factor = 1);
  • CI идёт 40 минут, из них 15 — перезапуски флаки-тестов;
  • новый человек выходит на самостоятельную работу не за две недели, а за два месяца;
  • на любой вопрос «а что будет, если…» ответ «надо проверить», а не «вот так».

Обратите внимание: ни один из симптомов не звучит как «код некрасивый». Все они измеримы и все переводятся в деньги. Это и есть язык, на котором о долге разговаривают с менеджментом.

Виды долга: у каждого свои проценты

«Технический долг» — зонтик над очень разными вещами, у которых сильно разная скорость нарастания и разная цена возврата.

Вид Пример Как нарастает Цена возврата
Кодовый дублирование, гигантские функции, магические числа линейно, медленно низкая, локальная
Тестовый нет тестов, флаки, тесты на реализацию быстро: без тестов страшно менять всё остальное средняя, но окупается сразу
Архитектурный сервис знает про таблицы другого сервиса медленно, но необратимо: поверх строят новое очень высокая
Данных схема не отражает домен, денормализация «на время» быстро: данные накапливаются, миграция дорожает каждый день высокая, требует миграций
Зависимостей Java 8, библиотека без поддержки с 2019 сам по себе, пока вы спите растёт скачками (EOL, CVE)
Инфраструктурный деплой руками по инструкции в вики линейно + риск человеческой ошибки средняя
Документационный документация врёт быстро обесценивается низкая, если делать регулярно
Знаниевый ушёл единственный человек, знавший модуль скачком при увольнении самая высокая: восстанавливать нечего

Отдельно про долг зависимостей, потому что его недооценивают чаще всего. Это единственный вид долга, который растёт, даже если вы не написали ни строчки кода. Мир движется: библиотека выходит из поддержки, находят CVE, облачный провайдер объявляет EOL версии managed-базы, магазин приложений требует новый target SDK. В какой-то момент «просто обновиться» становится проектом на квартал, потому что между вашей версией и актуальной — четыре мажорных релиза с ломающими изменениями.

Практика, которая дешевле любого героизма: обновляться регулярно и понемногу, автоматически (Dependabot, Renovate), с политикой «обновление зависимостей — обычный PR, а не проект». Плюс инвентаризация: что у нас стоит, до какой даты поддерживается, что уже в статусе EOL. Пересечение с безопасностью здесь прямое — см. Безопасность в пайплайне.

Как измерять долг, не обманывая себя

Первый порыв — прикрутить статический анализатор, посмотреть на «technical debt: 342 days» в SonarQube и предъявить это менеджменту. Не работает: число получено из эвристик про стиль кода и не отвечает на вопрос «сколько нам это стоит».

Измерять стоит последствия, а не свойства кода:

  • Доля незапланированной работы. Сколько процентов ёмкости за квартал ушло на баги, инциденты и «срочно посмотреть». Растёт — долг растёт.
  • Lead time для изменения. Сколько проходит от коммита до прода; и отдельно — сколько занимает типовая фича сейчас против года назад. Метрики DORA и как их правильно читать — в DORA и инженерных метриках.
  • Change failure rate. Доля релизов, потребовавших отката или хотфикса.
  • Время прогона CI и доля флаки-тестов. Прямая, ежедневная, всеми ощущаемая цена.
  • Возраст зависимостей. Медианное отставание от актуальных мажорных версий; количество компонентов вне поддержки.
  • Время онбординга. Сколько недель до первого самостоятельного изменения в проде.

А чтобы понять, куда именно вкладывать деньги, полезнее всего пересечь две вещи: насколько файл сложный и насколько часто его трогают. Идея принадлежит Адаму Торнхиллу («Your Code as a Crime Scene»); считается она из обычного git log без всякого специального софта.

Карта горячих точек: сложность против частоты изменений

# Топ-30 самых часто меняемых файлов за последний год —
# первая половина карты горячих точек, считается за секунды
git log --since="1 year ago" --name-only --pretty=format: \
  | grep -E '\.(py|go|ts|java|cs)$' \
  | sort | uniq -c | sort -rn | head -30

# Сколько разных людей правило файл (знаниевый риск и bus factor)
git log --since="1 year ago" --format='%an' -- path/to/OrderService.java \
  | sort -u | wc -l

Вывод, ради которого всё затевалось: сложный файл, который никто не трогает, — не проблема. Долг там есть, но процентов вы по нему не платите. Рефакторить надо пересечение «сложно» и «часто меняется» — там каждый час вложений возвращается многократно. Это радикально отличается от подхода «пройдёмся анализатором и починим все замечания».

Отдельно про закон Гудхарта: как только «покрытие тестами 80%» становится целью, оно перестаёт быть метрикой качества. Появляются тесты, вызывающие методы без единого ассерта. Покрытие — диагностический инструмент («в этом модуле 4%, посмотрим почему»), а не KPI.

Как продать рефакторинг бизнесу

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

Что работает.

1. Говорите последствиями, а не свойствами. Не «модуль оплаты — спагетти», а «за последние три месяца 6 из 11 инцидентов пришли из модуля оплаты; средняя фича в нём делается в 2,5 раза дольше, чем в остальном коде; каждый релиз мы держим тестировщика лишний день на ручном регрессе».

2. Считайте, но честно. Метод расчёта важнее цифр — и цифры ниже условные, для демонстрации метода, а не норматив:

Наблюдение (из трекера, не из ощущений):
  доработки в модуле оплаты — 4 задачи в месяц
  средний перерасход из-за состояния модуля — ~1,5 дня на задачу
  → 6 инженеро-дней в месяц теряется постоянно

Предложение:
  рефакторинг ядра модуля — 12 инженеро-дней (оценка команды, ±50%)
  ожидаемое сокращение перерасхода — примерно вдвое

Окупаемость:
  экономия ~3 дня в месяц → вложение возвращается за ~4 месяца,
  дальше идёт в плюс, пока модуль активно развивается

Риск бездействия:
  инциденты в оплате = прямые потери выручки и репутации;
  за квартал их было 6

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

3. Выбирайте формат вложения под ситуацию.

Формат Как выглядит Когда работает Риск
По пути (правило бойскаута) улучшаем то, что и так трогаем всегда, база размывает PR, не решает крупное
Фиксированный процент 15–20% ёмкости каждого спринта на долг зрелая команда с доверием процент тихо съедается фичами
Привязка к фиче «эта фича делается 3 дня вместо 8, если сначала 2 дня на подготовку» лучший вариант для крупного долга требует уметь честно оценивать
Отдельный тикет с результатом «сократить время сборки с 40 до 12 минут» измеримый инфраструктурный долг легко откладывается вечно
Fix-it week / технический спринт вся команда на неделю уходит в долг после тяжёлого квартала, перед пиком нагрузки превращается в ритуал без приоритизации

Наиболее устойчивый — привязка к фиче. Вы не просите денег «на красоту», вы выбираете более дешёвый способ сделать то, что бизнесу и так нужно.

4. Никогда не просите «переписать всё». Джоэл Спольски в классическом тексте Things You Should Never Do объяснил, почему большая перепись — самая опасная стратегическая ошибка: старый код уродлив не просто так, в нём годами накапливались исправления реальных багов, о которых вы уже не помните. Переписав, вы выбросите знание вместе с кодом и будете год ловить те же самые баги заново — а рынок в это время не подождёт.

Как отдавать долг: стратегии

Разберём инструменты из этой схемы.

Правило бойскаута («оставь стоянку чище, чем нашёл», популяризировано Робертом Мартином): трогаешь файл — улучши что-то маленькое рядом. Важная граница, которую джуны нарушают первым делом: не смешивайте рефакторинг и функциональные изменения в одном PR. Ревьюер не сможет отделить «переименовали переменную» от «поменяли условие», ревью займёт втрое дольше, а при откате придётся откатывать и полезное. Правильно: PR №1 — только рефакторинг, поведение не меняется, тесты те же и зелёные; PR №2 — фича.

Характеризующие тесты (Майкл Фезерс, «Working Effectively with Legacy Code»). Классический тупик: код надо менять, но тестов нет, а написать тесты нельзя, потому что код не тестируем. Выход — тесты, которые фиксируют текущее поведение, включая странное и, возможно, ошибочное. Их задача не проверить корректность, а поймать вас за руку, если вы что-то незаметно поменяли.

# Легаси: считает скидку, поведение никто не помнит целиком.
# Первый шаг — не улучшать, а зафиксировать. Значения получены
# прогоном текущего кода, а не выведены из требований.
import pytest
from legacy.pricing import calculate_discount

@pytest.mark.parametrize("total,is_vip,promo,expected", [
    (1000, False, None,      0),
    (1000, True,  None,      50),      # VIP: 5%
    (5000, True,  None,      500),     # порог 3000 → 10%
    (5000, True,  "SUMMER",  750),     # промо суммируется — так ли задумано?
    (0,    True,  "SUMMER",  0),
    (-100, False, None,      0),       # отрицательная сумма не падает (!)
])
def test_characterization(total, is_vip, promo, expected):
    assert calculate_discount(total, is_vip, promo) == expected

Комментарии в таком тесте — не украшение. Строка «промо суммируется — так ли задумано?» — это зафиксированный вопрос к продакту. Часто именно такие тесты вскрывают, что система годами вела себя не так, как все думали.

Branch by abstraction (описание Фаулера): вместо долгоживущей ветки вводим абстракцию над заменяемым куском, пишем новую реализацию рядом, переключаем флагом, потом удаляем старую. Всё это время код в main, релизы идут как обычно.

Strangler Fig (Фаулер, 2004) — та же идея на уровне системы: новая реализация растёт вокруг старой, трафик переводится по частям, старое отмирает по кускам. Отсылка к фикусу-душителю. Основной способ выбраться из большого легаси без «переписывания всего». Практика миграций данных под этим соусом (expand–contract) разбиралась в Релизе и эксплуатации, а сам архитектурный контекст — в Архитектурных паттернах.

Удаление. Самый недооценённый инструмент. Мёртвые фичи, забытые фича-флаги (флаг, включённый на 100% полтора года назад, — это уже не флаг, а мёртвая ветка кода), эндпоинты без вызовов, задания в кроне, которые ничего не делают. Прежде чем удалять — добавьте телеметрию и подождите цикл (месяц, квартал, для отчётности — год): вы удивитесь, что используется, и ещё больше — что нет. Минус тысяча строк кода не требует объяснений перед бизнесом и мгновенно снижает стоимость сопровождения.

Легаси: без презрения

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

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

Второе, циничное и точное: легаси — это код, который приносит деньги. Если бы не приносил, его бы просто выключили, и он бы вас не мучил. Система, которую все ругают на кухне, обычно та, на которой держится выручка.

Отсюда — уважение к забору Честертона: прежде чем убрать непонятную конструкцию, выясните, зачем она поставлена. Странный if с проверкой на конкретного клиента почти наверняка появился после инцидента. sleep(200) в коде интеграции — потому что у партнёра гонка на их стороне. Первый рефлекс «убрать этот бред» — самый дорогой рефлекс в индустрии.

Как заходить в незнакомую систему, когда документации нет:

  1. Тесты — если есть, это лучшая существующая спецификация. Читайте их первыми.
  2. git log и git blame — история изменений плюс номера тикетов в сообщениях коммитов. Часто это единственный сохранившийся ответ на вопрос «почему так».
  3. Логи и мониторинг прода — какие пути реально исполняются. Половина кода может быть недостижима.
  4. Люди — тот, кто помнит контекст, экономит вам недели. Спрашивать — не признак слабости, а рациональное использование ресурса; на дистанции это разбирается в Первых 90 днях.
  5. Маленькое безопасное изменение — до конца понять систему чтением невозможно. Проведите через весь конвейер что-то крошечное: заодно узнаете, как тут вообще устроены сборка, тесты и релиз.

Эволюция продукта: системы стареют

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

Мейр Леман в 1970–80-х сформулировал законы эволюции ПО. Два из них стоит помнить наизусть:

  • Закон непрерывного изменения: система, используемая в реальной среде, обязана меняться, иначе она становится всё менее полезной. Неизменный код не «стабилен», он устаревает относительно движущегося мира.
  • Закон растущей сложности: по мере изменений сложность системы растёт, если специально не тратить силы на её снижение. То есть беспорядок — состояние по умолчанию, порядок требует энергии. Это не пессимизм, это термодинамика проектов.

Деприкейшн: как правильно убивать

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

Что здесь важно и почему:

  • Сначала телеметрия, потом решения. Без данных о реальном использовании любой разговор об отключении — гадание. Логируйте не только «сколько вызовов», но и «кто вызывает».
  • Заголовки Deprecation и Sunset в HTTP-ответах (RFC 9745 и RFC 8594) — машиночитаемое предупреждение, которое клиент увидит в логах, даже если пропустил письмо.
  • Brownout — короткие плановые отключения по расписанию, объявленные заранее. Жестоко, но работает: те, кто игнорировал шесть писем, обнаруживаются за один день и, что важно, до финального отключения, когда ещё можно помочь.
  • Удаление кода — отдельный шаг после отключения. Иначе мёртвый код останется в системе навсегда и будет пугать людей ещё три года.

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

Как понять, что фичу пора выключать

Симметричный вопрос к разработке: продукт растёт не только добавлением. Признаки мёртвой фичи — низкий или нулевой недельный охват, отсутствие обращений в поддержку (никто не жалуется, потому что никто не пользуется), стоимость поддержки выше вклада в выручку. Как считать продуктовые метрики и не обмануться — в Метриках продукта; разработчику здесь важно другое: инициировать разговор об удалении — нормальная и ценная инициатива, и она хорошо смотрится в разговоре о грейде.

Что всё это даёт лично вам

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

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

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

Это отлично звучит на собеседовании. История «я нашёл, что 60% инцидентов приходят из одного модуля, посчитал перерасход, защитил рефакторинг, время фичи упало вдвое» бьёт любой рассказ про новую фичу: в ней есть данные, инициатива, коммуникация с бизнесом и измеримый результат. Как строить такие истории — в Как проходить собеседования.

И честное предупреждение о ловушке. Есть риск стать «тем, кто держит легаси»: незаменимым, ценным и застрявшим. Признаки — вы единственный, кто может это чинить; вас не отпускают на новые проекты именно поэтому; ваши достижения не видны никому, кроме тех, кто и так знает. Противоядия: документировать и передавать знание (незаменимость — уязвимость, а не актив), фиксировать результаты в измеримых величинах, регулярно обсуждать это на one-to-one. Механика такого разговора — в Как расти.

Типичные ошибки

  • «Дайте нам спринт, мы всё перепишем». Почти всегда заканчивается системой, у которой те же проблемы плюс новые, плюс полгода без фич.
  • Рефакторинг в одном PR с фичей. Убивает ревью и делает откат невозможным.
  • Рефакторинг без тестов. Это не рефакторинг, а переписывание с закрытыми глазами. Рефакторинг по определению не меняет поведение — а как вы это докажете?
  • Рефакторинг холодного кода. Красивая переработка модуля, который трогают раз в год, — потраченное время. Смотрите на карту горячих точек.
  • «Техдолг» как отговорка. Иногда за фразой «тут техдолг» скрывается «мне лень разбираться» или «я бы написал иначе».
  • Бэклог багов как свалка. Тысяча открытых тикетов, из которых 900 неактуальны, — это не бэклог, а способ не принимать решений. Регулярно закрывайте старое явным «won’t fix».
  • Игнорирование обновлений зависимостей до момента, когда обновиться уже нельзя без переписывания.
  • Личное отношение к чужому коду. Автор писал в других условиях, с другим знанием и другими сроками. Через год ваш сегодняшний код будет выглядеть так же — и это нормально.
  • Молчаливое страдание. Если долг не оцифрован и не озвучен, для бизнеса его не существует. Ваша боль — не аргумент; цифры из трекера — аргумент.

Мини-итог

  • После релиза начинается самая долгая и дорогая фаза жизни системы; сопровождение — это четыре разных занятия, и большая часть из них — развитие, а не починка.
  • Линии поддержки экономят дорогое время, но глушат обратную связь; хорошие команды намеренно пробивают в фильтре окна.
  • Severity — свойство дефекта, priority — решение бизнеса; путать их дорого. «Won’t fix» — честный статус.
  • Техдолг по Каннингему — разрыв между текущим пониманием и кодом, а не «плохой код». Управляем только осознанный и записанный долг.
  • Измеряйте последствия (доля незапланированной работы, lead time, change failure rate, флаки, возраст зависимостей), а не замечания анализатора.
  • Вкладывайтесь в пересечение «сложно» × «часто меняется». Холодный сложный код можно не трогать.
  • Отдавайте долг маленькими шагами: характеризующие тесты → branch by abstraction → strangler fig. Большая перепись — самая дорогая стратегия.
  • Продукты стареют по законам Лемана; деприкейшн и удаление — такая же часть работы, как разработка.
  • Поддержка и легаси — не наказание, а самый быстрый способ вырасти, если не застревать в роли единственного носителя знания.

Источники

Что дальше

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

Кто есть кто в команде: роли, зоны ответственности, кто что решает

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов