AI

Understanding OpenSpec & Spec-Driven Development

Why spec-driven development turns AI coding agents from unpredictable chatbots into good programming assistants, and how OpenSpec makes it work.

Fotis Adamakis
Fotis Adamakis
Software Engineer / Technical Writer
10 min read
June 23, 2026

Every codebase has two stories.

The first one is easy to find. It lives inside the codebase. Every commit, diff, rename, and deleted line is still there if you know where to look.

The second one is harder to recover. It is the history of why the system changed.

Why was this endpoint designed this way? Why is this rule enforced in the service layer? Why did we reject the simpler implementation? Why does this constraint exist at all?

That history usually lives everywhere except the codebase: in tickets, slack threads, meeting notes, pull request comments, and the memory of whoever happened to be there at the time.

OpenSpec is built around a simple idea:

the intent behind a change should be versioned as carefully as the code itself.

It gives every system a living specification, every proposed change a reviewable shape, and every shipped change a permanent place in the project history. Before implementation starts, you define what is changing, why it is changing, what must stay true, and what’s the definition of Done.

That makes OpenSpec useful especially when AI coding agents enter the workflow. Because once the intent is explicit, the implementation is something easy to follow.

Three things break very often:

  • Unstable context. The agent forgets what you told it three prompts ago, or hallucinates a different project structure on each run.
  • Uncontrollable output. You ask for a small refactor and get a sweeping rewrite that touches twenty files.
  • No engineering hooks. There is no way to put this workflow in front of CI, in a code review, or in front of a teammate. It is a chat log, not a system.

Openspec is a small, open-source tool that fixes all three. It does not replace your AI agent. It puts a disciplined spec layer in front of it so the agent has somewhere to anchor its work, and you have something reviewable to push back against.

The short version. OpenSpec determines the boundaries. The agent is responsible for execution.

What OpenSpec actually is

OpenSpec is a spec-driven development (SDD) implementation tool. Not a framework, not an agent, not a runtime. It is a workflow and a folder structure that turns specifications into the single source of truth for what your system is, and what it is becoming.

Specs get written, sort of. They live in Notion, in Confluence, in the bottom of a Jira ticket, in the README nobody has opened in six months. The problem is often outdated specs which leads to many issues:

  • Overall intent gets lost. New contributors cannot see the shape of the system.
  • Feature overalaps go undetected. Two specs that look fine in isolation break each other in practice.
  • Validation is impossible. The live system has no way to check whether the spec still matches reality.
  • No shared map. Nobody, human or AI, can reason about the whole thing consistently.

OpenSpec fixes this by collapsing the spec into one living document per system, plus a queue of well-defined changes flowing through it. The agent reads the spec. The agent edits the spec. The agent implements the spec. Every step is checkable.

OpenSpec workflow: proposed, approved, applied, archived

The mental model

Contract and Contractor

The cleanest way to think about OpenSpec is as a contract. The agent is the contractor.

A contractor who shows up with no contract will build whatever they think you meant. A contractor with a blueprint knows what to deliver, what is forbidden, and what “done” looks like. OpenSpec is the contract. The agent is the contractor.

The contract lives in a structured spec file that the agent reads before doing anything. A simplified example, for a Java/Spring Boot service:

project:
name: user-service
language: Java
framework: Spring Boot 3.x
rules:
- "Services cannot access the database directly. Use the Repository layer only."
- "Controllers are responsible for parameter validation only. No business logic."
- "All public interfaces return Result<T> for consistent error handling."
- "No new third-party dependencies without an ADR."
- "Forbidden: storing PII in logs, synchronous calls to external services, raw SQL outside the Repository layer."

That file is the engineering manual for the agent. It defines the stack, the architecture, the boundaries, the things the agent is allowed to do, and the things it is forbidden from doing. The agent reads it on every task. If it suggests something that violates the rules, the violation is visible in the spec, not buried in a chat log.

The workflow

Propose → Apply → Archive loop

This loop is the core idea of OpenSpec. It splits writing code into three phases that mirror how experience engineers work: agree on what we are building, build it, make the decision permanent.

Every change goes through three slash commands the tool injects into your agent:

  • /openspec:proposal <change-name> - Describe what you want. The agent writes a change folder. No code yet.
  • /openspec:apply <change-name> - Only after the spec is approved, the agent writes code. Strictly following the spec.
  • /openspec:archive <change-name> - Once merged, the change is folded into the main spec. The spec stays in sync with reality.

The proposal step produces four files in openspec/changes/<change-name>/:

openspec/changes/<id>/
├── proposal.md ← why (problem, motivation, scope)
├── design.md ← technical decisions / approach
├── tasks.md ← implementation checklist
└── specs/ ← spec deltas (NOT a single spec.md)
└── <capability>/
└── spec.md

A working example

Let’s add a “list users” endpoint to the service.

Step 1: Propose.

Terminal window
/openspec:proposal Add user list API

The agent reads the existing spec, then comes back with questions before writing a single file. Authentication: cookie, bearer, or both? Pagination: cursor or offset? Sort fields: which ones, and what does an invalid value return? Empty result set: 200 with [], or 404? The agent does not guess on anything that would change the public interface. Once the ambiguities are resolved, it produces proposal.md, design.md, tasks.md, and specs/<capability>/spec.md under openspec/changes/add-user-list-api/. Nothing else. The endpoint is described as an interface, not as a Python function.

If you are familiar with the popular /grill-me skill. It works the same way.

Step 2: Review.

Terminal window
openspec list
openspec show add-user-list-api
openspec validate add-user-list-api

You read the specs the way you would read a PR description. The interface is clear. You catch the missing pagination, the missing sort parameter, the unclear error case. You push back:

Add pagination with cursor-based paging, default page size 20, max 100. Add sort and order query params. Return 400 with a structured error when sort is invalid.

The agent updates the specs only. It does not touch code, does not touch tasks, does not “get a head start” on implementation. This is the discipline.

Step 3: Apply.

Terminal window
/openspec:apply add-user-list-api

Now the agent writes the code, strictly following the tasks.md. Each task is a checkbox. Each gets a status as it completes. You can stop the run after any task, inspect the diff, and resume.

Step 4: Archive.

Terminal window
/openspec:archive add-user-list-api

The change is merged into the main spec at openspec/specs/. The change folder is moved to openspec/archive/. The spec now reflects the system as it actually is. Six months from now, when someone asks “why does the user list API work this way?”, the archived change folder answers the question.

The split between proposal and apply is the part you skip when you wing it with an AI agent. It feels slow on day one. It is the only thing keeping the system coherent on day two hundred.

Delta specs and the source of truth

A subtle design choice makes this loop work for both humans and AI: the spec is never edited directly during a change. Instead, OpenSpec uses delta specs.

A delta is a small document that says, in plain language, what is changing:

  • ADDED Requirements. New behaviour the system gains.
  • MODIFIED Requirements. Behaviour that changes shape.
  • REMOVED Requirements. Behaviour that goes away.

Deltas are easy for a human to read at a glance, and trivial for an LLM to parse. You never need to diff the whole spec to see what is happening in a given change. On the archive step, the deltas are merged into the main spec at openspec/specs/, and the change folder moves to openspec/archive/ as a permanent record.

This gives you two things at once:

  • A living source of truth that always reflects the current system.
  • A full audit trail of how the system got here. Every architectural choice, every API contract change, every “we used to do X but switched to Y” is searchable in archive/.

For AI agents, deltas are a forcing function. The model is not asked to reason about a giant document and figure out what is new. It is asked to read a delta that is a few hundred words long, then merge it. That is a much smaller, much more reliable task.

Going further: worktrees, subagents, and ADRs

Once the basic loop is in place, two extensions make OpenSpec work for teams shipping in parallel.

Git Worktrees + subagents. The pattern is simple. Propose all upcoming changes on main first, so the spec stays consistent. Then apply each change in its own worktree, via a subagent. Each subagent runs validation before merge. Changes come back, get merged in order, and get archived. The source-of-truth spec updates once, at the end, when the dust has settled. Several change streams move at once without the spec drifting out from under the team.

ADRs alongside specs. Specs describe what the system does. They do not say why a particular architecture was chosen. OpenSpec supports a spec-driven-with-adr schema that adds Architectural Decision Records next to the main spec. Each ADR captures context, options considered, the decision, and the consequences, and persists through archive. A year from now, the ADR answers “why is this service structured like this?” while the spec answers “what does this service do?”

Both extensions keep decisions discoverable instead of buried in Slack threads or in the head of whoever happens to remember.

How this is different from a CLAUDE.md?

This is the first question I got when introducing OpenSpec to my team, and it is fair. A CLAUDE.md or .cursorrules file at the repo root is the obvious first attempt at giving an agent context. OpenSpec is doing something different.

A context file is static and read-only. You write it once, the agent reads it, and over time it drifts from how the system actually behaves. There is no validation step or notion of “this rule applies only to change X.” A wall of text the model reads every time.

OpenSpec is structured, validated, and accumulating. Specs are diffed against the implementation. Changes flow through a loop you can review, like code. Decisions accumulate in archive/. The contract gets sharper over time, not blurrier.

It also sits in a different spot from GitHub Spec Kit or Kiro, which are heavier, opinionated frameworks. OpenSpec is closer to a workflow than a framework. It works with Claude Code, Cursor, OpenCode, or any agent that can read a folder and run a CLI. You can adopt it in one afternoon and keep your existing tools.

Drawbacks

OpenSpec will slow you down a bit. The spec layer adds an overhead. You are writing things down before writing code, reviewing them, and maintaining them. The question is when that cost pays off.

It pays off when:

  • The project is brownfield. A new spec captures the current state of the system without forcing a rewrite. New changes flow in cleanly.
  • The change is real. Multi-file work, new endpoints, schema migrations, anything a junior engineer would need a design doc for. This is exactly where AI agents hallucinate the most.
  • The team is large enough to need coordination. When two people cannot fit the whole system in their head, a living spec gives them a shared map.
  • You are using AI agents heavily. Solo founders, small teams with one experienced engineer, anyone for whom the agent is doing 30%+ of the code. The agent needs a contract more than a human does, because the agent has no memory between sessions.

It does not pay off for:

  • One-line bug fixes.
  • Throwaway prototypes that will be deleted in a week.
  • Solo projects so small you can keep the whole system in your head and never touch an AI agent for them.

The honest framing: the value scales with change size, team size, and how much you lean on AI. For a three-file weekend script, OpenSpec is overkill. For a service that three engineers ship to every day, with an agent in the loop, it is the difference between a system that drifts and a system that compounds.

From chatbot to a contractor

The interesting part of OpenSpec is not the tool. It is the change in mindset. You stop treating the agent as a chatbot that might hand you a working solution. You start treating it as a contractor who needs a blueprint, a review, and an archive step. The work changes from “ask the AI and copy the answer” to “agree on the spec, watch the agent execute against it, and update the spec when we are done.”

That is the same loop we have always used with junior engineers, with contractors, with ourselves. Spec, build, archive. The only new thing is that the executor is a model instead of a person, and the loop is now fast enough to run dozens of times a day.

The bottleneck was never typing the code.

Fotis Adamakis

Software Engineer / Technical Writer

Fotis Adamakis

Experienced software engineer and tech lead at Preply. Writing about front end architecture, accessibility, system design, and developer productivity. Lessons from building and maintaining large-scale frontend applications, with a focus on practical patterns that make codebases easier to understand, scale, and evolve.

Based inBarcelona, Spain 🇪🇸