Три подхода, три уровня

В прошлом уроке мы зафиксировали главный принцип SDD: спека — источник правды, а код — её реализация. Теперь разберём, чем SDD отличается от двух подходов, с которыми вы, скорее всего, уже встречались: TDD и BDD.

Главное различие — не в том, какой подход «лучше», а в том, на каком уровне абстракции каждый из них работает.

TDD: тест как мини-спецификация функции

Test-Driven Development — цикл из трёх шагов: Red → Green → Refactor. Сначала пишется падающий тест для ещё не существующей функции, потом минимальный код, чтобы тест прошёл, и наконец рефакторинг без поломки теста.

TDD работает на уровне отдельной функции или метода. Тест — это формальное утверждение о конкретном поведении конкретного кода:

# Пишем тест первым — функция ещё не существует
def test_generate_reset_token_returns_32_chars():
    token = generate_reset_token(user_id=42)
    assert len(token) == 32
    assert isinstance(token, str)

# Потом пишем функцию, чтобы тест прошёл
def generate_reset_token(user_id: int) -> str:
    import secrets
    return secrets.token_hex(16)

TDD отвечает на вопрос «правилен ли этот кусок кода?» — но молчит о том, нужна ли функция вообще и что она означает для бизнеса.

BDD: поведение на бизнес-языке

Behavior-Driven Development поднимает абстракцию выше. Вместо тестов — сценарии, которые понимает и разработчик, и product manager. Чаще всего используется синтаксис Gherkin:

Feature: Сброс пароля

  Scenario: Действительная ссылка открывает форму сброса
    Given пользователь запросил сброс пароля
    And ссылка была отправлена 30 минут назад
    When пользователь переходит по ссылке
    Then он видит форму для ввода нового пароля

  Scenario: Просроченная ссылка показывает ошибку
    Given пользователь запросил сброс пароля
    And ссылка была отправлена 2 часа назад
    When пользователь переходит по ссылке
    Then он видит сообщение «Ссылка устарела, запросите новую»

BDD отвечает на вопрос «как система ведёт себя снаружи?» — что видит пользователь, как он взаимодействует с системой. Технических деталей по-прежнему нет.

Check yourself
Не подглядывая — что BDD говорит о технической реализации? Например, каким алгоритмом генерируется токен сброса пароля?

SDD: спека как главный артефакт

SDD работает ещё на уровень выше. Прежде чем писать тесты или Gherkin-сценарии, вы фиксируете полную картину: зачем нужна фича, какие бизнес-правила должны соблюдаться, каковы ограничения и критерии готовности — и только потом переходите к архитектуре, задачам и коду.

SDD не определяет, как именно проверять поведение или корректность кода — это задача BDD и TDD соответственно.

graph TD SDD["SDD — Spec-Driven Development\nЧто строим? Зачем?\nspec.md · plan.md · tasks.md"] BDD["BDD — Behavior-Driven Development\nКак ведёт себя система?\nGiven / When / Then"] TDD["TDD — Test-Driven Development\nПравилен ли код?\nRed → Green → Refactor"] SDD -->|задаёт интент| BDD BDD -->|проверяет поведение| TDD style SDD fill:#4A90E2,color:#fff style BDD fill:#5BA55B,color:#fff style TDD fill:#E8A838,color:#000
graph TD
    SDD["SDD — Spec-Driven Development\nЧто строим? Зачем?\nspec.md · plan.md · tasks.md"]
    BDD["BDD — Behavior-Driven Development\nКак ведёт себя система?\nGiven / When / Then"]
    TDD["TDD — Test-Driven Development\nПравилен ли код?\nRed → Green → Refactor"]
    SDD -->|задаёт интент| BDD
    BDD -->|проверяет поведение| TDD
    style SDD fill:#4A90E2,color:#fff
    style BDD fill:#5BA55B,color:#fff
    style TDD fill:#E8A838,color:#000
SDD, BDD и TDD — три уровня одного стека. Каждый отвечает на свой вопрос и не заменяет других.

Почему SDD не отменяет TDD и BDD

Это ключевой момент, который легко пропустить. SDD, BDD и TDD не конкурируют — они работают на разных уровнях и дополняют друг друга:

  • SDD задаёт интент: что и зачем строим, каковы ограничения. Выход — spec.md, plan.md, tasks.md.
  • BDD проверяет поведение системы: сценарии на бизнес-языке, которые можно автоматизировать.
  • TDD фиксирует корректность реализации: каждая функция проверена на уровне кода.

На практике это выглядит так: вы пишете spec.md (SDD), по нему Claude Code генерирует план и задачи, в ходе реализации появляются Gherkin-сценарии и unit-тесты. Все три уровня существуют одновременно — SDD просто стоит над ними, а не вместо них.

Check yourself
Команда внедрила SDD. Один разработчик говорит: «Теперь у нас есть spec.md — зачем вообще писать unit-тесты?» Он прав?

Один пример — три уровня

Возьмём правило из прошлого урока: «При трёх неверных попытках входа аккаунт блокируется на 15 минут». Вот как оно выглядит на каждом уровне.

SDD (spec.md):

- При трёх неверных попытках входа подряд аккаунт блокируется на 15 минут.
- После блокировки пользователь видит понятное сообщение с временем разблокировки.
- Успешный вход сбрасывает счётчик неверных попыток.

BDD (Gherkin):

Scenario: Блокировка после трёх неверных попыток
  Given пользователь ввёл неверный пароль 2 раза
  When пользователь вводит неверный пароль в третий раз
  Then аккаунт блокируется на 15 минут
  And пользователь видит «Аккаунт заблокирован на 15 минут»

TDD (Python):

def test_three_failed_logins_lock_account():
    user = create_user(password="correct")

    for _ in range(3):
        login(user, password="wrong")

    result = login(user, password="correct")
    assert result.error == "ACCOUNT_LOCKED"
    assert result.locked_for_minutes == 15

Каждый уровень добавляет конкретику: SDD задаёт правило, BDD описывает сценарий взаимодействия, TDD проверяет конкретную функцию. Убери один уровень — и остальные теряют часть смысла.

Когда спека избыточна

SDD хорош там, где есть неопределённость: нужно сначала договориться о «что», прежде чем переходить к «как». Но полный цикл Spec → Plan → Tasks → Implement бывает оверкилом:

  • Небольшой багфикс с понятной причиной. Напишите тест, воспроизводящий баг, потом исправьте. Спека ничего не добавит.
  • Утилитарная функция с чётким контрактом. «Добавь truncate_text(text, max_length) по образцу уже существующих» — TDD справится без spec.md.
  • Спайк или исследование. Цель — узнать, реализуемо ли что-то; результат уходит в корзину. Спека только мешает.
  • Рефакторинг без изменения поведения. Поведение уже задокументировано тестами; новая спека ничего не добавит.

Простой маркер: если вы легко можете написать тест до кода — используйте TDD. Если перед написанием теста нужно сначала договориться о том, что система должна делать, — нужна спека.

Check yourself
Вам нужно добавить функцию truncate_text(text, max_length), которая обрезает строку и добавляет «…». Какой подход выберете — SDD или TDD — и почему?

Упражнение: распределите по уровням

Ниже — восемь артефактов из разработки фичи «сброс пароля». Каждый относится ровно к одному уровню. Распределите их.

Quick recall
Почему SDD не заменяет TDD и BDD, а работает вместе с ними?
Quick recall
Почему BDD-сценарии пишут на Gherkin, а не на обычном коде?
Quick recall
Почему TDD не может ответить на вопрос, нужна ли функция для бизнеса?