SDD наоборот: не программируем по спекам, а получаем их на сдачу

в 13:12, , рубрики: ai-агенты, claude code, llm, openspec, SDD, Superpowers, tdd

Недавно мне нужно было внести изменение в проект, где не было никакого SDD-фреймворка. Вообще никакого, Карл! Ни proposal, ни design, ни дельт спек, ни даже скромного tasks.md в уголке. Я морально приготовился к долгому онбордингу агента: сейчас он будет час ходить по коду, путаться в модулях и спрашивать, где тут вход. А модель просто разобралась в коде, нашла места будущего вмешательства, сделала изменение, и оно спокойно прошло человеческое ревью у кодовнера. Без единой спеки. «А что, так можно было?»

После этого я посмотрел на свой рабочий проект с OpenSpec, на папку openspec/ с её двумя сотнями файлов, и задал себе резонный вопрос: зачем мы генерируем, храним и заносим под git кучу артефактов, которые современной модели не нужны? Кому мы пишем эти бумаги, если главный читатель прекрасно обходится без них?

Из этого вопроса вырос SlimSpec, плагин для Claude Code. Он берёт из SDD то, что работает, и выкидывает остальное. На реальном проекте сопоставимое изменение обошлось ему в 29,7M токенов против 45,7M у OpenSpec, а в git не попало ни строчки процессных файлов. Дальше расскажу, откуда взялась идея, как устроен флоу, покажу цифры и то, чем пришлось заплатить. Спойлер: заплатить пришлось, бесплатных обедов не бывает даже в мире LLM.

Зачем вообще был нужен SDD

Ещё недавно модели были слабее, контекст маленьким, сабагентов не было. Работали так: в одной сессии составляешь подробный план, в следующей его реализуешь. Модель постоянно теряла контекст, как золотая рыбка, которая на третьем круге аквариума снова знакомится с замком. И память о проекте приходилось куда-то складывать, иначе каждая сессия начиналась с «привет, я новый джун, расскажите мне всё с начала».

Spec-driven development закрывал ровно эти боли: маленький контекст, отсутствие сабагентов, нужда в подробном плане. Записал всё на бумажку, бумажку положил в репозиторий, следующая сессия бумажку прочитала. Надёжно, как швейцарские часы. Так он и стал стандартом.

Тогда же появились два главных, на мой взгляд, фреймворка: OpenSpec и Superpowers. Я поработал с обоими, основным у меня стал OpenSpec. За что я их ценил:

  • режим explore (в Superpowers он называется brainstorming), где дизайн складывается раньше плана. Это вообще лучшее, что случилось с AI-разработкой: сначала думаем, потом пишем код, а не наоборот;

  • сам план и спецификации;

  • у OpenSpec ещё и мерж спек: в живой спеке остаётся только важное. Superpowers этого лишён напрочь и просто копит design-файлы, как бабушка пакеты под раковиной. Выкинуть жалко, а найти нужный уже нельзя.

В SDD-флоу, каким его видело сообщество, всё это ложилось идеально.

Где OpenSpec начал тормозить

В пет-проектах я долго сидел на OpenSpec с «китайскими» моделями и проблем не видел. Скорее наоборот: код стало легче писать и поддерживать, пропали семантические дубли (ну вы знаете, когда в проекте вдруг находятся три функции форматирования даты), агент чаще делал то, что нужно, и делал правильнее.

На коммерческом проекте мы командой тоже выбрали OpenSpec, и поначалу я был в этом уверен на сто процентов. Через пару месяцев стало понятно, что флоу очень медленный. Особенно когда мы ушли от ручного ревью. Подписка корпоративная, лимиты жалеть незачем, мы шпарили в несколько окон терминала над разными фичами, а процесс всё равно тянулся, как очередь в МФЦ за справкой о том, что у тебя есть справка.

Что тормозило:

  • После proposal идут ещё четыре артефакта: design, tasks, дельты спек и прочее. Мы их практически не читали. Но они честно писались, коммитились и уезжали в архив. Без бумажки ты букашка, а с бумажкой… ну, тоже букашка, только с бумажкой.

  • Любая правка в незаархивированном change выливается в ещё один change. Передумал по ходу? Будьте добры, заполните заявление по форме.

  • Перед архивацией обязателен ребейз. И ещё надо попросить агента «ручками» проверить мерж спек, потому что кто-то другой мог залезть и «испортить» секцию, в которую вписывается MODIFIED-блок.

Мой личный рекорд бюрократии выглядит так. Нужно было выпилить фичу: ненужный кусочек экрана. Задача на удаление, что может быть проще? Удалить код, удалить тесты, закрыть тикет. Итоговый дифф:

SDD наоборот: не программируем по спекам, а получаем их на сдачу - 1

Почти 5,5 тысячи строк ушло, и почти столько же пришло. Пришли, конечно, спеки: proposal о том, зачем удаляем, design о том, как удаляем, tasks о том, в каком порядке удаляем, и дельты REMOVED о том, что именно удалили. Чтобы выкинуть 5 тысяч строк, пришлось написать 5 тысяч строк. Это как сдать старый диван на свалку, но сначала заполнить на него техпаспорт, акт приёма-передачи и опись царапин.

А потом случился тот самый проект без фреймворка. И пазл сложился.

Основная сессия и есть дизайн

Всю механику я давно отдаю сабагентам. Основная сессия у меня превратилась в разговор: брейнштормлю фичу с агентом, и тут же рождаются дизайн и план. Отступления от плана фиксируются там же, новых changes не нужно. Если доработка разрастается, агент сам предлагает вынести её за скоуп, в non-goals. Никаких заявлений, никаких форм. Сказал «давай иначе», и стало иначе.

Выходит, источник знаний о фиче у меня уже есть, это сама сессия. Файлы нужны только на случай, если сессия потеряется или её пережуёт компакшн. Как запасной парашют: хорошо, что он есть, но прыгать с ним каждый день необязательно. Отсюда и «SDD наоборот»: не спека ведёт работу, а работа в конце оставляет после себя спеку.

Артефакты изменения: OpenSpec против SlimSpec

Артефакты изменения: OpenSpec против SlimSpec

Вокруг этой идеи я и решил собрать свой фреймворк. Взять лучшее у предшественников и сделать легче, быстрее и дешевле. Классическое «я его слепила из того, что было», только с TDD.

Как устроен SlimSpec

Внутри только скиллы и описания агентов для Claude Code. Скриптов, хуков и CLI нет, ломаться там особо нечему. Ставится и запускается так:

/plugin marketplace add yushman/slimspec
/plugin install slimspec@slimspec

/slimspec <идея>

Весь цикл на одной картинке:

Флоу SlimSpec: explore → plan → apply → verify → ground

Флоу SlimSpec: explore → plan → apply → verify → ground

Начинается всё с explore. Сабагент-сканер читает спеки модулей, потом код, и приносит короткую сводку: что тут есть, где что лежит, что может сломаться. Дальше основная сессия меня грилит: один вопрос на одно решение, независимые вопросы приходят пачкой. Ощущение как на собеседовании, только собеседуют твою же идею, и это полезно: половина «очевидных» решений на втором вопросе оказывается не такой уж очевидной. Итог ложится в explore.md: цель, non-goals, решения с отвергнутыми альтернативами, риски. Отдельных proposal и design нет, explore и есть спека изменения. К плану сессия сама не переходит, это решаю я.

В plan планировщик раскладывает работу в tasks.md по волнам. Волна делится на дорожки (lanes) из связанных задач. Дорожки одной волны трогают непересекающиеся файлы, поэтому идут параллельно. Слона едят по частям, но если частей несколько и тарелки разные, их можно есть одновременно. У каждой задачи сразу записан первый падающий тест.

Волны и дорожки в tasks.md

Волны и дорожки в tasks.md

apply короче всех: на дорожку по implementer’у, он идёт по задачам по порядку и на каждую приносит red и green. После волны основная сессия гоняет весь набор тестов, чтобы две параллельные дорожки не подрались где-нибудь на стыке.

С verify интереснее. Ревьюер сверяет результат с планом и дизайном и смотрит, что red упал по делу, а не из-за опечатки или забытого импорта. Тест, который падает с ModuleNotFoundError, это не red, это просто красный. Код-ревью он отдаёт встроенному /code-review. Баги в коде автоматически уходят обратно в apply. Вопросы к дизайну приходят ко мне через refine: он правит explore.md и возвращает в apply только инвалидированные задачи. Галя, у нас отмена, но только трёх позиций, остальные пробиваем. Если два автоматических круга исправлений не помогли, сессия зовёт меня.

Про ground ниже отдельно, там самое главное.

Остановок две: после explore и после plan. На каждой сессия показывает подробный разбор. После explore это дизайн с диаграммами «было/стало» и ссылками на path:line, после plan все волны, дорожки и тест на каждую задачу. Остальное едет само и дёргает меня только вопросами. Можно спокойно пойти за кофе. Можно даже за вторым.

Сабагенты не разговаривают с пользователем

Решения принимает основная сессия. Сабагенты работают по принципу «каждый сверчок знай свой шесток»: сделал свою часть и отчитался. Отчёт короткий и структурированный, без логов и диффов:

status: DONE | BLOCKED | FAILED
task: T3
files: [src/auth/token.ts, src/auth/token.test.ts]
decisions: ["used existing clock abstraction"]
deviations: ["renamed validate → verifyToken"]
tdd:
  red: "token.test.ts: rejects expired token — FAIL (expected 401, got 200)"
  green: "12 passed"
questions: []

Упёрся в открытый вопрос: останавливается со статусом BLOCKED. Самостоятельно ничего не додумывает и «как-нибудь сам» не решает. Основная сессия спрашивает меня и перезапускает агента с ответом. Контекст основной сессии остаётся чистым, без простыней тестовых логов, но в нём лежит каждое решение и каждый итог.

Основная сессия и сабагенты

Основная сессия и сабагенты

TDD и ревью вшиты во флоу

По-моему, TDD и код-ревью уже стали стандартом, так пусть лучше живут во фреймворке, чем в чьей-то дисциплине. Дисциплина, как известно, заканчивается в пятницу вечером. TDD проверяется не хуком, а по свидетельствам: задачу без red-прогона verify не примет. Ты не пройдёшь! (с) Гэндальф, ревьюер на полставки. Где TDD неприменим (конфиг, стили, миграции), задача помечается test: none — <причина> с альтернативной проверкой, и все такие исключения видны на гейте плана. Спрятать «ой, тут тесты не нужны» в середину списка не получится.

Два временных файла

Изменение живёт в .slimspec/changes/<slug>/, там лежат explore.md и tasks.md. Папку SlimSpec сам добавляет в .gitignore, а после ground удаляет. Отряд не заметил потери бойца, и правильно сделал: всё полезное из бойца к этому моменту уже переехало в спеки. Историю хранят коммиты и обновлённые спеки. Потерялась сессия? /slimspec <slug> продолжит с того места, где остановились.

Grounding: единственное, что осталось от SDD

Последний этап, ground, наверное, единственное, что роднит SlimSpec с классическим SDD.

Из изменения дистиллируются долговременные знания: поведение, инварианты, контракты, решения. План и процесс выбрасываются. Кому через полгода интересно, что задача T3 шла во второй волне? Правильно, никому. Дальше это вливается в спеку модуля, и здесь главное отличие от OpenSpec. Мерж идёт не по ADDED/MODIFIED/REMOVED-блокам. Его делает агент, которому понятны причины, структура и дизайн изменения. Противоречащее утверждение он переписывает, а не дописывает рядом. Иначе спека быстро превращается в «было так, но потом стало вот так, хотя смотри пункт 7». Потом агент проверяет, что обновлённая спека покрывает цель изменения и не спорит с принятыми решениями.

Grounding: дистилляция и слияние в CLAUDE.md модуля

Grounding: дистилляция и слияние в CLAUDE.md модуля

Результат ложится во вложенный CLAUDE.md нужного модуля. Claude Code сам подгружает такие файлы, когда работает в этой директории, так что спека попадает в контекст без лишних движений и ровно там, где нужна. Одна спека на модуль вместо россыпи файлов по capability. Не надо помнить, какую спеку подсунуть агенту: он зашёл в модуль, и спека уже там.

Цифры

Хватит теории, давайте мерить. На SlimSpec я перевёл два своих проекта. Замеры делал на одном из них, looqlog. Это просмотрщик логов на Rust с веб-интерфейсом, раньше он жил на OpenSpec и переехал командой /slimspec:migrate-openspec.

Миграция

У нас было 23 файла спек, 23 изменения в архиве, 200 файлов в openspec/ и 733 килобайта истории. Не то чтобы всё это было нужно агенту, но раз уж начал собирать артефакты, становится трудно остановиться.

OpenSpec

SlimSpec

Спеки поведения

23 файла, 2 941 строка, ~155 КБ

3 файла, 484 строки, ~34 КБ

Архив изменений

23 изменения, 12 394 строки, ~733 КБ

0

Корневой CLAUDE.md

130 строк

121 строка

Файлов в openspec/

200

0

Миграция looqlog: OpenSpec против SlimSpec

Миграция looqlog: OpenSpec против SlimSpec

Сценарии WHEN/THEN влились в требования, которые они и описывали. Пустые разделы Purpose и шаблонные SHALL ушли (SHALL, SHALL NOT, SHALL ALWAYS… спека звучала как устав караульной службы). Агент читает только спеку модуля, в котором работает: правка в web/ подтягивает ~11 КБ, а не капабилити-файлы со всего дерева. Через четыре изменения на SlimSpec спеки модулей подросли до 4 файлов и 641 строки. Это всё ещё на 78% меньше, чем было у OpenSpec. Спеки растут, но растут как надо: вместе с кодом, а не вместе с бюрократией.

Сколько стоит одно изменение

Считал по транскриптам Claude Code. Токены здесь означают весь вход, который обработала модель (input + чтение кэша + запись в кэш), основная сессия и сабагенты вместе. Самая близкая по размеру пара:

OpenSpec (logcat-padded-tags)

SlimSpec full (iana-tz-hash)

Изменено кода

409 строк, 4 файла, 1 модуль

435 строк, 15 файлов, 3 модуля

Обработано токенов

45,7M

29,7M (−35%)

Токенов на строку кода

~110k

~68k (−38%)

Ревью

нет

2 раунда verify на глубине high, один реальный блокер исправлен

Закоммичено процессных файлов

~530 строк

0

Время с гейтами

91 мин

56 мин

И это при том, что SlimSpec успел больше: 5 вопросов по решениям, 3 гейта, 9 задач в 3 волнах, ground в 4 спеки, ADR и оба README. Блокер, найденный ревьюером, был паникой на крайних датах. Та самая ошибка, которая в проде вылезает ровно 31 декабря в 23:59. В прогоне OpenSpec ревью не было вовсе.

Стоимость изменения в токенах

Стоимость изменения в токенах

На мелких изменениях то же самое. Два изменения на OpenSpec обработали 31,5M и 45,7M токенов, два на SlimSpec (fast и medium) 6,8M и 12,5M. Один implementer в OpenSpec в одиночку съедал 13–16M токенов, сабагент SlimSpec 0–1M: каждому достаётся одна узкая дорожка, и ему не нужно читать весь мир, чтобы поменять одну функцию.

Теперь оговорки, их лучше назвать самому, пока это не сделали в комментариях. n = 1–2 на подход, проект один. Статистики тут как в анекдоте про среднюю температуру по больнице. Мелкие изменения на SlimSpec были в 5–10 раз меньше по коду, так что часть разницы объясняется просто размером. Часть агентов SlimSpec работала на Sonnet или Haiku. А основная сессия full-прогона тащила посторонний контекст из более ранней работы, и это, наоборот, завышает её счёт. Так что цифры не бенчмарк, а наблюдение. Но наблюдение довольно громкое.

Что не взлетело по дороге

Гладко было на бумаге. Кое-что я сначала сделал иначе и потом переделывал.

Implementer на каждую задачу. Выглядело красиво: свежий агент, узкий контекст, чистота и порядок. На деле каждый холодный старт заново читал explore.md, спеки и код. Сколько задач, столько раз «здравствуйте, а что у вас тут за проект?». Теперь один implementer ведёт дорожку из связанных задач.

Worktree для изоляции в режиме full. Не взлетело, потому что worktree сабагента ответвляется от закоммиченного состояния и не видит незакоммиченных результатов прошлых волн. Вторая волна честно строила дом без первого этажа. Пришлось бы коммитить каждую волну. Оставил параллелизм по непересекающимся файлам в одном рабочем дереве.

Хотел хук, который не даёт править код без падающего теста. Звучит строго и надёжно. Но промпт-хук не может проверить, что red-прогон действительно был, а хуки плагина включены всегда, независимо от режима. Охранник, который не умеет проверять пропуска, но стоит у каждой двери. Проверка переехала в verify.

С настройкой вышла смешная история: сначала написал bash-визард. С менюшками, с вопросами, всё как у людей. Потом выяснилось, что скилл не умеет вести интерактивный скрипт, а ! в сессии работает без TTY. Визард получился красивый, но запустить его было нельзя. Сейчас /slimspec:init просто задаёт вопросы прямо в сессии.

И мелочь, которая раздражала каждый день: в explore вопросы шли строго по одному. Ждёшь круг на каждое решение, даже когда все вопросы известны заранее. Ощущение как в техподдержке: «ваш вопрос очень важен для нас, оставайтесь на линии». Теперь независимые идут пачкой, до четырёх за раз.

Чем пришлось заплатить

Ложки нашлись, но осадочек остался. Вот что ушло вместе с лишними артефактами:

  • Сценариев как готового списка тест-кейсов больше нет.

  • Обоснования отдельных изменений частично пропали. Что-то осталось в ADR, остальное только в git-истории. Если вы любите археологию по git log, вам понравится.

  • Самая большая спека модуля (~14 КБ) грузится целиком при любой работе в этом модуле.

  • Работает только с Claude Code. OpenSpec поддерживает 30+ AI-инструментов, Superpowers тоже работает со многими агентами и умеет кучу всего помимо спек: отладку, worktree, ревью. SlimSpec закрывает только spec-driven цикл и не притворяется швейцарским ножом.

Настройка под проект

Конфиг необязателен, у каждого ключа есть дефолт. Не хотите ничего настраивать? Не настраивайте, всё заведётся и так. /slimspec:init спросит всё в сессии и запишет .slimspec/config.yaml:

mode: medium              # fast | medium | full
spec:
  format: claude-md       # claude-md | readme | central | native | none
  unit: auto              # auto (по билд-файлам) | glob директорий, напр. "src/*/"
  adr: false              # дополнительно вести решения в docs/adr/NNNN-*.md
test: auto                # или явная команда
commit: none              # none | lane | wave
apply:
  agents: 3               # максимум параллельных implementer
review:
  depth: auto             # auto (по режиму) | low | medium | high
models: {}                # модели по ролям: scanner, planner, implementer, reviewer, grounder

Пара слов о том, что тут не очевидно.

mode задаёт вес цикла. В fast explore и plan живут в одном файле, вопросов не больше трёх, implementer один, ревью на low. Это для «поменять текст кнопки и не сойти с ума». medium добавляет сканер, грилинг, планировщик и параллельные дорожки, ревью на medium. full сверху ставит явный гейт на explore.md и ревью на high, для изменений, после которых хочется спать спокойно. Можно вообще не выбирать: после скана /slimspec сам предложит режим по размеру изменения.

spec.format решает, где живут спеки. По умолчанию это вложенные CLAUDE.md. Можно README.md рядом с кодом, общую папку specs/ для фич поперёк модулей, нативные доки языка (Go doc.go, Rust //!, Python docstring, KDoc) или none, тогда ground пропускается. Для тех, кто живёт по принципу «код и есть документация».

spec.unit говорит, что считать модулем; auto находит границы по package.json, go.mod, Cargo.toml, build.gradle(.kts), pyproject.toml.

commit включает коммит после каждой дорожки или волны. Коммитит только основная сессия, агенты никогда. Пушить SlimSpec не умеет в принципе, и это фича: никаких сюрпризов в main в три часа ночи.

models раздаёт модели по ролям. По умолчанию сканер и implementer на Sonnet, а планировщик, ревьюер и grounder на Opus. Думать дорого, делать дешевле, вот и вся экономика.

Если проект уже сидит на OpenSpec или Superpowers, есть /slimspec:migrate-openspec и /slimspec:migrate-superpowers. Переносятся только спеки: каждая привязывается к модулям, сжимается и сверяется с кодом. Где код расходится со спекой, решаете вы, потому что иногда врёт спека, а иногда код, и угадывать тут нельзя. Старые файлы удаляются только после вашего подтверждения.

Вместо вывода

Сильным моделям не нужны тяжёлые спеки. Им достаточно подсказать, что делать и где искать, остальное они восстановят из кода быстрее, чем дочитают четыре артефакта. Токены, которые уходят на proposal, который никто не откроет, лучше потратить на следующую фичу. А ещё лучше на новый проект. Миллион токенов туда, миллион сюда, а в итоге набегает на ещё один пет-проект, который давно лежит в списке «когда-нибудь».

Если вы тоже упирались в медленный SDD-флоу, попробуйте SlimSpec на одном изменении и сравните со своим текущим процессом. Особенно интересно, как он поведёт себя в команде и на проектах крупнее моих. Пишите в комментариях или в issues, ругать тоже можно, только с цифрами.

SlimSpec на GitHub · Сайт

Автор: yushman

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js