Что такое технический долг?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Технический долг
Технический долг (Technical Debt) — это стоимость быстрого решения проблемы вместо правильного, которую придётся платить позже в виде дополнительной работы. Это финансовая метафора: как финансовый долг с процентами, технический долг растёт со временем, если его не погашать.
Типы технического долга
1. Код без тестов
# ❌ Долг: код работает, но нет тестов
def calculate_discount(price, customer_type):
if customer_type == 'vip':
return price * 0.8
elif customer_type == 'regular':
return price * 0.9
else:
return price
# ✅ Погашение долга: добавляем тесты
import pytest
def test_vip_discount():
assert calculate_discount(100, 'vip') == 80
def test_regular_discount():
assert calculate_discount(100, 'regular') == 90
def test_no_discount():
assert calculate_discount(100, 'unknown') == 100
2. Дублирование кода
# ❌ Долг: одинаковый код в трёх местах
def process_user_data(data):
if not data:
return None
data = data.strip().lower()
return data
def process_email(email):
if not email:
return None
email = email.strip().lower()
return email
def process_username(username):
if not username:
return None
username = username.strip().lower()
return username
# ✅ Погашение долга: выносим общую функцию (DRY)
def normalize_string(value):
if not value:
return None
return value.strip().lower()
process_user_data = normalize_string
process_email = normalize_string
process_username = normalize_string
3. Хардкод и магические числа
# ❌ Долг: магические числа разбросаны по коду
def calculate_bonus(salary):
if salary < 50000:
return salary * 0.05 # Что это?
elif salary < 100000:
return salary * 0.10 # А это?
else:
return salary * 0.15 # И это?
# ✅ Погашение долга: константы с понятными именами
MIN_BONUS_THRESHOLD = 50000
MID_BONUS_THRESHOLD = 100000
MIN_BONUS_RATE = 0.05
MID_BONUS_RATE = 0.10
MAX_BONUS_RATE = 0.15
def calculate_bonus(salary):
if salary < MIN_BONUS_THRESHOLD:
return salary * MIN_BONUS_RATE
elif salary < MID_BONUS_THRESHOLD:
return salary * MID_BONUS_RATE
else:
return salary * MAX_BONUS_RATE
4. Отсутствие документации
# ❌ Долг: непонятная функция без комментариев
def fn(a, b, c):
return (a ** 2 + b ** 2) ** 0.5 if c else a + b
# ✅ Погашение долга: документация и типизация
def calculate_distance_or_sum(
x: float,
y: float,
use_euclidean: bool
) -> float:
"""
Вычисляет расстояние или сумму двух значений.
Args:
x: Первое значение (координата X или число)
y: Второе значение (координата Y или число)
use_euclidean: True для формулы расстояния,
False для простой суммы
Returns:
Расстояние по формуле Евклида или сумма x + y
Example:
>>> calculate_distance_or_sum(3, 4, True)
5.0
>>> calculate_distance_or_sum(3, 4, False)
7
"""
if use_euclidean:
return (x ** 2 + y ** 2) ** 0.5
else:
return x + y
5. Плохая архитектура (Spaghetti Code)
# ❌ Долг: смешение логики и представления
from flask import Flask
app = Flask(__name__)
@app.route('/user/<int:user_id>')
def get_user(user_id):
import sqlite3
conn = sqlite3.connect('db.sqlite')
cursor = conn.cursor()
# Бизнес-логика
cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,))
user = cursor.fetchone()
if not user:
return {"error": "User not found"}, 404
# Форматирование
age = 2024 - user[2]
return {"id": user[0], "name": user[1], "age": age}
# ✅ Погашение долга: слоистая архитектура
# domain/user.py
class User:
def __init__(self, id, name, birth_year):
self.id = id
self.name = name
self.birth_year = birth_year
@property
def age(self):
return 2024 - self.birth_year
# application/user_service.py
class UserService:
def __init__(self, repo):
self.repo = repo
def get_user(self, user_id):
return self.repo.find_by_id(user_id)
# infrastructure/user_repository.py
class SQLiteUserRepository:
def find_by_id(self, user_id):
# Работа с БД
pass
# presentation/routes.py
@app.route('/user/<int:user_id>')
def get_user(user_id):
user = user_service.get_user(user_id)
if not user:
return {"error": "User not found"}, 404
return {"id": user.id, "name": user.name, "age": user.age}
Метафора: финансовый долг
# Быстрое решение (берём долг)
функция_без_проверок() # Работает, но опасно
# Процент: позже возникают баги
# Правильное решение (не берём долг)
функция_с_тестами_и_валидацией() # Медленнее изначально
# Но экономит время на отладку и поддержку
Как измерить технический долг
1. Метрики покрытия тестами
# Смотрим процент покрытия
pytest --cov=myapp --cov-report=html
# Цель: >80% coverage
2. Cyclomatic Complexity (сложность кода)
# ❌ Высокая сложность: много if/else
def check_user(user):
if user:
if user.active:
if user.verified:
if user.premium:
if user.balance > 0:
return "All good"
# Pylint: Complexity = 5
# ✅ Низкая сложность: логичные части
def is_valid_user(user):
return user and user.active and user.verified
def is_premium_user(user):
return is_valid_user(user) and user.premium
def can_process(user):
return is_premium_user(user) and user.balance > 0
# Pylint: Complexity = 1 для каждого
3. Инструменты анализа
# Pylint
pylint myapp/
# Flake8
flake8 myapp/
# Black (форматирование)
black myapp/
# Bandit (безопасность)
bandit -r myapp/
Стратегия погашения долга
1. Регулярная рефакторизация
# Выделяй 20% спринта на улучшение кода
# вместо новых фич
2. Boy Scout Rule: оставляй код чище
# Когда трогаешь файл, улучши его чуть-чуть
# Если видишь плохой код — добавь тесты
# Если видишь дублирование — вынеси в функцию
3. Запрашивай код-ревью
# Перед слиянием в main проверяй:
# - Есть ли тесты?
# - Сложность не выросла?
# - Документирован ли код?
4. Автоматизируй проверку качества
# .github/workflows/quality.yml
name: Code Quality
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: pip install pylint flake8 pytest
- run: pylint src/
- run: flake8 src/
- run: pytest --cov=src/
Примеры последствий долга
Без долга:
- Быстро добавляешь фичи
- Легко найти баги
- Новичок быстро разбирается
- Низкие затраты на поддержку
С долгом:
- Медленно разрабатываешь (ищешь баги)
- Сложно добавлять фичи
- Новичок теряется в коде
- Высокие затраты на поддержку
Лучшие практики
- Пиши тесты с самого начала (TDD)
- Рефакторь регулярно, не откладывай
- Используй типизацию (mypy, Pydantic)
- Документируй сложное (docstrings, README)
- Делай код простым (KISS, DRY)
- Моделируй архитектуру (чистые слои, DDD)
- Автоматизируй проверки (CI/CD)
Помни: платить за долг нужно постоянно, иначе он начнёт давить всей проектом. Лучше потратить 1 час на рефакторинг сегодня, чем 10 часов на отладку завтра.