"Мы должны срочно порефакторить весь проект! Это же технический долг!"
"Забудь. Мы не трогаем код, который работает. Концентрируйся на фичах."
Два CTO. Два противоположных подхода. Оба правы. И оба ошибаются.
Первая команда потратила 3 месяца на рефакторинг модуля авторизации. Код стал красивым. Тесты зелёные. Архитектура — как из учебника. Бизнес потерял $400k упущенной выгоды, пока конкуренты запустили две новые фичи.
Вторая команда игнорировала "технический мусор" 2 года. Каждая новая фича занимала в 3 раза больше времени. Багов стало в 4 раза больше. Проект ушёл на полную переписывание, потеряв год разработки.
Вопрос не в том, рефакторить или нет. Вопрос — когда, что и за сколько.
Я 15 лет коллекционирую истории про технический долг. Был консультантом на 40+ проектах, где "долг" убивал продукт. Разберём, как измерять то, что кажется неизмеримым, и принимать решения, которые не приведут вас к банкротству (ни финансовому, ни моральному).
Что такое технический долг (и почему все понимают его неправильно)
Определение, которое реально работает
Технический долг — это разница между текущей архитектурой и идеальной архитектурой для решения текущих бизнес-задач.
Ключевое слово: текущих. Не будущих. Не гипотетических. Текущих.
Это как кредит в банке:
- Берёшь взаймы — идёшь в 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 (Архитектура слабая)
Framework для принятия решений (рефакторить или забить?)
Теперь у вас есть метрики. Что с ними делать? Вот 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 месяца)Реальные кейсы: когда рефакторить, а когда — забить
Кейс 1: "Как мы НЕ рефакторили payment gateway и потеряли $500k"
Ситуация:
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 потерь.
Кейс 2: "Как мы зря порефакторили admin панель и потеряли 3 месяца"
Ситуация:
SaaS стартап. CTO решил "привести код в порядок". Начал с admin панели.
Debt Metrics:
- Cyclomatic Complexity: 12 (нормально)
- Test Coverage: 75% (хорошо)
- Code Churn: 15 (низкий)
Решение: Переписать admin на React вместо Django admin.
Что случилось:
- 3 месяца разработки
- Конкуренты выпустили 2 новых фичи
- Клиенты недовольны (нет обновлений)
- Рефакторинг НЕ принёс бизнес-выгоды
Результат:
- $400k упущенной выгоды
- 2 ключевых клиента ушли к конкурентам
- Team выгорел
Урок: Не рефакторите то, что работает и не блокирует бизнес.
Красные флаги:
- "Давайте перепишем на модный фреймворк"
- "Этот код некрасивый" (но работает)
- "Мне не нравится архитектура" (без бизнес-обоснования)
Кейс 3: "Как 2 дня рефакторинга сэкономили $50k/год"
Ситуация:
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)
Последний совет: Технический долг — это не зло
Технический долг — это инструмент.
Кредит в банке — не зло. Кредит позволяет купить квартиру сейчас, а не через 20 лет. Но если брать кредиты бесконтрольно — банкротство.
То же с техническим долгом:
✅ Взять долг ради MVP — правильно ✅ Взять долг ради deadline — иногда оправдано ❌ Не платить долг годами — самоубийство ❌ Брать долг ради "скорости" без плана возврата — глупость
Главное правило:
"Каждый технический долг должен иметь план возврата. Иначе это не долг, а дефолт."
Вопросы для себя:
- Знаем ли мы, где у нас долг? (метрики)
- Знаем ли мы, сколько он стоит? (Cost of Debt)
- Есть ли план возврата? (backlog задачи)
- Когда мы заплатим? (debt sprint)
Если хотя бы на 2 вопроса ответ "нет" — у вас проблемы.
Что делать прямо сейчас
Шаг 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 Documentation — мониторинг технического долга
- Martin Fowler: Technical Debt — классическая статья
- Managing Technical Debt (книга) — Philippe Kruchten
- Radon Documentation — метрики для Python
- CodeClimate — автоматический анализ кода
- Codecov — test coverage мониторинг
Делитесь опытом!
Я рассказал про метрики и framework. Теперь ваша очередь:
- Какой самый дорогой технический долг был у вас?
- Как вы принимаете решения о рефакторинге?
- Были случаи, когда НЕ рефакторить было правильным решением?
Пишите в комментариях или в Telegram. Обсудим метрики, поделимся кейсами.
Нужна помощь с аудитом технического долга? Пишите на почту — проведу анализ вашего проекта, дам рекомендации по приоритизации рефакторинга. Первая консультация бесплатно.
Понравилась статья? Поделитесь с коллегой, который говорит "рефакторинг — это трата времени" или "надо всё переписать". Спасёте его проект (и карьеру).
Подписывайтесь на обновления в Telegram — пишу про архитектуру, метрики и управление разработкой. Без воды, только практика.



