OpenSpec 如何透過規格先行,協助 AI 編碼智能體按需求實作
Spec Before Code: How OpenSpec Keeps AI Coding Agents Building the Right Thing
OpenSpec 是一個開源、輕量的規格驅動開發框架,作者介紹它如何先在程式碼庫中記錄需求,再讓 AI 編碼助理按規格實作。流程包括探索、提案、套用、驗證及封存,並以差異規格、設計文件和任務清單記錄每項變更。作者亦指出,若同一智能體負責實作與驗證,可能重複相同誤讀,建議以具體情境、獨立測試及 CI 降低風險。
Over the past few months I've changed a lot about how I work with coding agents, and one of the tools that made the biggest difference was OpenSpec.
The problem it solves is simple. When you ask an AI for something straight in the chat, the requirement only exists in that conversation. The agent reads it its own way, and you often find out it misunderstood after 400 lines of code. Close the session and the context is gone with it.
OpenSpec flips the order: before any code gets written, you and the agent agree on what will be built, in writing, inside the repository.
This post covers how OpenSpec is organized, what the workflow looks like with a concrete example, when it's worth the extra step, a weakness in the verification step that came up in a great discussion on LinkedIn, and how it relates to ADRs.
What OpenSpec is
OpenSpec is an open source (MIT), lightweight framework for spec-driven development with AI coding assistants. It works with Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode and 30+ other tools, through slash commands the agent understands.
It isn't a new IDE or a platform you have to move to. It's a folder of Markdown files in your repository, a small CLI, and a set of instructions for your agent.
Installing it takes two commands:
npm install -g @fission-ai/openspec@latest
cd your-project
openspec init
openspec init asks which tools you use and installs the matching commands and instructions for each of them.
How it's organized
Everything lives in an openspec/ folder, split in two:
openspec/
├── specs/ # Source of truth: how the system behaves today
│ ├── auth/
│ │ └── spec.md
│ └── payments/
│ └── spec.md
└── changes/ # Proposed modifications, one folder each
├── add-password-reset/
│ ├── proposal.md # Why and what
│ ├── design.md # How (technical approach)
│ ├── tasks.md # Implementation checklist
│ └── specs/
│ └── auth/
│ └── spec.md # Delta: what changes in auth
└── archive/ # Finished changes, kept for history
-
specs/describes how the system currently behaves, organized by domain. -
changes/holds proposed modifications. Each change is a self-contained folder, so two changes can be in progress at the same time without stepping on each other.
The key idea is the delta spec. A change doesn't restate the whole spec. It only describes what is being added, modified or removed. That makes OpenSpec work well on existing codebases, which is where most of our work actually happens.
The workflow
In practice, the flow looks like this:
-
Explore (
/opsx:explore): the agent reads the codebase and helps you understand the problem and the options. No code, no commitment. -
Propose (
/opsx:propose <name>): it drafts the change folder: proposal, delta specs, design and tasks. - You review it. This is the most important step. Fixing a paragraph is far cheaper than fixing a pull request.
-
Apply (
/opsx:apply): the agent implements the tasks, checking them off as it goes. -
Verify (
/opsx:verify): it checks that the implementation matches the spec. This one is part of the expanded workflow profile, which you enable withopenspec config profile. -
Archive (
/opsx:archive): the deltas are merged intospecs/, and the change folder moves tochanges/archive/with a date prefix.
After archiving, the spec describes the system as it is now, and the archived change keeps the full story of why it got that way.
The steps aren't rigid gates. You can go back and update the proposal or the design at any point when you learn something during implementation. That flexibility is one of the things I like most about it compared with heavier spec frameworks.
A concrete example: password reset
Say you want users to reset their password by email, with a link that expires in 15 minutes. Here's what the main artifacts look like.
proposal.md captures the intent and, just as important, the scope:
# Proposal: Add password reset by email
## Intent
Users who forget their password currently need to contact support.
Let them reset it themselves with a secure, short-lived link.
## Scope
In scope:
- "Forgot password" request from the login screen
- Single-use reset link sent by email, valid for 15 minutes
- Revoking active sessions after a successful reset
Out of scope:
- SMS or authenticator-based recovery
- Changes to the sign-up flow
The delta spec describes observable behavior with concrete scenarios:
# Delta for Auth
## ADDED Requirements
### Requirement: Password reset by email
The system MUST let a user reset their password through a single-use
link sent to their registered email, valid for 15 minutes.
#### Scenario: Valid reset link
- GIVEN a user requested a password reset 5 minutes ago
- WHEN they open the link and submit a new valid password
- THEN the password is updated
- AND all of the user's active sessions are revoked
- AND the link can no longer be used
#### Scenario: Expired link
- GIVEN a reset link created 16 minutes ago
- WHEN the user opens it
- THEN the reset is refused with a "link expired" message
- AND the password is unchanged
#### Scenario: Unknown email
- GIVEN no account exists for the submitted email
- WHEN a reset is requested
- THEN the response is identical to the one for an existing account
- AND no email is sent
That last scenario is a good example of why the review step matters. "Don't reveal whether an account exists" is easy to forget in a prompt, and it's a security requirement. Written as a scenario, it's visible, reviewable and testable.
design.md holds the technical approach (how tokens are generated and stored, rate limiting, which email service), and tasks.md is the checklist the agent follows during Apply:
## 1. Reset tokens
- [ ] 1.1 Generate single-use tokens with a CSPRNG; store only their hash
- [ ] 1.2 Expire tokens after 15 minutes; invalidate on use
- [ ] 1.3 Tests for valid, expired and reused tokens
## 2. API
- [ ] 2.1 POST /password-reset/request with identical responses for unknown emails
- [ ] 2.2 POST /password-reset/confirm; revoke sessions on success
- [ ] 2.3 Rate limit both endpoints per IP and per account
The spec says what the system must do. The design and tasks say how. Keeping those apart is what lets you review the behavior without getting lost in implementation details.
What to look for when you review
Since the review is the step that pays for everything else, it's worth being deliberate about it. These are the questions I ask:
- Is the scope right? Is anything in scope that shouldn't be, or missing from the out-of-scope list?
- Are the scenarios concrete? "The link expires" is vague. "A link created 16 minutes ago is refused" is testable.
- Are the edge cases there? Errors, empty states, abuse, permissions, concurrency.
- Does anything conflict with existing decisions? A new library, a new pattern, a different way of storing data.
- Could someone write a test from each scenario? If not, it's still too ambiguous.
When it's worth using
- Features that touch several parts of the system. The more the work spreads across the codebase, the more expensive a late misunderstanding gets.
- Existing codebases. The agent needs to understand what's already there before changing it, and delta specs are designed for exactly that.
- Teams with more than one person (or more than one agent) on the same code. Everyone works from the same document, not from different chat histories.
- Requirements that are still moving. Updating the spec and regenerating the tasks is easier than rewriting code.
And when it isn't: one-line bug fixes, small visual tweaks, throwaway scripts. There, the process gets in the way more than it helps. OpenSpec's own docs push in the same direction: most changes should use a light spec, and the full treatment is for higher-risk work like API contracts, migrations and security changes.
My rule of thumb: if I'd have to explain the task to a teammate before they started, it deserves a spec.
The Verify trap: who checks the checker?
When I posted about OpenSpec on LinkedIn, someone asked a question I think every team using spec-driven development should think about:
If the same agent that implemented the tasks also verifies the implementation against the spec, what stops it from misreading the spec the same way in both steps?
The concern is real. If the same agent, with the same context, implements and then verifies, it tends to repeat the same misreading. The Verify step is good at catching what was left out or not done. It doesn't guarantee the interpretation was right in the first place.
What you can do to reduce that risk:
- Make the scenarios concrete. The more specific they are, with real input and output values, the less room there is for interpretation. The spec review is the moment to remove ambiguity.
- Turn the scenarios into tests before Apply. Essentially acceptance tests, written by a person or by a separate agent, based on the spec and not on the implementation. Each GIVEN/WHEN/THEN maps naturally to a test.
- Keep those tests hidden from the implementing agent. Run them only in CI, or block read access to that folder in the agent's permissions. Like a held-out evaluation set, this stops the agent from writing code just to make the tests pass.
- Verify in a clean session, with no history from the implementation. Using a different model can help too. OpenSpec's own docs recommend clearing the context before implementation, and the same hygiene applies to verification.
- Let CI have the final word. The tests running in the pipeline decide, not the agent saying everything matches.
The discussion went one step further: if someone outside the team, like a customer or an auditor, doubted a green run, how would they know the hidden tests weren't edited after the code was written, and that they ran against that exact build? The short answer involves locking the tests before Apply, pinning their commit in CI, and signed build provenance (SLSA, Sigstore). It's a big enough topic that I'll cover it in a separate post.
OpenSpec and ADRs
A question I get often: if I already write ADRs, do I need specs? They solve different problems and work well together.
| ADR | OpenSpec | |
|---|---|---|
| Answers | Why did we decide this? | What should the system do? |
| Unit | One decision | One change or capability |
| Lifecycle | Immutable; superseded by a new ADR | Living; updated with every archived change |
| Leads to implementation? | No | Yes: tasks, apply, verify |
| Horizon | Long term, history | The current change and the current state |
They meet in design.md. A change's design often contains technical decisions. If a decision only matters for that feature, it can stay there. If it goes beyond the feature and affects the whole project (switching databases, changing the authentication approach, adopting a queue), write an ADR and link it from the design.
A practical rule: if someone will want to know in two years why something was done this way, write an ADR. If you want the agent to build the right thing now, write a spec.
I wrote more about ADRs and AI agents in ADRs Are the Guardrails Your AI Coding Agent Is Missing.
Getting started
- Install the CLI and run
openspec initin an existing project. - Pick a real feature of medium size, not a one-line fix and not a rewrite.
- Start with
/opsx:exploreand let the agent map the code before proposing anything. - Run
/opsx:propose, then spend real time on the review. Rewrite vague scenarios until each one could become a test. - Apply, verify, archive, and look at how
specs/changed.
After one or two changes, you'll have a feel for which work deserves a spec and which doesn't.
Conclusion
AI agents are very good at building what you asked for. The hard part is making sure what you asked for is what you actually meant. OpenSpec moves that agreement out of a chat window and into the repository, where both people and agents can read it, review it, and come back to it later.
It isn't magic, and the verification step needs help from good tests to be trustworthy. But writing the spec before the code is one of the habits that improved my work with agents the most.
How about you? Do you write the spec before asking the AI for code, or do you still go straight to the prompt?
來源:DEV Community · dev.to