← Назад к вопросам

Что такое технический долг?

2.3 Middle🔥 151 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Технический долг (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/

Примеры последствий долга

Без долга:

  • Быстро добавляешь фичи
  • Легко найти баги
  • Новичок быстро разбирается
  • Низкие затраты на поддержку

С долгом:

  • Медленно разрабатываешь (ищешь баги)
  • Сложно добавлять фичи
  • Новичок теряется в коде
  • Высокие затраты на поддержку

Лучшие практики

  1. Пиши тесты с самого начала (TDD)
  2. Рефакторь регулярно, не откладывай
  3. Используй типизацию (mypy, Pydantic)
  4. Документируй сложное (docstrings, README)
  5. Делай код простым (KISS, DRY)
  6. Моделируй архитектуру (чистые слои, DDD)
  7. Автоматизируй проверки (CI/CD)

Помни: платить за долг нужно постоянно, иначе он начнёт давить всей проектом. Лучше потратить 1 час на рефакторинг сегодня, чем 10 часов на отладку завтра.

Что такое технический долг? | PrepBro