<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Archlint on Михаил Шогин</title><link>https://mshogin.ru/tags/archlint/</link><description>Recent content in Archlint on Михаил Шогин</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>Михаил Шогин</copyright><lastBuildDate>Wed, 29 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://mshogin.ru/tags/archlint/index.xml" rel="self" type="application/rss+xml"/><item><title>Спека как живой контракт: SPDD, archlint и двусторонний sync</title><link>https://mshogin.ru/notes/spdd-and-archlint-living-spec/</link><pubDate>Wed, 29 Apr 2026 00:00:00 +0000</pubDate><guid>https://mshogin.ru/notes/spdd-and-archlint-living-spec/</guid><description>&lt;h2 id="симптом"&gt;Симптом
&lt;/h2&gt;&lt;p&gt;LLM в кодовой базе ускоряет одного разработчика. Команда от этого не ускоряется — наоборот, на ревью обрушивается поток сгенерированного кода, который никто не успевает понять. Спека, написанная на старте задачи, через неделю уже не описывает то, что в master. Кто-то правит код, кто-то правит .md, через месяц расхождение замолчано.&lt;/p&gt;
&lt;p&gt;Это не про дисциплину команды. Это про архитектуру процесса, в которой &amp;ldquo;спека&amp;rdquo; и &amp;ldquo;код&amp;rdquo; не имеют формальной связи.&lt;/p&gt;
&lt;h2 id="диагноз-от-thoughtworks"&gt;Диагноз от Thoughtworks
&lt;/h2&gt;&lt;p&gt;28 апреля 2026 на сайте Мартина Фаулера вышла статья &lt;a class="link" href="https://martinfowler.com/articles/structured-prompt-driven/" target="_blank" rel="noopener"
&gt;Structured Prompt-Driven Development&lt;/a&gt; от Wei Zhang и Jessie Xia. Они формулируют проблему точнее, чем я: &amp;ldquo;Local speed improves. But that doesn&amp;rsquo;t automatically translate into system-level throughput.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Их предложение в трёх пунктах:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Промпт — это first-class артефакт.&lt;/strong&gt; Версионируется, ревьюется, переиспользуется как код. Не реплика в чате.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REASONS Canvas&lt;/strong&gt; — 7-частная структура промпта: Requirements, Entities, Approach, Structure, Operations, Norms, Safeguards. Первые четыре — абстракция, пятый — исполнение, последние два — governance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Замкнутая петля между промптом и кодом&lt;/strong&gt; с правилом, что обновлять первым:
&lt;ul&gt;
&lt;li&gt;изменение поведения -&amp;gt; сначала промпт, потом код&lt;/li&gt;
&lt;li&gt;рефакторинг -&amp;gt; сначала код, потом sync обратно в промпт&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Третий пункт — главное. Он превращает вечный спор &amp;ldquo;что source of truth&amp;rdquo; в процедуру.&lt;/p&gt;
&lt;h2 id="где-это-совпало-с-моей-практикой"&gt;Где это совпало с моей практикой
&lt;/h2&gt;&lt;p&gt;У меня уже был свой spec-driven workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/my/archlint/templates/specifications/spec-template.md&lt;/code&gt; — шаблон спеки с разделами Architecture, Requirements, Acceptance Criteria, Implementation Steps. Размерные градации XS/S/M/L/XL.&lt;/li&gt;
&lt;li&gt;Правило в &lt;code&gt;~/.claude/rules/spec-workflow.md&lt;/code&gt;: &amp;ldquo;задача &amp;gt;30 минут или &amp;gt;3 шагов — обязательно спека до старта&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Команда &lt;code&gt;/ms-add-spec&lt;/code&gt; в Claude Code, которая генерирует спеку под задачу.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;archlint&lt;/code&gt; — статический анализатор, который валидирует архитектурные правила (200+) на графе из AST.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;REASONS Canvas почти один в один ложится на мой шаблон:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;REASONS&lt;/th&gt;
&lt;th&gt;Мой шаблон&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;R Requirements&lt;/td&gt;
&lt;td&gt;## Requirements (FR1, FR2, &amp;hellip;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E Entities&lt;/td&gt;
&lt;td&gt;## Architecture / Data Model (UML Class)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A Approach&lt;/td&gt;
&lt;td&gt;## Overview / Solution Summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S Structure&lt;/td&gt;
&lt;td&gt;## Architecture / Component Overview (C4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O Operations&lt;/td&gt;
&lt;td&gt;## Implementation Steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N Norms&lt;/td&gt;
&lt;td&gt;(не было отдельного раздела)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S Safeguards&lt;/td&gt;
&lt;td&gt;(не было отдельного раздела)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Совпадение на 70-80%. Архитектурный governance у меня вообще сильнее — &lt;code&gt;archlint&lt;/code&gt; проверяет автоматически то, что в SPDD держится на ревьюере-человеке.&lt;/p&gt;
&lt;h2 id="где-spdd-добавил-то-чего-не-было"&gt;Где SPDD добавил то, чего не было
&lt;/h2&gt;&lt;p&gt;Два пункта, которые у меня отсутствовали или были сформулированы неверно.&lt;/p&gt;
&lt;h3 id="1-спека-неизменна-в-процессе"&gt;1. Спека &amp;ldquo;неизменна в процессе&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;В моём &lt;code&gt;spec-workflow.md&lt;/code&gt; буквально стояла формулировка:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Спека = ЧТО делать (неизменна в процессе)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Это противоположно идее живого артефакта. Спека написана, согласована, перенесена в &lt;code&gt;inprogress/&lt;/code&gt; — и потом никем не трогается, пока задача не закроется. На практике это значит, что любая правка реализации, отличающаяся от плана, проваливается между спекой и кодом.&lt;/p&gt;
&lt;h3 id="2-norms-и-safeguards-неявные"&gt;2. Norms и Safeguards неявные
&lt;/h3&gt;&lt;p&gt;Стандарты команды (как именно мы оборачиваем ошибки, какой logger, какие naming-конвенции) у меня живут в куче .md файлов: &lt;code&gt;~/.claude/rules/architecture.md&lt;/code&gt;, проектные CLAUDE.md, в чате. На уровне отдельной спеки их явно никто не выносит. Авторы SPDD выделяют:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Norms&lt;/strong&gt; — стандарты как принято писать. Часть автоматизирована (golangci-lint, archlint), часть нет.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safeguards&lt;/strong&gt; — неприкосновенные инварианты. Нарушение — стоп, не warning. Обратная совместимость, perf budgets, security boundaries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Когда они в одной странице со спекой, тихий рефактор поперёк стандарта становится заметным на ревью.&lt;/p&gt;
&lt;h2 id="что-я-в-итоге-сделал"&gt;Что я в итоге сделал
&lt;/h2&gt;&lt;p&gt;Два патча, оба сегодня.&lt;/p&gt;
&lt;h3 id="патч-1-clauderulesspec-workflowmd"&gt;Патч 1: &lt;code&gt;~/.claude/rules/spec-workflow.md&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;Удалил &amp;ldquo;Спека неизменна&amp;rdquo;. Заменил на &amp;ldquo;Спека — живой контракт, синхронизирован с кодом всегда&amp;rdquo;. Добавил раздел &amp;ldquo;Двусторонняя синхронизация&amp;rdquo; с явным правилом prompt-first vs code-first:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Изменение поведения (publicly-exposed контракт, бизнес-логика, валидация)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; сначала спека, потом код
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Рефакторинг (поведение не меняется)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-&amp;gt; сначала код, потом sync обратно в спеку
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;И отдельный раздел &amp;ldquo;Norms и Safeguards: governance внутри спеки&amp;rdquo; с примерами и привязкой к archlint/golangci-lint.&lt;/p&gt;
&lt;h3 id="патч-2-myarchlinttemplatesspecificationsspec-templatemd"&gt;Патч 2: &lt;code&gt;~/my/archlint/templates/specifications/spec-template.md&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;Добавил две секции после Acceptance Criteria:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Norms
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; N1: error wrapping через fmt.Errorf(&amp;#34;%w&amp;#34;) или errtrace.Wrap
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; N2: логирование через slog с контекстом
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; N3: naming - DB snake_case, JSON camelCase
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Автоматизировано:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; golangci-lint passes (errcheck, errorlint, sloglint)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; archlint passes (layered, fan-out, ISP)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Safeguards
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; S1: обратная совместимость публичного API в рамках minor релиза
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; S2: p99 latency endpoint X не растёт более чем на 10%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; S3: нет утечки PII в логи и метрики
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Проверка:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; Smoke test на обратную совместимость
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; Бенчмарк до/после
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;- [ ]&lt;/span&gt; Аудит логов на чувствительные поля
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Размерные градации те же: для XS Safeguards обычно не нужны, для M/L/XL — обязательны.&lt;/p&gt;
&lt;h2 id="что-я-не-взял"&gt;Что я НЕ взял
&lt;/h2&gt;&lt;p&gt;Не взял CLI &lt;code&gt;openspdd&lt;/code&gt; и команду &lt;code&gt;/spdd-prompt-update&lt;/code&gt;. Слишком много церемонии для одиночной работы и для команды без отдельного workflow-инженера. У меня та же дисциплина держится на:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/ms-add-spec&lt;/code&gt; — создать спеку&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/ms-checkpoint&lt;/code&gt; — зафиксировать прогресс на длинной задаче&lt;/li&gt;
&lt;li&gt;&lt;code&gt;archlint&lt;/code&gt; — валидация Norms+Safeguards автоматически где можно&lt;/li&gt;
&lt;li&gt;Code review через GitLab — двусторонний sync проверяется на ревью&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Не взял правило &amp;ldquo;никогда не редактировать промпт руками&amp;rdquo;. У них это compensation за CLI-managed lifecycle. У меня его нет, и руками править .md спеку — нормальная операция.&lt;/p&gt;
&lt;p&gt;Не взял заявление про &amp;ldquo;~99% intent alignment&amp;rdquo;. Это маркетинг, кейс N=1.&lt;/p&gt;
&lt;h2 id="двойная-оптика"&gt;Двойная оптика
&lt;/h2&gt;&lt;p&gt;Технический слой статьи: дисциплина для AI-кодинга в команде.&lt;/p&gt;
&lt;p&gt;Архитектура мышления за этим: проблема, которую решают авторы — не &amp;ldquo;AI пишет неправильный код&amp;rdquo;, а &amp;ldquo;bandwidth ревьюера не растёт со скоростью генератора&amp;rdquo;. Когда LLM выдаёт результат быстрее, чем человек успевает его понять, узкое место — когнитивная пропускная способность того, кто принимает изменения. SPDD сжимает то, на чём держится ревью, до одного артефакта (Canvas), чтобы он влезал в bandwidth.&lt;/p&gt;
&lt;p&gt;Это та же задача, про которую я говорил на Стачке — контроль сложности через граф. Только на уровне процесса, а не статического анализа.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;archlint&lt;/code&gt; = governance внутри кода (граф, правила, инварианты).
SPDD/спека = governance над изменениями (контракт между намерением и реализацией).&lt;/p&gt;
&lt;p&gt;Эти две вещи комплементарны, не конкурируют. Если у тебя есть один без другого, ты получаешь либо красивые архитектурные правила без контракта на изменения, либо пухлые спеки без автоматической проверки.&lt;/p&gt;
&lt;p&gt;Когда есть оба — каждый коммит проходит и через &amp;ldquo;не сломал ли архитектуру&amp;rdquo; (archlint), и через &amp;ldquo;соответствует ли намерению&amp;rdquo; (спека). На ревьюера падает только то, что обе автоматизации пропустили.&lt;/p&gt;
&lt;h2 id="ссылки"&gt;Ссылки
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Оригинал статьи: &lt;a class="link" href="https://martinfowler.com/articles/structured-prompt-driven/" target="_blank" rel="noopener"
&gt;Structured Prompt-Driven Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openspdd&lt;/code&gt; (Thoughtworks-affiliated CLI): &lt;a class="link" href="https://github.com/gszhangwei/open-spdd" target="_blank" rel="noopener"
&gt;https://github.com/gszhangwei/open-spdd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;archlint: &lt;a class="link" href="https://github.com/mshogin/archlint" target="_blank" rel="noopener"
&gt;https://github.com/mshogin/archlint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>