OpenSpec is a Spec-centric engineering methodology and toolchain designed to improve the determinism and verifiability of complex systems throughout the full cycle from design to delivery, through unified structured documents and automated workflows. This guide uses a complete small e-commerce website case study to demonstrate the full process — from architecture design, system design, and module design, to interface design, unit testing, integration testing (covering interface-layer acceptance), and performance testing — in order to facilitate adoption in real-world projects.
Further Reading: For a deep retrospective on the AI collaboration workflow for this case (prompt design and interaction records) and cross-language replication validation in Python, see OpenSpec Practical Guide: Deep Retrospective on AI-Assisted Software Engineering.
CLI Reference Manual: For the complete command reference for the OpenSpec CLI (init, validate, archive, etc.), see OpenSpec User Manual.
Training Materials: This project includes companion presentation slides OpenSpec User Manual, suitable for team training and technical sharing.
OpenSpec is not merely a document format — it is an engineering practice of Spec-Driven Development. It advocates “spec as the source of truth”, ensuring that code and tests always remain consistent with the design, solving the persistent problem in traditional development where “documentation lags behind the code”.
Terminology Note: In this document “spec” and “Spec” are used interchangeably, both referring to the document structure defined by OpenSpec; “specification” and “spec” may also be used interchangeably without distinction.
OpenSpec’s design follows four core principles:
OpenSpec divides project state into two core areas, ensuring separation between “current state” and “the change process”:
Project Configuration (openspec/config.yaml)
A project-level configuration file that declares the Schema type (spec-driven), project context (tech stack, architectural constraints, etc.), and rules for each document type. This configuration is injected into every AI planning request.
Source of Truth (openspec/specs/)
This is the description of the system’s current actual behavior. All released features must have a corresponding spec definition here. The directory structure is organized by Capability, e.g., catalog-management/spec.md, payment/spec.md.
Proposed Changes (openspec/changes/)
This is for in-progress changes. Each change is an independent folder (e.g., openspec/changes/add-login/) containing complete context:
.openspec.yaml): The change’s Schema type and creation time, managed automatically by the CLI.proposal.md): Why & What. Describes the background, intent, scope, and capability list.design.md): How. Technical solution, architecture diagrams, and data flow.tasks.md): Steps. Specific implementation steps and acceptance criteria.When a change is developed and archived, its Delta Spec is merged into the main Spec, forming the new source of truth.
OpenSpec provides two primary workflow modes: CLI and Slash Commands (AI collaboration).
The CLI (openspec) is suited for management operations by human developers:
init): Generate the standard directory structure in one step, configure the .openspec environment.list / view): Quickly retrieve existing changes and specs.validate): Check the validity of document structure.archive): Move completed changes to the archive area.OpenSpec 1.0+ introduced the OPSX workflow, implementing a dynamic command system — AI no longer receives static instructions but actively queries the CLI to understand the current project state and document dependencies.
In supported AI editors (such as Cursor, Claude Code, Qoder, Windsurf, Junie, Lingma IDE, ForgeCode, and 20+ other tools), it is recommended to drive development using Slash Commands:
Default Core Configuration:
| Command | Purpose |
|---|---|
/opsx:propose <description> |
Create a change and generate all planning documents in one step (proposal/design/specs/tasks) |
/opsx:explore |
Enter exploration mode — think through problems and investigate the codebase without writing code |
/opsx:apply |
Implement tasks according to tasks.md, dynamically reading the current change context |
/opsx:archive |
Complete and archive the current change, prompt whether to sync Delta Spec to the main spec |
Extended Workflow Commands (enabled via openspec config profile)
| Command | Purpose |
|---|---|
/opsx:new |
Initialize the change directory structure only (no documents created) |
/opsx:continue |
Create the next document in dependency order (step-by-step mode) |
/opsx:ff |
Fast-forward to generate all planning documents at once |
/opsx:verify |
Verify that the implementation is consistent with the spec |
/opsx:sync |
Merge Delta Spec into the main spec (without archiving) |
/opsx:bulk-archive |
Bulk-archive multiple completed changes |
/opsx:onboard |
15-minute full-process guided walkthrough |
The key difference between the OPSX workflow and the old version is: actions, not phases. You can edit any document at any time — there is no phase locking.
Structured Validation
OpenSpec uses structured schemas (e.g., Zod) internally to validate Spec documents, ensuring that documents are not merely text but structured data conforming to a defined schema. This makes automatic Test Case Generation possible. Users only need to run openspec validate to trigger this validation, with no need to write schemas manually.
Telemetry
Built-in optional anonymous telemetry based on PostHog, used to collect command execution data (e.g., command_executed) to help teams analyze tool usage frequency and process bottlenecks. By design, it strictly follows privacy principles — no parameters, IP addresses, or business content are collected — and it can be completely disabled via the OPENSPEC_TELEMETRY=0 environment variable.
OpenSpec’s iteration process is centered on “spec-first”, but does not mandate a cumbersome approval process.
Before any code begins, first use proposal.md to define Why and What.
Use the CLI to quickly initialize the project structure (if you are using the examples/ecommerce-mini source code directly, this step is already complete and can be skipped):
openspec init --tools none
If you are using the companion source code, run the following command to confirm that the spec documents are complete and valid:
openspec validate v1-mvp
Create a new changeset (e.g., v1-mvp) under openspec/changes/, containing the following files:
proposal.md (Macro Intent)This is the starting point of the project, clearly defining “why we are doing this” and “what the criteria for success are”.
# Proposal: Ecommerce Mini MVP
## Why
### Background
This project originated from in-depth community discussions about AI-assisted programming, and requires a complete hands-on case to demonstrate the specific application of OpenSpec specifications in AI-assisted programming.
### Problem Statement
A standardized way is needed to ensure that humans and AI reach consensus on requirements while keeping code and documentation in sync.
### Alternatives Considered
1. **Write code directly**: Fast but lacks documentation support.
2. **Traditional requirements documents**: Separated from code, easily becomes outdated.
3. **OpenSpec spec-driven**: Define the spec first, then write the code, maintaining consistency ✓ This approach was chosen.
## What Changes
### New Resources Added
- **Domain Models**: Core entities such as Product, User, Cart, Order.
- **API Interfaces**: RESTful interfaces for products, cart, orders, payments, etc.
### New Capabilities
- Product list query and listing.
- Cart management (add, remove).
- Order checkout and inventory deduction.
## Capabilities
### New Capabilities
- `catalog-management`: Product list query, product listing, and inventory deduction management
- `cart-management`: Cart item addition, removal, and quantity limits
- `order-management`: Order creation, order query, and total price calculation
- `payment`: Order payment and status transitions
- `domain-model`: Core business entity definitions
- `error-handling`: Unified error response format and standard error code system
### Modified Capabilities
(None — this is a brand-new MVP project)
## Impact
- **Affected Code**: `ecommerce-mini/src/` (Node.js), `ecommerce-mini-python/src/` (Python)
- **New APIs**: 8 REST endpoints (GET /api/products, POST /api/products, POST /api/cart/items, DELETE /api/cart/items/:id, POST /api/orders, GET /api/orders/:id, POST /api/payments/:orderId, GET /metrics)
- **Dependencies**: Node.js has zero npm dependencies; Python depends on FastAPI + Pydantic
## Scope
### In Scope
- Product list query and listing.
- Cart management (add, remove).
- Order checkout and inventory deduction.
- Payment simulation.
- In-memory storage and file persistence.
### Out of Scope
- Complex search and recommendations.
- Real payment gateway integration.
- Admin management interface.
## Goals (SLO)
- **Latency**: Core interface p99 < 100ms.
- **Concurrency**: Support 50 RPS.
- **Quality**: Core logic test coverage > 80%.
design.md: System design draft (architecture diagrams, data flow).specs/: Specific interface definitions and data models.tasks.md: Decomposed development tasks.Based on the Proposal, determine the following in design.md:
This is the core of OpenSpec — code is a mapping of the spec.
domain/): Directly maps the data models in the Spec.http/): Directly maps the API definitions in the Spec.__tests__/): Directly maps the acceptance criteria (Scenarios) in the Spec.performance.spec.js) during the development phase to ensure SLOs are met.openspec validate command to verify that the spec document format is correct.# Validate the change spec
openspec validate v1-mvp
# Successful validation output
Change 'v1-mvp' is valid
Note: The validate command checks Proposal section completeness, directory structure, Delta Header format, and Scenario coverage. For detailed validation rules and troubleshooting, see OpenSpec User Manual - 6. Validation and Common Errors.
When v1-mvp development is complete and has passed acceptance, run /opsx:archive (recommended) or the CLI command openspec archive. This merges the Specs from the changeset into the main spec directory (openspec/specs/), making them the latest source of truth for the system.
This case builds a micro e-commerce system called ecommerce-mini, containing five core contexts:
To focus on the OpenSpec process, the following engineering trade-offs were made:
This project uses a classic four-layer architecture to ensure separation of concerns:
| Layer | Directory (src/) |
Responsibilities | Dependency Direction |
|---|---|---|---|
| Interface Layer | http/ |
Handle HTTP requests, parameter parsing, authentication, response formatting | -> Application |
| Application Layer | services/ |
Use case orchestration, e.g., “place order” involves deducting stock, clearing cart | -> Domain, Repo |
| Domain Layer | domain/ |
Pure business entities (types.js) and logic, no external dependencies |
None |
| Infrastructure | repo/, persist/ |
Data persistence implementation (Memory/File) | Implementation Detail |
Using the “place order” scenario as an example:
POST /api/orders request.201 Created with the order details.Standard RESTful JSON style is used.
/api/products.200 OK: Query/modification successful.201 Created: Resource creation successful.400 Bad Request: Business validation failed (e.g., cart is empty).401 Unauthorized: Not logged in.409 Conflict: Resource conflict (e.g., insufficient stock).In OpenSpec, we first define the models in changes/v1-mvp/specs/domain-model/spec.md. Spec files must use the Delta Header + Requirement + Scenario format:
Spec Definition (changes/v1-mvp/specs/domain-model/spec.md):
# Domain Model Specification
## Overview
Core business entity definitions, with no dependency on any external framework.
## ADDED Requirements
### Requirement: Product Entity Definition
The system SHALL define a product entity containing a unique identifier, name, price, and stock.
**Priority**: P0 (Critical)
**Rationale**: Products are the core entity of an e-commerce system and the foundation of all transactions.
#### Scenario: Create a Valid Product
Given a new product needs to be created
When product information is provided: { id, name, priceCents, stock }
Then the product entity is created successfully
And the id format is prod_xxxx
And priceCents >= 0
And stock >= 0
---
### Requirement: Non-negative Stock Validation
Stock MUST NOT become negative after deduction.
**Priority**: P0 (Critical)
**Rationale**: Negative stock leads to overselling and affects business accuracy.
#### Scenario: Deduct Stock When Insufficient
Given a product has a stock of 5
When an attempt is made to deduct 6 units of stock
Then an OUT_OF_STOCK error is thrown
And the stock remains unchanged
Code Implementation (src/domain/types.js / JSDoc):
Since this project uses plain JS, we implement the Spec mapping via JSDoc:
/**
* @typedef {Object} Product
* @property {string} id
* @property {string} name
* @property {number} priceCents
* @property {number} stock
*/
Unified error response structure for easy front-end processing:
{
"code": "OUT_OF_STOCK",
"message": "Product [prod_123] is out of stock"
}
listProducts(): Full query (no pagination in Demo).getProduct(id): Detail query.deductStock(id, qty): Atomic stock deduction, must handle concurrent contention (simplified to a single-threaded lock in Demo).addToCart(userId, item): Incremental update.clearCart(userId): Clear after order placement.userId, Value is a Cart object.createOrder(userId): Core complex logic, coordinates Cart and Catalog.payOrder(orderId): Status transition PENDING -> PAID.A key advantage of OpenSpec is using Markdown to write highly readable spec documents while maintaining structure. In v1.2.0, specs are organized by Capability, one folder per capability. The following are real excerpts from openspec/changes/v1-mvp/specs/catalog-management/spec.md and openspec/changes/v1-mvp/specs/order-management/spec.md:
# Catalog Management Specification
## Overview
Product catalog management capability, covering product queries, listing, and inventory management.
## ADDED Requirements
### Requirement: Product List Query
The system SHALL provide a product list query interface that returns all available products.
**Priority**: P0 (Critical)
**Rationale**: Product browsing is the core entry function of an e-commerce system; users must be able to view purchasable products.
#### Scenario: Get All Products
Given product data exists in the system
When the user requests GET /api/products
Then a status code of 200 is returned
And a product array Product[] is returned
# Order Management Specification
## Overview
Order management capability, covering order creation, query, and total price calculation.
## ADDED Requirements
### Requirement: Order Creation
The system SHALL support settling a user's shopping cart to create an order, handling inventory deduction and cart clearing in the process.
**Priority**: P0 (Critical)
**Rationale**: Order creation is the core transaction process, involving coordination of multiple modules (Cart -> Catalog -> Order).
#### Scenario: Successfully Create an Order
Given the user has items in their shopping cart
And the product inventory is sufficient
When a POST /api/orders request is sent with { userId }
Then a status code of 201 is returned
And the newly created Order is returned
And the order status is PENDING_PAYMENT
And the shopping cart is cleared
And the inventory is deducted
#### Scenario: Insufficient Inventory When Creating an Order
Given the user has items in their shopping cart
And the product inventory is insufficient
When a POST /api/orders request is sent with { userId }
Then a status code of 409 is returned
And the error code OUT_OF_STOCK is returned
Note:
- You must use
## ADDED/MODIFIED/REMOVED Requirementsas the Delta Header.- Each requirement uses the
### Requirement: <title>format, and the description must containSHALLorMUST.- Each requirement must include
**Priority**and**Rationale**fields.- Each requirement must contain at least one
#### Scenario:block in Gherkin format (Given/When/Then).The
Product[]andOrderreferences here refer to data models defined indomain-model/spec.md, maintaining definition consistency.Format Check: After writing, be sure to run
openspec validate v1-mvpto verify the format, and avoid blocking subsequent processes due to missing section headings or Scenarios.
This is the core of OpenSpec — code is a mapping of the spec. When writing code, developers (or AI) should always have the Spec file open as a reference.
Establish a Spec ↔ Code ↔ Test mapping to ensure every Spec item has code coverage:
| Spec Definition (Requirements) | Code Implementation | Verification Method |
|---|---|---|
POST /api/orders |
src/http/server.js (Route Handler) |
Integration Test |
Response 409: Stock insufficient |
catch (e) { if (e.message === 'OUT_OF_STOCK') ... } |
Unit Test (Error Case) |
Order.totalCents (Model) |
src/domain/types.js (Interface) |
JSDoc / Code Review |
p99 < 100ms (SLO) |
performance.spec.js (Performance Test) |
CI Pipeline |
Practical Tip: Reference the corresponding Spec section in code comments, e.g., // Corresponds to Spec: POST /api/orders.
OpenSpec-practise/
├── docs/ <-- Documentation directory
│ ├── openspec-user-manual.md <-- OpenSpec User Manual
│ ├── openspec-practical-guide.md <-- Practical Guide (this document)
│ └── openspec-ai-workflow-analysis.md <-- AI Collaboration Workflow Analysis
├── examples/
│ ├── openspec/ <-- OpenSpec spec directory
│ │ ├── config.yaml <-- Project configuration (tech stack, rules, etc.)
│ │ ├── specs/ <-- Main spec (source of truth after archiving)
│ │ │ ├── catalog-management/spec.md
│ │ │ ├── cart-management/spec.md
│ │ │ ├── order-management/spec.md
│ │ │ ├── payment/spec.md
│ │ │ ├── domain-model/spec.md
│ │ │ └── error-handling/spec.md
│ │ └── changes/v1-mvp/ <-- MVP change spec
│ │ ├── .openspec.yaml <-- Change metadata
│ │ ├── proposal.md <-- Corresponding proposal document
│ │ ├── design.md <-- Corresponding design document
│ │ ├── tasks.md <-- Corresponding task list
│ │ └── specs/ <-- Delta specs (organized by capability)
│ │ ├── catalog-management/spec.md <-- Catalog management spec
│ │ ├── cart-management/spec.md <-- Cart management spec
│ │ ├── order-management/spec.md <-- Order management spec
│ │ ├── payment/spec.md <-- Payment spec
│ │ ├── domain-model/spec.md <-- Domain model spec
│ │ └── error-handling/spec.md <-- Error handling spec
│ ├── ecommerce-mini/ <-- Node.js Implementation
│ │ └── src/
│ │ ├── domain/types.js <-- Corresponds to Data Models in the Spec
│ │ ├── services/ <-- Corresponds to Business Rules in the Spec
│ │ ├── http/server.js <-- Corresponds to API Definitions in the Spec
│ │ └── persist/ <-- Corresponds to Storage Strategy in the Design
│ └── ecommerce-mini-python/ <-- Python Implementation
Controller Layer (src/http/server.js):
// Corresponds to Spec: POST /api/orders
if (pathname === "/api/orders" && req.method === "POST") {
const body = await readJson(req);
try {
// Orchestrate business logic (Orchestration)
// 1. Check cart (Rule: Cart Not Empty)
// 2. Check inventory (Rule: Stock Check)
// 3. Create order
const order = orderService.createOrder(body.userId);
return sendJson(res, 201, order);
} catch (e) {
if (e.message === "CART_EMPTY")
return sendError(res, "CART_EMPTY", "Cart is empty", 400);
if (e.message === "OUT_OF_STOCK")
return sendError(res, "OUT_OF_STOCK", "Insufficient stock", 409);
throw e;
}
}
After writing code, how can you effectively evaluate consistency between the implementation and the Spec? This section introduces the validation methods and tools recommended by OpenSpec.
The traceability matrix was already presented in 9.1 Traceability Matrix. It is recommended to maintain it concurrently during the code-writing phase, ensuring every Spec item has a corresponding code implementation and test verification.
OpenSpec’s Gherkin-format scenarios are naturally suited for conversion into test cases:
Scenario in the Spec (specs/order-management/spec.md):
#### Scenario: Successfully Create an Order
Given the user has items in their shopping cart
And the product inventory is sufficient
When a POST /api/orders request is sent with { userId }
Then a status code of 201 is returned
And the newly created Order is returned
And the shopping cart is cleared
And the inventory is deducted
Corresponding Integration Test (__tests__/integration.spec.js):
The following example assumes the test environment service is running at
http://localhost:3000(thebasevariable points to that address).
it('complete shopping flow', async () => {
// 1. List product → Given product inventory is sufficient
const res1 = await fetch(`${base}/api/products`, {...})
assert.strictEqual(res1.status, 201)
// 2. Add to cart → Given user has items in their shopping cart
const res2 = await fetch(`${base}/api/cart/items`, {...})
assert.strictEqual(res2.status, 200)
// 3. Place order → When POST /api/orders is sent
const res3 = await fetch(`${base}/api/orders`, {...})
assert.strictEqual(res3.status, 201) // Then status code 201 is returned
const order = await res3.json()
assert.strictEqual(order.totalCents, 1000) // Then the newly created order is returned
assert.strictEqual(order.status, 'PENDING_PAYMENT')
})
OpenSpec does not replace TDD — it works in concert with TDD:
Traditional TDD: Requirements → Write tests → Write code → Refactor
OpenSpec + TDD: Requirements → Write Spec → Validate Spec → Write tests (based on Scenarios) → Write code → Refactor
Key Differences:
Encountering format errors? See OpenSpec User Manual - 6. Validation and Common Errors for troubleshooting.
When reviewing a PR, check for consistency between implementation and Spec:
Tests are not an afterthought — they are the executable version of the spec.
src/domain/logic.spec.js)Targeting pure function logic, such as amount calculation and state machine transitions.
import { test } from "node:test";
import assert from "node:assert";
import { calculateTotal } from "./logic.js";
test("calculateTotal sums up item prices", () => {
const items = [
{ priceCents: 100, quantity: 2 },
{ priceCents: 50, quantity: 1 },
];
const total = calculateTotal(items);
assert.strictEqual(total, 250);
});
__tests__/integration.spec.js)Simulates real user paths, verifying inter-module collaboration.
node --test examples/ecommerce-mini/__tests__/integration.spec.jsperformance.spec.js)Defines and validates SLOs.
node:test and fetch are used).This project has no package.json dependencies (aside from development tools), which not only showcases native capabilities but also lowers the barrier to running the project.
The development version uses in-memory storage; data is reset on restart.
# Start the server
node examples/ecommerce-mini/src/http/server.js
# Run the test suite in another terminal
node --test examples/ecommerce-mini/__tests__/
The production version has file persistence, authentication, and idempotency checking enabled.
# Start the production server (Port 3002)
node examples/ecommerce-mini/src/http/server.prod.js
To demonstrate how OpenSpec handles complexity, three advanced features are introduced in server.prod.js.
src/persist/fileStore.js)Spec Change: The system needs to retain user data even after a restart.
Implementation:
FileStore class, using fs.writeFileSync for atomic JSON file writes.Spec Change: All non-public interfaces must carry a Bearer Token.
Implementation:
Bearer Token format validation middleware.GET /api/products are exempt) that lack an Authorization header, returning 401 Unauthorized.Spec Change: For duplicate payment requests for the same order, the system should return the same result without charging again.
Implementation:
Idempotency-Key in the header.idempotencyKey judgment block inside the POST /api/orders handler in server.prod.js, where developers can integrate Redis or an in-memory cache for deduplication.Spec Change: The system needs to expose a /metrics endpoint for monitoring collection.
Implementation:
{ "requests": 100, "latencies": { "p99": 12 } } (latency in ms).Through the ecommerce-mini case, we have demonstrated how OpenSpec spans the full lifecycle from “Proposal” to “Production”.
We hope this practical guide helps you better apply the OpenSpec methodology in real-world projects.