Один CTO говорит: «Рефакторим весь проект, это же долг». Другой: «Не трогай то, что работает. Фичи».
Первая команда три месяца полировала авторизацию. Тесты зелёные, архитектура как в учебнике. Пока они полировали, конкуренты выкатили две фичи. Бизнес посчитал $400k упущенной выгоды.
Вторая два года не трогала «мусор». Фича стала занимать втрое больше времени, багов стало вчетверо. Год ушёл на переписывание.
Вопрос не «рефакторить или нет». Вопрос: что, когда и за сколько.
Пятнадцать лет я смотрю, как долг убивает продукт. На сорока с лишним проектах одно и то же: долг считают моралью, а это арифметика.
Что считать долгом
Определение
Технический долг это разница между текущей архитектурой и той, которая решает текущие задачи. Не будущие. Не гипотетические.
Как кредит:
- Берёшь взаймы: быстрее в production
- Платишь проценты: каждая новая фича дороже
- Дефолт: переписывание с нуля
- Рефинансирование: рефакторинг
Метафора от Ward Cunningham (автор термина, 1992):
"Shipping first-time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite... The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt."
Виды долга
Не весь долг одинаковый. Есть ипотека, а есть кредит на iPhone. Разберём.
1. Преднамеренный долг (Deliberate Debt)
Что: Сознательное решение написать "быстро и грязно" ради скорости.
Пример:
# TODO: Сейчас hardcode, потом вынести в конфиг
PAYMENT_API_URL = "https://api.stripe.com/v1"
def process_payment(amount):
# Временный костыль для MVP
# После MVP: добавить retry logic, fallback, мониторинг
response = requests.post(PAYMENT_API_URL, data={"amount": amount})
return response.json()Когда оправдан:
- MVP для валидации гипотезы
- Deadline критичен (конференция, выставка, демо инвестору)
- Feature нужен для замера метрик (A/B тест)
Риски:
- TODO превращается в NEVER
- "Временный" костыль живёт годами
- Проценты копятся незаметно
2. Непреднамеренный долг (Inadvertent Debt)
Что: Команда не знала лучшего решения. Наивная реализация.
Пример:
# Junior думал, что это нормально
def get_all_users():
users = []
for user_id in range(1, 100000): # О, боже...
user = db.query(f"SELECT * FROM users WHERE id={user_id}")
if user:
users.append(user)
return users
# Production через месяц: 503 Server TimeoutКак возникает:
- Junior разработчики без менторства
- Отсутствие code review
- Команда не знает паттерны для данной задачи
Как избежать:
- Code review обязателен
- Парное программирование для сложных задач
- Обучение команды best practices
3. Устаревший долг (Bit Rot)
Что: Код был хорош год назад. Но библиотеки обновились, паттерны изменились.
Пример:
# 2020 год: это было модно
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
# 2025 год: теперь так никто не делает
# Есть async views, Pydantic валидация, rate limiting из коробкиПричины:
- Зависимости устарели
- Best practices эволюционировали
- Новые фреймворки появились
Когда трогать:
- Security уязвимости
- Блокирует новые фичи
- Мешает масштабированию
4. Архитектурный долг (Architectural Debt)
Что: Неправильная архитектура на уровне системы.
Пример:
# Первоначальная архитектура (MVP)
Monolith Django App
↓
PostgreSQL
# Реальность через 2 года (100k+ users)
Monolith (5M строк кода)
↓
PostgreSQL (1TB, тормозит)
# Нужная архитектура
API Gateway
↓
├─ User Service (microservice)
├─ Payment Service (microservice)
├─ Notification Service (microservice)
↓
PostgreSQL + Redis + Kafka
Когда критично:
- Team вырос с 3 до 30 человек
- Deploy занимает 2 часа
- Нельзя масштабировать горизонтально
Метрики
Нельзя управлять тем, что не измеряешь. Вот метрики, которые я использую на реальных проектах.
1. Code Churn Rate (частота изменений кода)
Что измеряет: Как часто файл меняется. Высокий churn = высокий долг.
Формула:
Code Churn = Количество изменений файла за период / Всего файлов
Как измерить:
# Git: топ-10 самых изменяемых файлов за 6 месяцев
git log --since="6 months ago" --pretty=format: --name-only \
| sort | uniq -c | sort -rg | head -10Пример вывода:
245 src/models/user.py # ← Тут явно проблемы
189 src/api/payments.py # ← И тут тоже
87 src/utils/validators.py
56 src/views/dashboard.py
Интерпретация:
- Churn > 100: Файл переписывается каждую неделю. Код нестабилен.
- Churn 50-100: Высокая активность. Возможно, долг копится.
- Churn < 50: Стабильный код.
Действия:
# Если user.py меняется 245 раз за 6 месяцев:
# 1. Code review: почему столько изменений?
# 2. Рефакторинг: возможно, класс слишком большой (God Object)
# 3. Разделить: User → UserProfile, UserAuth, UserSettings2. Cyclomatic Complexity (цикломатическая сложность)
Что измеряет: Количество независимых путей выполнения в коде.
Формула:
Complexity = Edges - Nodes + 2 (для графа потока управления)
Простыми словами: Количество if, for, while, and, or в функции.
Инструмент:
# Python: radon
pip install radon
radon cc -a src/ --total-average
# Вывод:
# src/models/user.py
# M 245:0 UserModel.validate - D (23) # ← Сложность 23: это плохо
# M 112:0 UserModel.save - C (15)IDE с встроенной поддержкой:
Современные IDE показывают Cyclomatic Complexity прямо в редакторе:
- PyCharm: показывает complexity в виде подсветки (серый = норма, желтый = внимание, красный = проблема)
- VS Code: через расширение "Python Complexity" или "SonarLint"
- IntelliJ IDEA: встроенный анализ кода с метриками
- Visual Studio: Code Metrics (Analyze → Calculate Code Metrics)
Пример в PyCharm:
def process_payment(...): # ← PyCharm показывает: Complexity: 23 (серая подсказка)
# Если наведёте курсор, увидите: "Cyclomatic complexity too high"Преимущество IDE:
- Видишь метрики во время написания кода (не пост-фактум)
- Настраиваешь пороги (warning при > 10, error при > 20)
- Не нужно запускать отдельные команды
Интерпретация (стандарт Software Engineering Institute):
- 1-10: Простой код, легко тестировать
- 11-20: Средняя сложность, риск багов растёт
- 21-50: Высокий риск, код непонятен
- 50+: Untestable code, рефакторинг обязателен
Пример плохого кода:
def process_payment(user, amount, currency, method, promo_code=None):
if user.is_active:
if user.balance >= amount:
if currency in ["USD", "EUR", "RUB"]:
if method == "card":
if user.card_verified:
if promo_code:
discount = get_discount(promo_code)
if discount:
amount = amount * (1 - discount)
# ... ещё 50 строк вложенных if
return True
return False
# Complexity: 42 🤯Рефакторинг:
def process_payment(user, amount, currency, method, promo_code=None):
_validate_user(user)
_validate_currency(currency)
_validate_payment_method(user, method)
final_amount = _apply_discount(amount, promo_code)
return _charge_user(user, final_amount, method)
# Complexity каждой функции: 3-5 ✅3. Test Coverage (покрытие тестами)
Что измеряет: % кода, покрытого тестами.
Формула:
Coverage = (Строки, выполненные в тестах / Всего строк кода) × 100%
Инструмент:
# Python: pytest-cov
pytest --cov=src --cov-report=html
# Вывод:
# Name Stmts Miss Cover
# ---------------------------------------
# src/models/user.py 245 89 64% # ← Низкое покрытие
# src/api/auth.py 156 5 97% # ← ХорошоИнтерпретация:
- 80-100%: Хорошее покрытие (не гарантия, но индикатор)
- 50-80%: Средний долг, риск регрессий
- < 50%: Высокий долг, каждое изменение: русская рулетка
Важно: 100% coverage ≠ 0% багов. Но 0% coverage = 100% боль при рефакторинге.
Пример из жизни:
# Код без тестов (coverage 0%)
def calculate_discount(user, cart_total):
if user.is_vip and cart_total > 1000:
return cart_total * 0.2
elif user.orders_count > 10:
return cart_total * 0.1
return 0
# Junior поменял логику:
def calculate_discount(user, cart_total):
if user.is_vip or cart_total > 1000: # ← Ошибка: 'and' → 'or'
return cart_total * 0.2
# ...
# Результат: VIP клиент получил скидку 20% на корзину $50
# Бизнес потерял $10k за выходныеС тестами:
def test_vip_discount():
user = User(is_vip=True)
assert calculate_discount(user, 1500) == 300 # ❌ Тест упал
# Junior увидел ошибку ДО production4. Code Duplication (дублирование кода)
Что измеряет: % повторяющихся блоков кода.
Инструмент:
# jscpd (работает с Python, JS, etc.)
npm install -g jscpd
jscpd src/
# Вывод:
# Duplications: 23.4% # ← 23% кода дублируется
# Files: 156
# Clones: 42Интерпретация:
- < 5%: Нормально (иногда дублирование оправдано)
- 5-15%: Средний долг
- > 15%: Высокий долг, DRY principle нарушен
Пример:
# ❌ Дублирование (3 раза один и тот же код)
def create_user(data):
if not data.get("email"):
return {"error": "Email is required"}, 400
if not re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", data["email"]):
return {"error": "Invalid email"}, 400
# ...
def update_user(data):
if not data.get("email"):
return {"error": "Email is required"}, 400
if not re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", data["email"]):
return {"error": "Invalid email"}, 400
# ...
def send_invite(data):
if not data.get("email"):
return {"error": "Email is required"}, 400
if not re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", data["email"]):
return {"error": "Invalid email"}, 400
# ...✅ Рефакторинг:
def validate_email(email):
if not email:
raise ValueError("Email is required")
if not re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email):
raise ValueError("Invalid email")
return email.lower()
def create_user(data):
email = validate_email(data.get("email"))
# ...5. Bus Factor (фактор автобуса)
Что измеряет: Сколько человек должно попасть под автобус, чтобы проект встал.
Формула:
Bus Factor = Количество ключевых разработчиков, которые знают критичный код
Инструмент:
# Git: кто автор каждой строки?
git ls-files | xargs -n1 git blame --line-porcelain | grep "^author " | sort | uniq -c | sort -rg
# Вывод:
# 12456 John Doe # ← Если John уволится: проект умрёт
# 3421 Alice Smith
# 987 Bob JohnsonИнтерпретация:
- Bus Factor = 1: Критично. Один человек знает весь код.
- Bus Factor = 2-3: Риск. Зависимость от нескольких людей.
- Bus Factor ≥ 5: Здоровая команда.
Пример из жизни:
Senior ушёл в отпуск на 2 недели. Production упал. Никто не знал, как работает авторизация. Потеряли 3 дня на реверс-инжиниринг собственного кода.
Решение:
- Code review всем кодом
- Парное программирование
- Документация архитектуры
- Rotation (каждый должен поработать с каждым модулем)
6. Debt Ratio (коэффициент долга)
Что измеряет: Стоимость исправления долга / Стоимость написания кода с нуля.
Формула (SonarQube):
Debt Ratio = (Remediation Cost / Development Cost) × 100%
Пример:
Remediation Cost: 120 дней (время на рефакторинг)
Development Cost: 400 дней (время, потраченное на разработку)
Debt Ratio = (120 / 400) × 100% = 30%
Интерпретация:
- < 5%: Отличный код
- 5-10%: Хорошо
- 10-20%: Средний долг
- > 20%: Критично, рефакторинг или переписывание
Инструмент:
# SonarQube (бесплатная версия)
docker run -d --name sonarqube -p 9000:9000 sonarqube:latest
# Запустить анализ проекта7. Lead Time (время добавления фичи)
Что измеряет: Сколько времени занимает добавление новой фичи.
Формула:
Lead Time = Время от коммита до production
Как измерить:
# JIRA/GitHub: среднее время задачи "In Progress" → "Done"
# Или через git:
git log --all --format="%h %ai" | head -100Интерпретация:
- Lead Time растёт со временем: Долг копится
- Lead Time стабилен: Долг контролируется
- Lead Time снижается: Вы молодцы (или фичи становятся проще)
Пример:
| Период | Средний Lead Time | Вывод |
|---|---|---|
| Q1 2024 | 3 дня | Нормально |
| Q2 2024 | 5 дней | Долг растёт |
| Q3 2024 | 8 дней | Критично |
| Q4 2024 | 14 дней | Код превратился в болото |
Причины роста:
- Код стал сложнее (Cyclomatic Complexity вырос)
- Больше регрессий (Test Coverage упал)
- Больше merge conflicts (Архитектура слабая)
Рефакторить или нет
Теперь у вас есть метрики. Что с ними делать? Вот decision framework из 4 шагов.
Шаг 1: Оценить стоимость долга (Cost of Debt)
Формула:
Cost of Debt = Время на обходные решения × Стоимость часа разработки
Пример:
# У вас в коде:
def get_user_orders(user_id):
# Костыль: нет индекса на user_id, запрос тормозит
# Каждый раз добавляем LIMIT 100, чтобы не упасть
return db.query("SELECT * FROM orders WHERE user_id = ? LIMIT 100", user_id)
# Стоимость долга:
# - Каждая новая фича с заказами требует костыль
# - Разработчики тратят 30 минут на каждый костыль
# - 10 фич в квартал = 5 часов
# - Стоимость часа разработчика: $50
# Cost of Debt = 5 часов × $50 = $250/кварталЕсли стоимость > $1000/год: рассмотрите рефакторинг.
Шаг 2: Оценить стоимость рефакторинга (Cost of Refactoring)
Формула:
Cost of Refactoring = Время на рефакторинг × Стоимость часа + Риск регрессий
Пример:
# Рефакторинг: добавить индекс на orders.user_id
# Время: 2 часа (миграция + тесты)
# Риск: низкий (индекс не ломает логику)
# Cost of Refactoring = 2 часа × $50 + $0 (риск) = $100Вывод: $100 вложений vs $250/квартал экономии = окупится за 1.5 месяца.
Шаг 3: Оценить opportunity cost (упущенная выгода)
Вопрос: Что вы НЕ сделаете, пока будете рефакторить?
Пример:
Вы планируете 2 недели на рефакторинг архитектуры платежей. За это время:
- Конкурент запустит новую фичу
- Вы НЕ выпустите запланированный feature
- Клиенты будут ждать bug fix
Формула:
Opportunity Cost = Потенциальная выгода от новых фич × Вероятность успеха
Пример:
Новая фича: Subscription billing
Потенциальная выгода: $10k MRR
Вероятность успеха: 70%
Opportunity Cost = $10k × 0.7 = $7k
Рефакторинг платежей:
Экономия: $2k/год на поддержке
Opportunity Cost: $7k упущенной выгоды
Вывод: НЕ рефакторить сейчас. Сначала фича.
Шаг 4: Приоритизация по матрице Impact/Effort
Матрица:
High Impact │ REFACTOR NOW │ SCHEDULE │
│ (quick wins) │ (важно) │
────────────┼────────────────┼──────────────┤
Low Impact │ MAYBE │ IGNORE │
│ (if free time)│ (tech vanity)│
────────────┴────────────────┴──────────────┘
Low Effort High Effort
Примеры:
| Задача | Impact | Effort | Решение |
|---|---|---|---|
| Добавить индекс на user_id | High | Low | ✅ REFACTOR NOW |
| Переписать монолит в микросервисы | High | High | 📅 SCHEDULE |
| Переименовать переменную x → user | Low | Low | 🤷 MAYBE |
| Переписать на TypeScript | Low | High | ❌ IGNORE |
Полный алгоритм принятия решения
def should_refactor(debt_item):
# Шаг 1: Стоимость долга
cost_of_debt = estimate_debt_cost(debt_item)
# Шаг 2: Стоимость рефакторинга
cost_of_refactoring = estimate_refactoring_cost(debt_item)
# Шаг 3: Упущенная выгода
opportunity_cost = estimate_opportunity_cost(debt_item)
# Шаг 4: ROI рефакторинга
roi = (cost_of_debt - cost_of_refactoring - opportunity_cost) / cost_of_refactoring
# Правила решения
if roi > 2.0:
return "REFACTOR NOW" # Окупится в 2x
elif roi > 1.0:
return "SCHEDULE" # Окупится, но не срочно
elif roi > 0:
return "MAYBE" # Окупится, но есть лучшие варианты
else:
return "IGNORE" # Не окупитсяПример использования:
debt_item = {
"name": "Добавить индекс на orders.user_id",
"cost_of_debt": 250, # $/квартал
"cost_of_refactoring": 100, # $
"opportunity_cost": 0 # Не блокирует фичи
}
roi = (250 - 100 - 0) / 100 = 1.5
# Вывод: SCHEDULE (окупится за 1.5 месяца)Кейсы
Payment gateway: не рефакторили
Ситуация:
E-commerce платформа. Payment gateway написан в 2018 году. Код работал, но:
- Нет тестов (coverage 0%)
- Hardcode на каждом шагу
- 1 разработчик (Bus Factor = 1)
Debt Metrics:
- Cyclomatic Complexity: 68 (critical)
- Code Churn: 180 изменений за год
- Lead Time для payment фич: 2 недели (вместо 2 дней)
Решение команды: "Не трогаем. Работает: не лезь."
Что случилось:
Black Friday 2023. Трафик вырос в 10 раз. Payment gateway упал. Разработчик в отпуске. Никто не знает, как чинить.
Результат:
- 8 часов downtime
- $500k упущенных продаж
- 2 недели на emergency fix
- 1 месяц на полную переписывание
Урок: Если Bus Factor = 1 на критичном модуле: рефакторинг ОБЯЗАТЕЛЕН.
Что надо было сделать:
- Добавить тесты (1 неделя)
- Упростить код (1 неделя)
- Документация (2 дня)
Итого: 2.5 недели vs 1 месяц emergency + $500k потерь.
Админка: рефакторили зря
Ситуация:
SaaS стартап. CTO решил "привести код в порядок". Начал с admin панели.
Debt Metrics:
- Cyclomatic Complexity: 12 (нормально)
- Test Coverage: 75% (хорошо)
- Code Churn: 15 (низкий)
Решение: Переписать admin на React вместо Django admin.
Что случилось:
- 3 месяца разработки
- Конкуренты выпустили 2 новых фичи
- Клиенты недовольны (нет обновлений)
- Рефакторинг НЕ принёс бизнес-выгоды
Результат:
- $400k упущенной выгоды
- 2 ключевых клиента ушли к конкурентам
- Team выгорел
Урок: Не рефакторите то, что работает и не блокирует бизнес.
Красные флаги:
- "Давайте перепишем на модный фреймворк"
- "Этот код некрасивый" (но работает)
- "Мне не нравится архитектура" (без бизнес-обоснования)
Два дня и пятьдесят тысяч
Ситуация:
API сервис. Каждый endpoint дублирует логику аутентификации.
Debt Metrics:
- Code Duplication: 32% (критично)
- 150 строк одинакового кода в 45 файлах
Проблема:
Bug в логике аутентификации → нужно фиксить в 45 местах → 2 дня работы → риск пропустить файл.
Решение:
2 дня на рефакторинг:
- Вынести аутентификацию в middleware
- Покрыть middleware тестами
- Удалить дублированный код
Результат:
- Bug fix теперь: 15 минут (вместо 2 дней)
- Экономия: 20 bug fixes/год × 2 дня × $300/день = $12k/год
-
- снижение риска security ошибок
ROI:
Cost: $600 (2 дня × $300)
Savings: $12k/год
ROI: 20x в первый год
Урок: Code duplication на критичных участках: первый кандидат на рефакторинг.
Инструменты
1. SonarQube (лучший all-in-one)
Что умеет:
- Cyclomatic Complexity
- Code Duplication
- Test Coverage
- Security уязвимости
- Debt Ratio
Установка:
docker run -d --name sonarqube -p 9000:9000 sonarqube:latestИнтеграция в CI/CD:
# .gitlab-ci.yml
sonarqube:
script:
- sonar-scanner -Dsonar.projectKey=my-project
only:
- main2. CodeClimate (для GitHub)
Что умеет:
- Автоматический анализ pull requests
- Debt trends (динамика долга)
- Интеграция с GitHub Actions
Цена: $0 (open source), $199/месяц (private repos)
3. Radon (Python, бесплатно)
Установка:
pip install radonКоманды:
# Cyclomatic Complexity
radon cc -a src/
# Maintainability Index
radon mi src/
# Raw metrics (LOC, LLOC, SLOC)
radon raw src/4. Git analytics (бесплатно)
Code Churn:
git log --since="6 months ago" --pretty=format: --name-only \
| sort | uniq -c | sort -rg | head -10Bus Factor:
git ls-files | xargs -n1 git blame --line-porcelain \
| grep "^author " | sort | uniq -c | sort -rg5. Codecov (Test Coverage)
Интеграция:
# .gitlab-ci.yml
test:
script:
- pytest --cov=src --cov-report=xml
- bash <(curl -s https://codecov.io/bash)Фичи:
- Coverage trends
- Pull request комментарии
- Badges для README
Чеклист
Рефакторить сейчас, если:
- Bus Factor = 1 на критичном модуле
- Cyclomatic Complexity > 20
- Test Coverage < 50% на критичном коде
- Security уязвимости (CVSS > 7.0)
- Lead Time растёт каждый квартал
- Code Duplication > 30%
- Production incidents связаны с этим кодом
Запланировать, если:
- Debt Ratio > 10%
- Code Churn > 100 за полгода
- Блокирует новые фичи (но не критично)
- Команда растёт (новички тонут в коде)
- Onboarding занимает > 1 месяца
Не трогать, если:
- Код работает и не блокирует бизнес
- ROI < 1.0 (не окупится)
- Opportunity cost высок (есть важные фичи)
- «Просто некрасиво» (без бизнес-боли)
- Модуль планируется удалить в ближайшие 6 месяцев
Как внедрять
1. Debt Sprint (20% времени на рефакторинг)
Правило: Каждый 5-й спринт: debt sprint.
Что делаем:
- Фиксим top-10 debt items (по метрикам)
- Добавляем тесты на критичные модули
- Обновляем документацию
Пример:
Sprint 1-4: Features
Sprint 5: Debt Sprint
- Task 1: Покрыть тестами payment gateway (coverage 0% → 80%)
- Task 2: Рефакторинг UserModel (complexity 45 → 12)
- Task 3: Добавить индексы на топ-10 slow queries
2. Boy Scout Rule (правило бойскаута)
Правило: Оставляй код чище, чем нашёл.
Как применять:
# Ты фиксишь баг в функции process_payment()
# Видишь, что там complexity 25
# Тратишь +30 минут на рефакторинг
# Complexity становится 10
# Commit: "Fix payment bug + refactor for clarity"Ограничения:
- Не больше 30% времени на рефакторинг
- Не меняй архитектуру (только локальные улучшения)
3. Debt Dashboard (визуализация долга)
Метрики на dashboard:
- Debt Ratio (SonarQube)
- Test Coverage (Codecov)
- Lead Time (Jira/GitLab)
- Code Churn (Git)
Пример:
┌─────────────────────────────────────────┐
│ Technical Debt Dashboard Q4 2025 │
├─────────────────────────────────────────┤
│ Debt Ratio: 12% ⚠️ (was 8%) │
│ Test Coverage: 78% ✅ (was 75%) │
│ Lead Time: 6 days 📈 (was 5) │
│ Critical Issues: 3 🚨 │
└─────────────────────────────────────────┘
Review: Раз в квартал с командой. Обсуждаем тренды.
4. Debt Tag в Pull Requests
Правило: Если PR увеличивает долг: ставим тег [DEBT].
Пример:
PR #123: [DEBT] Quick fix for payment bug
Debt:
- Added hardcode for Stripe API key (should be in env)
- Skipped tests (time constraint)
- Complexity increased from 12 to 18
Action item:
- Created task DEBT-456 to refactor in next sprint
Польза:
- Осознанный долг (не случайный)
- Трекинг (знаем, где копится долг)
- Планирование (DEBT-задачи в backlog)
Долг это не мораль
Кредит на квартиру не зло. Кредит без графика платежей это уже не кредит.
Долг ради MVP нормален. Долг ради дедлина иногда оправдан. Годами не платить это дефолт, даже если репозиторий зелёный.
Четыре вопроса: где долг (метрики)? сколько стоит (Cost of Debt)? есть задача в backlog? когда платим (debt sprint)?
Если на два из четырёх нет, это уже не управление, а надежда.
Что делать сейчас
Шаг 1: Измерить долг (30 минут)
# 1. Code Churn
git log --since="6 months ago" --pretty=format: --name-only \
| sort | uniq -c | sort -rg | head -10
# 2. Cyclomatic Complexity (Python)
pip install radon
radon cc -a src/
# 3. Test Coverage
pytest --cov=src --cov-report=term-missing
# 4. Bus Factor
git ls-files | xargs -n1 git blame --line-porcelain \
| grep "^author " | sort | uniq -c | sort -rgШаг 2: Приоритизировать (1 час)
Создайте таблицу:
| Debt Item | Impact | Effort | ROI | Действие |
|---|---|---|---|---|
| Payment gateway | High | Medium | 5.0 | REFACTOR NOW |
| Admin панель | Low | High | 0.2 | IGNORE |
| User model | Medium | Low | 2.5 | SCHEDULE |
Шаг 3: Запланировать (10 минут)
- Создайте задачи в Jira/GitLab для top-3 debt items
- Добавьте в следующий спринт 1-2 задачи
- Запланируйте Debt Sprint через 4 недели
Шаг 4: Автоматизировать (1 час)
# .gitlab-ci.yml
debt-analysis:
stage: test
script:
- pip install radon
- radon cc -a src/ --total-average
- pytest --cov=src --cov-report=term
only:
- mainСсылки
- SonarQube
- Martin Fowler, Technical Debt
- Philippe Kruchten, Managing Technical Debt
- Radon
- CodeClimate
- Codecov



