JavaRush /Курсы /Claude code /Технический долг и static...

Технический долг и static signals

Claude code
26 уровень , 1 лекция
Открыта

1. Эмоциональный диагноз кода бесполезен

Когда вы впервые открываете legacy-сервис, мозг очень быстро ставит диагнозы: «грязно», «перемудрили», «я хочу домой». Реакция человеческая, особенно если перед вами mrr-engine, документированный на уровне «ну оно как-то работает». Но по эмоциям решения не принимают.

Проблема ведь не в том, что эмоции неправильные, а в том, что по ним нельзя принимать решения. «MrrFormulas.java выглядит подозрительно» — коллега кивнёт из вежливости. «Низкое покрытие, логика pause/resume разбросана по двум классам, файл меняли одиннадцать раз за год разные люди» — вот это по делу.

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

Слабая формулировка Полезная формулировка
mrr-engine какой-то мутный mrr-engine/MrrFormulas.java меняли 11 раз за год, покрытие около 25%, а сценарий pause/resume не покрыт тестами, поэтому изменение логики MRR здесь рискованно
Конфиги разъехались Флаги биллинга лежат и в application.yml, и в переменных окружения, а правила приоритета не описаны, поэтому поведение на стендах может отличаться
Тесты хрупкие Интеграционный тест MrrSnapshotIT покрывает только 3 happy-path сценария и не трогает partial refund, downgrade и pause/resume

Вот это и есть нужный вам поворот мышления. Вы не лечите систему словом «некрасиво» — сначала делаете её наблюдаемой.

2. Формула одного полезного сигнала

Чтобы не тонуть в описаниях на полстраницы, удобно пользоваться простой формулой. Держите её короткой:

Сигнал = область + evidence + риск + следующий шаг

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

Вот так может выглядеть одна запись по CashFlow Dashboard:

## Область
mrr-engine / pause-resume flow

## Доказательства
- `mrr-engine/MrrFormulas.java:88` — paused subscription остаётся в active set
- `subscriptions/SubscriptionService.java:154` — pause меняет статус, но не влияет на MRR
- `tests/integration/MrrSnapshotIT.java` — нет сценария pause/resume

## Риск
Business-critical flow без явной safety net

## Следующий шаг
До любых изменений зафиксировать текущее поведение сценария pause/resume

Позже сигнал уйдёт в current-state документ как подтверждённый факт либо сожмётся в строку RISK_MAP.md. Но начинается всё именно с этой короткой формы.

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

Полезно помнить ещё одно правило: один сигнал — одна мысль. Если в записи и deprecated dependency, и слабые тесты, и странный конфиг, и метод на двести строк — это не один сигнал, а четыре. Разделите: иначе не поймёте, что делает область опасной.

3. Источники evidence в legacy-проекте

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

flowchart LR
    A[Код и конфиги] --> D[Evidence]
    B[Тесты и coverage] --> D
    C[Git history и отчёты сборки] --> D
    D --> E[Технический сигнал]
    E --> F[Риск и следующий шаг]

Если говорить совсем практично, то в CashFlow Dashboard полезны четыре источника. Первый — исходники: классы, методы, конфигурации, миграции. Второй — тесты и покрытие: не наличие теста, а то, что именно он проверяет. Третий — git history: файлы, которые часто меняют разные люди, выдают хрупкую границу. Четвёртый — артефакты сборки: устаревшие библиотеки, предупреждения, deprecated API, нестабильные конфиги.

Простейшие команды здесь уже дают много информации:

git log --since="1 year ago" --oneline -- mrr-engine/MrrFormulas.java
# 9f3a2c1 Исправлен partial refund
# 6a4d8e0 Учтён pause/resume
# 21bc774 Коррекция MRR на границе месяца

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

Ещё один полезный сигнал — покрытие:

./gradlew test jacocoTestReport
# BUILD SUCCESSFUL
# HTML-отчёт покрытия: build/reports/jacoco/test/html/index.html

Если отчёт показывает, что mrr-engine покрыт на 25%, а payments — на 60%, это не «хороший против плохого», а факт, который соединяется с бизнес-критичностью: для модуля, влияющего на MRR и отчёты для Commerce OS, 25% — повод быть аккуратным.

Иногда полезны и совсем «приземлённые» команды:

grep -R "TODO\|FIXME" -n legacy/
# legacy/OldBillingUtils.java:47 // TODO убрать после миграции PSP
# legacy/RefundMath.java:113 // FIXME временный расчёт proration

TODO и FIXME не доказывают проблему автоматически. Но если они стоят в расчётах refund и proration без тестов рядом — это уже не записка на холодильнике, а часть картины.

4. Static signals, заслуживающие внимания

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

Static signals — это сигналы, которые можно получить без запуска продакшена и правок кода. Ниже — типичные случаи CashFlow Dashboard:

Что заметили Почему это важно
Низкое покрытие в mrr-engine Это business-critical область, и любое изменение здесь трудно проверить локально без дополнительной safety net
legacy/OldBillingUtils.java используется в десятках мест Зависимость на «старый универсальный класс» часто означает скрытую связанность и дорогую стоимость изменения
PaymentRetryService.java часто меняли разные авторы Это признак файла с высоким churn: зона нестабильна, ответственность размыта, а интерфейс, возможно, выбран неудачно
Feature flags размазаны по application.yml и env Конфигурационное поведение может различаться между средами, а значит, ошибка будет воспроизводиться не всегда

Есть ещё несколько частых признаков, которые полезно держать в голове. Дублирование логики — про риск расхождения правил, а не про некрасивый код. Метод на 150 строк опасен не длиной, а тем, что в нём трудно изолировать один бизнес-сценарий от другого. Устаревшая зависимость в платежах страшнее такой же в экспорте CSV — разный радиус поражения. Хрупкий тест — слабый датчик: падает не тогда, когда система сломана, и молчит, когда она поплыла.

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

5. Запрос к Claude: анализ вместо угадывания

Claude в таких задачах очень полезен, но только если вы задаёте ему рамку. Без неё он выдаёт красивый обзор про архитектурные недостатки, часть даже похожа на правду. Но «похоже на правду» и «подтверждено» — разные вещи.

Плохой запрос звучит так: «Найди весь технический долг в проекте». В таком виде задача слишком широкая — Claude приносит красивый, но рыхлый текст.

Гораздо лучше работает узкий запрос с форматом ответа и запретом на фантазию:

Проанализируй только `mrr-engine/MrrFormulas.java` и связанные тесты.
Не предлагай исправлений.
Верни не больше 5 сигналов в формате:
область -> evidence -> риск -> следующий шаг.
Используй только факты, которые можно подтвердить файлами
или выводом команд. Если уверенности нет, пометь это как гипотезу.

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

Полезен и двухпроходный режим работы. Сначала просите собрать факты: какие тесты, какой churn, есть ли TODO, какие методы вызываются чаще. Потом на этих фактах — 35 сигналов. Галлюцинаций меньше: Claude работает как аналитик с материалами дела.

И ещё одно практическое правило: на discovery держитесь read-only или plan-режима. Как только у модели появляется возможность «заодно поправить», она смешивает диагностику с операцией. А вам нужен именно диагноз.

6. Связка технического сигнала с бизнес-риском

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

Представьте два файла. ReportCsvFormatter.java некрасивее — длинноват, дублирует форматирование. Но через MrrFormulas.java идёт расчёт MRR, попадающий в дашборд Commerce OS и в отчёты для финансового менеджера, и потому он важнее, хоть и короче.

Область Технический сигнал Бизнес-вес Практический вывод
mrr-engine/MrrFormulas.java Низкое покрытие, высокий churn, спорная логика pause/resume Высокий Изменять только после фиксации текущего поведения
reporting/ReportCsvFormatter.java Дублирование и длинный метод Средний или низкий Можно отложить, если сейчас нет задач в этой зоне
application.yml + env-флаги Непрозрачный приоритет конфигурации Высокий для воспроизводимости Сначала задокументировать фактическое поведение сред
legacy/OldBillingUtils.java Слишком много зависимых мест Высокий по стоимости изменения Любая правка требует оценки радиуса воздействия

Именно здесь рождается хороший recommended next step — не «переписать всё», а действие, пропорциональное риску. Для business-critical no-test зоны — фиксация текущего поведения. Для high-churn файла — проверка ownership и карты вызовов. Для fragile config — документация приоритета значений. Для deprecated utility — оценка того, сколько мест от него зависит. Чем приземлённее, тем лучше.

Legacy не любит героизм. Legacy любит конкретику.

7. Фиксация сигналов в рабочей заметке

И ещё одна привычка, без которой signal log развалится: не держать сигналы только в голове или в чате с Claude. Память у человека дырявая, у длинной сессии с ИИ — тоже. Не зафиксировали — через два дня снова смотрите в тот же файл и мучительно вспоминаете, почему он казался опасным.

Такая заметка — не постоянный артефакт рядом с RISK_MAP.md, а промежуточный signal log. Он удерживает наблюдения между discovery-note и устойчивыми current-state- и decision-артефактами, пока вы не собрали их в ARCHITECTURE_CURRENT.md или запись risk map.

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

## Сигнал 1
Область: `mrr-engine/MrrFormulas.java`
Доказательства:
- low coverage (~25%)
- `paused` остаётся в active set
- нет теста на pause/resume
Риск: business-critical flow без safety net
Следующий шаг: сначала зафиксировать текущее поведение сценария

## Сигнал 2
Область: `legacy/OldBillingUtils.java`
Доказательства:
- используется в 30+ местах
- в файле есть `TODO` про старый PSP
Риск: высокая связанность, дорогая цена изменения
Следующий шаг: не менять точечно без карты зависимостей

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

Если хотите, можно добавить ещё одно поле — Уверенность или Проверить вручную — там, где вывод построен на косвенных сигналах. Честная пометка «гипотеза» доверие к документу укрепляет.

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

1
Задача
Claude code, 26 уровень, 1 лекция
Недоступна
Static signal: coverage report не собирается в legacy build
Static signal: coverage report не собирается в legacy build
1
Задача
Claude code, 26 уровень, 1 лекция
Недоступна
Документ DEBT_SIGNALS.md по evidence, а не по ощущениям
Документ DEBT_SIGNALS.md по evidence, а не по ощущениям
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ