AI Writes Code Faster Than You Can Think — That's the Problem SDD Fixes
Foreword
As of July 2026, on GitHub, the SDD library spec-kit has over 120,000 stars, and OpenSpec has over 60,000 stars. Earning such high star counts on GitHub must mean there's something to it. AI coding is already so fast and so powerful, so why do we still need SDD? This article aims to discuss that.
How Do We Usually Develop with AI?
Our usual AI development process looks something like this:
Propose a requirement
↓
Let AI read the project
↓
Let AI start writing code
↓
yes, yes, yes, don't ask me again
↓
Run and test
↓
There's a bug, the requirement wasn't understood correctly
↓
Whip the AI: "You wrote this part wrong, what I meant was xxxxx"
↓
AI continues to fix it
↓
After N rounds, it's finally done
↓
I'm the best
For example, if we want to add a reminder feature to a Todo App, we just need to tell the AI:
Add a task reminder feature to the Todo App.
The AI can quickly modify the data model, add a time selection page, and then call the system notification interface.
After running it, you can indeed receive notifications.
But when you actually start testing, you might discover many issues that weren't clearly stated:
- What happens after the user denies notification permissions?
- If the time is modified, should the old notification be cancelled?
- After deleting or completing a task, should the notification still trigger?
- After clicking a notification, which page should it open?
So we continue to supplement the prompts, and the AI continues to modify the code.
But it's not that the AI can't write code; it's that before writing, we didn't clearly explain the result we truly wanted. For the parts we didn't articulate, the AI can only guess. If it guesses right, the feature is completed quickly; if it guesses wrong, we have to keep modifying.
In actual development, we certainly don't just have a single requirement sentence; there's usually a requirements document. A requirements document lets us know what the feature is, but it doesn't necessarily specify clearly how it should behave or interact in every situation.
When writing test cases, testers often think of scenarios like permission failures, repeated operations, state changes, or abnormal data. This part of the thinking should be completed before code implementation; otherwise, the AI might not notice these details.
Of course, this doesn't mean writing all test cases at this stage. If the expected results are already clear and only need verification by changing devices, system versions, or data combinations, then that can be left for the testing phase.
SDD is here to solve this problem.
What is SDD?
SDD stands for Specification-Driven Development.
Simply put, before letting the AI write code, you first organize what needs to be done into a clear specification, and then generate a technical plan, tasks, and code based on that specification.
A typical process looks like this:
Propose requirement
↓
Specification: AI asks what you want to do, what conditions count as completion
↓
Plan: AI describes how it intends to implement it
↓
Tasks: AI breaks down its plan into executable tasks
↓
Implement: AI implements code according to the tasks
↓
Verify: Go back to the original specification and check if the results meet the requirements
If you've used plan mode, this process should feel familiar.
More formally:
- Specification: Explains what to do and what conditions count as completion.
- Plan: Explains how to implement it.
- Tasks: Breaks the plan down into executable tasks.
- Implement: Implements code according to the tasks.
- Verify: Returns to the original specification to check if the results meet the requirements.
Why Do We Need SDD?
It's because AI is too fast. It writes code so fast that before you've truly clarified the requirements, it has already finished writing. Then you look back and find that this part is wrong, that part is missing something, and you have to patch things up.
So the problem it solves is:
The code is already written, but the requirements haven't been truly thought through.
What it does is move the problems that were originally discovered after coding to be resolved as much as possible before coding.
Before:
- Write code first
- Discover requirement omissions after running
- Modify requirements and code
SDD:
- Organize requirements first
- Discover omissions and confirm
- Then generate code
It doesn't eliminate all rework, but modifying requirements when there are only a few paragraphs of text is usually simpler than modifying them after the code has already spread across multiple modules.
Additionally, it incidentally solves another important problem, which is the context problem.
When developing directly with AI, many decisions are scattered across your chat history with the AI. If you switch sessions, switch Agents, come back after some time, or accidentally close the session, you might forget why something was implemented that way.
But Spec, Plan, and Tasks can be saved in the project alongside the code. Subsequent AIs can read them directly, without us needing to re-explain everything each time.
What Happens After Using It?
Let's use the Todo App reminder feature as an example again.
This time, we won't let the AI write code immediately. Instead, we first tell it:
Add a task reminder feature to the Todo App.
Don't modify the code yet. Help me organize the feature scope, user behavior, and acceptance criteria. For anything unclear, ask me first; don't decide on your own.
After several rounds of confirmation, we might get a simple specification:
## Goal
Users can set a reminder time for unfinished tasks.
## Acceptance Criteria
- Users can create tasks without reminders;
- After modifying the reminder time, only the new notification is kept;
- After completing or deleting a task, cancel the corresponding notification;
- After the user denies notification permissions, the page needs to show a prompt;
- After clicking a notification, open the corresponding task.
## Not Included
- Does not support recurring reminders;
- Does not support server-side push.
After the specification is confirmed, let the AI generate a technical plan based on the project:
Based on the confirmed specification and the current project structure, generate a technical plan. Explain which modules need to be modified and what each module is responsible for.
After the plan is confirmed, break it down into tasks:
Break down tasks based on the specification and technical plan. Each task needs clear completion conditions and verification methods.
Finally, enter the code implementation phase, and after completion, verify item by item according to the specification.
So after using SDD, the development process hasn't magically changed; you still need to write code, run tests, and check results.
What has truly changed is that the AI no longer relies solely on a single ad-hoc prompt, but works based on a set of confirmed content.
So, chat-driven coding has become specification-driven coding.
How to Use It?
It doesn't require you to install any tools; it's just a set of rules. The simplest way is to add a few files to your project and start using it:
docs/
└── specs/
└── task-reminder/
├── spec.md
├── plan.md
└── tasks.md
Then set a few rules for yourself:
- Don't start design and implementation before the Spec is confirmed.
- Don't start breaking down tasks before the Plan is confirmed.
- Each task needs to explain how to verify it.
- If new requirements are discovered during implementation, update the Spec first.
- After completion, verify item by item according to the Spec.
Also, you don't need to go through the full process for every task.
- Small tasks: Implement directly.
- Tasks with some boundary conditions: Spec -> Tasks -> Implement -> Verify.
- Tasks affecting multiple modules: Spec -> Plan -> Tasks -> Implement -> Verify.
The focus of SDD isn't on generating many documents, but on deciding how much content needs to be clarified in advance based on the task's complexity.
Those SDD Libraries on GitHub
Spec Kit
The full capabilities provided by spec-kit are roughly as follows. For ease of understanding, the complete process is listed here, but not every requirement must execute all steps sequentially:
Constitution (Project-level)
↓
Specify
↓
Clarify (Optional)
↓
Plan
↓
Checklist (Optional)
↓
Tasks
↓
Analyze (Optional)
↓
Implement
↓
Converge
Among these, Constitution is mainly used to define the long-term principles the project adheres to and generally doesn't need to be re-executed for every requirement.
Clarify, Checklist, and Analyze are optional commands used as needed.
Let's see what each step does.
Constitution (Project-level): Defines the principles the entire project must follow.
- What architecture and tech stack to use.
- What quality standards the code must meet.
- What content must be tested.
- Whether adding third-party dependencies is allowed.
Specify: Organizes the raw requirement into
spec.md.- What problem to solve.
- What the user can do.
- What behavior the system should exhibit.
- What conditions count as completion.
Clarify (Optional): Finds unclear parts in the specification.
- Are there multiple interpretations?
- Are there any missed edge cases?
- What content are assumptions made by the AI itself?
- Writes the confirmed answers back to
spec.md.
Plan: Generates a technical plan based on the specification.
- What system APIs to use.
- Which modules need to be modified.
- How data flows.
- What method to use for testing.
Checklist (Optional): Checks the quality of the specification itself.
- Are the requirements complete?
- Is the expression clear?
- Are there conflicts between different requirements?
- Can each item be verified?
Tasks: Breaks the technical plan down into tasks.
- What each task needs to modify.
- Which files need to be modified.
- What dependencies exist between tasks.
- How to verify after completion.
Analyze (Optional): Checks if Spec, Plan, and Tasks are consistent.
- Do all behaviors in the specification have corresponding plans?
- Has everything in the plan been broken down into tasks?
- Are there any omissions or conflicts?
Implement: Implements code according to
tasks.md.- Executes tasks in dependency order.
- Runs corresponding tests.
- Updates task completion status.
Converge: Checks the final result against the original specification.
- Which requirements have been implemented.
- Which requirements have not been implemented.
- After discovering omissions, supplements tasks and continues implementation.
Its entire process can be summarized as:
- First, determine project rules.
- Clarify the requirements.
- Find ambiguities in the requirements.
- Design an implementation plan.
- Break it down into executable tasks.
- Implement the code.
- Return to the original requirements for verification.
OpenSpec
The difference between OpenSpec and Spec Kit is that OpenSpec doesn't emphasize going through multiple stages sequentially. Instead, it treats each requirement as a change to the project.
Two types of specifications are saved simultaneously in the project:
openspec/specs/
Records the currently confirmed behavior of the system.
openspec/changes/
Records the changes currently under development.
OpenSpec provides six core Workflows by default: Explore, Propose, Apply, Update, Sync, and Archive.
Verify is not in the default Core Profile but can be enabled as needed. Their usage is roughly as follows:
For the specific process, refer to the OpenSpec OPSX documentation.
Let's see what each step does:
Explore
Explores the requirement before formally creating a change:
- Reads the existing project.
- Understands the current implementation.
- Compares different solutions.
- Finds unclear issues.
- Does not create a formal change or modify code.
If the requirement is already clear, this can be skipped.
Propose
Creates an independent change directory for the requirement:
openspec/
└── changes/
└── change-name/
├── proposal.md
├── specs/
├── design.md
└── tasks.md
Where:
proposal.md: Why it's being done, what it will affect.specs/: What changes have occurred in system behavior.design.md: How it's planned to be implemented.tasks.md: What tasks need to be completed.
Specifications describe changes in the following ways:
ADDED: New requirements.MODIFIED: Modified existing requirements.REMOVED: Deleted requirements.RENAMED: Name-only changes.
design.md is not required for all requirements. If a change involves multiple modules, data models, external dependencies, or important technical choices, a Design needs to be written first; simple modifications can omit it.
Apply
Implements code according to the documents in the change:
- Reads Proposal, Specs, Design, and Tasks.
- Modifies code according to the task list.
- Updates task status after completion.
- If requirements or plans change during implementation, uses Update first to update existing artifacts.
This is also where OpenSpec differs from fixed-stage processes: if requirements or plans change during implementation, you can use Update to go back to previous artifacts, then continue with Apply.
Update
If requirements, technical plans, or tasks change, uses Update to modify existing change artifacts:
- Modifies existing Proposal, Specs, Design, or Tasks.
- Checks if other artifacts need to be adjusted together.
- Maintains consistency between requirements, design, and tasks.
- Does not create missing artifacts or modify code.
Update is not a fixed step that must be executed after Apply, but is used as needed when requirements or plans change.
If code has already been implemented according to the old plan, after updating the artifacts, Apply needs to be run again to align the code with the new plan.
Verify
Checks if the code conforms to the change content:
- Are all tasks completed?
- Has every requirement been implemented?
- Are edge cases handled?
- Does the implementation conform to the Design?
Verify is not in the Core Profile by default and needs to be manually enabled:
openspec config profile
openspec update
After checking verify in the configuration, the specific entry point depends on the Agent and Delivery used.
If using Commands Delivery, it's typically:
/opsx:verify
If using Codex's Skills Delivery, it's:
$openspec-verify-change
Complex features or cross-module modifications are suitable for executing Verify; simple, well-tested modifications can be skipped. For specific configuration methods, refer to OpenSpec Commands.
Sync
Merges the specifications from the current change into the project's main specifications:
openspec/changes/change-name/specs/ -> openspec/specs/
Sync can be actively executed during development, allowing other changes to read the latest specifications in advance.
If the feature is archived immediately after implementation, this can also be skipped and synced together during Archive.
Archive
Ends and archives the current change:
- Checks document and task status.
- Prompts to sync unmerged specifications.
- Moves the change to
archive/. - Preserves the complete history of Proposal, Design, Tasks, and Spec.
The structure after archiving is roughly:
openspec/
├── specs/
│ └── Currently valid specifications
└── changes/
└── archive/
└── Completed changes
So OpenSpec's entire process can be summarized as:
- Understand the requirement.
- Establish an independent change.
- Describe what changes have occurred in system behavior.
- Implement code according to the change.
- When requirements or plans change, update existing artifacts.
- Check if the code conforms to the change content as needed.
- Merge the changes into the current specifications.
- Archive the complete development history.
cc-sdd
cc-sdd focuses most on two things:
- Using Spec to clarify module boundaries and dependency relationships.
- After the specification is confirmed, letting Agents complete implementation task by task over a long period.
The overall process of cc-sdd is as follows:
For the specific process, refer to cc-sdd Workflow.
Let's also see what each of its steps does:
Steering
Records the long-term context of the entire project:
- What architecture and tech stack to use.
- How the code directory is organized.
- What development standards the project has.
- What dependencies exist between modules.
- What testing and verification methods to use.
Steering is mainly used for existing projects. If the Agent can already obtain complete project rules from other files, it can be used as needed.
Discovery
Determines what development approach should be taken for the current requirement:
- Implement directly, without creating a Spec.
- Modify an existing Spec.
- Create a new Spec.
- Break a large requirement into multiple Specs.
- Handle different parts with a mix of approaches.
Discovery saves a brief.md so that subsequent Agents don't need to re-understand the requirement. If it needs to be broken into multiple Specs, it also generates a roadmap.md.
So Discovery is not just about clarifying requirements; it's also responsible for judging whether this piece of work needs to enter the full SDD process.
Spec Init
Creates an independent workspace for the feature:
.kiro/
└── specs/
└── feature-name/
The subsequent Requirements, Design, and Tasks will all be saved in this directory.
Design
Generates the technical design:
- Investigates the current project implementation.
- Designs modules, interfaces, and data flow.
- Explains which files need to be modified.
- Clarifies what each module is responsible for.
- Records the allowed dependencies between modules.
design.md will contain a File Structure Plan, used to delineate the scope of files that subsequent tasks can modify.
cc-sdd calls this step Boundary-First, meaning boundaries are determined first, then multiple Agents can work independently.
Tasks
Breaks the Design down into tasks:
- Each task is only responsible for a clear piece of work.
- Marks dependencies between tasks.
- Explains the files and modules allowed to be modified.
- Records completion conditions and verification methods.
Markers like the following will appear in tasks:
_Boundary: What this task can modify
_Depends: What this task depends on
This way, when an Agent executes a task, it won't arbitrarily modify modules outside its scope.
Spec Batch
If Discovery determines the requirement is too large, you can use:
/kiro-spec-batch
It will:
- Create multiple Specs based on the Roadmap.
- Generate different Specs in parallel.
- Check for conflicts between multiple Specs.
- Check for duplicate interfaces and responsibilities.
- Ensure each Spec can be delivered independently.
Implementation
After the specification is confirmed, use:
/kiro-impl
cc-sdd supports two implementation modes.
Automatic mode:
- Executes only one task at a time.
- Creates a new implementation Agent for the task.
- Uses TDD to complete the code.
- Then has an independent Reviewer Agent check the result.
- On failure, uses a new debugging Agent to analyze the cause.
- Writes the experience back to
tasks.mdfor subsequent tasks to read.
Manual mode:
- Specifies the task to be executed.
- Completes the implementation in the current session.
- Also executes following the test, implement, and verify approach.
For specific execution methods, refer to cc-sdd Skill Reference.
Validation
Checks the final result after multiple tasks are combined:
- Have all tasks been completed?
- Does the implementation violate module boundaries?
- Is consistency maintained between different tasks?
- Do tests, builds, and static checks pass?
- Do upstream modifications require re-validating downstream tasks?
The correctness of individual tasks is mainly checked by the Reviewer Agent; Validation focuses more on whether the combination of multiple tasks can still work correctly.
The Core of cc-sdd
cc-sdd doesn't view Spec as a master document controlling all implementation details.
It tends to view Spec more as a contract between modules:
- What this module is responsible for.
- What it is not responsible for.
- What it can depend on.
- What needs to be re-validated after modification.
The specific implementation can still be freely decided by the Agent, but it cannot cross the confirmed boundaries. cc-sdd's design explanation also explicitly states that code is still the final running truth, and Spec is responsible for making responsibilities and boundaries clear.
So cc-sdd's entire process can be summarized as:
- First, determine if a Spec is needed.
- Break the requirement into independently deliverable scopes.
- Clarify module boundaries and dependencies.
- Break it down into individual tasks.
- Have different Agents implement and review.
- Finally, verify that the tasks can correctly collaborate with each other.
What Are Their Differences?
After looking at the flurry of processes above, you'll find they all do the same thing: first turn the requirement into a Spec, then generate a design, tasks, and code.
The difference lies in the problems they focus on.
Spec Kit focuses more on whether the requirements, plan, and tasks are complete and consistent.
It provides a full set of capabilities around requirements, design, and tasks, and uses Clarify, Checklist, and Analyze as needed based on task complexity.
It's asking:
Has this requirement been completely specified and implemented?
OpenSpec focuses more on what changes have occurred in the project.
It records each requirement as an independent change, merges it into the project's current specifications after completion, and preserves a complete change history.
It's asking:
What did this development add, modify, or delete from the existing system?
cc-sdd focuses more on how tasks should be broken down and how Agents should execute them.
It first clarifies module boundaries and task dependencies, then lets different Agents be responsible for implementation, review, and verification respectively.
It's asking:
How can the work be divided so that Agents can execute for a long time without interfering with each other?
So simply put:
- Spec Kit: Emphasizes keeping requirements, plans, and tasks complete and consistent.
- OpenSpec: Emphasizes managing ongoing changes.
- cc-sdd: Emphasizes dividing boundaries and letting Agents execute continuously.
What is TDD?
After talking about SDD, let's talk about TDD.
TDD stands for Test-Driven Development. It's not about writing tests after the code is finished, but developing iteratively according to the following process:
First write a test and confirm it fails (Red)
↓
Write just enough code to make the test pass (Green)
↓
Refactor the code while the tests still pass (Refactor)
↓
Continue implementing the next behavior
For example, SDD first specifies:
After completing a task, the corresponding reminder needs to be cancelled.
At the TDD stage, you would first write a test for this behavior, then implement the code to cancel the reminder, until the test passes.
It and SDD are not an either/or relationship; the problems they solve are different:
SDD first decides which behaviors should be implemented, and TDD then uses tests to implement these behaviors one by one.
They are not on the same level. You can understand TDD as an optional development method within the Implement stage of SDD:
SDD tools handle the outer process
Specification
↓
Design
↓
Tasks
↓
Implementation
↓
Verification
In the "Implementation" step,
you can choose to use TDD
Let's use the Todo reminder feature as an example again.
Initially, we discovered a problem:
After the user completes a task, should the reminder still trigger?
This problem should be handled by SDD, because at this point, we don't even know what the correct result is. After confirmation, the specification states:
After the user completes a task, the corresponding reminder needs to be cancelled.
Next, entering the implementation phase, TDD starts to play its role:
- First write a test: After completing a task, the method to cancel the reminder should be called.
- Run the test; because the code hasn't been implemented yet, the test fails.
- Write the code to cancel the reminder, making the test pass.
- Refactor the code, then continue implementing the next behavior.
Therefore:
- SDD discovers and confirms "after completing a task, the reminder should be cancelled."
- TDD ensures this behavior is correctly implemented by the code.
TDD won't decide for you whether the reminder should be cancelled after completing a task. If the requirement doesn't mention permission failure, TDD won't magically supplement that scenario for you. It can only, after the expected result is determined, turn that result into a test and then drive the code implementation.
Also, SDD does not require the use of TDD. Among the libraries just mentioned, cc-sdd explicitly puts TDD into the implementation process; Spec Kit can generate test-first tasks as needed; OpenSpec does not restrict the specific implementation method. TDD is a development method that can be adopted when SDD enters the code implementation phase.
Do You Need to Build Your Own SDD?
Actually, after reading this, you might find that they are all very powerful, but they might not perfectly fit your usual development process. Fortunately, we can build our own custom SDD.
The key here is what you care about, what you hope the AI can help you achieve, and to standardize this process.
Taking myself as an example, what I care about is:
- Are there any omissions or unreasonable parts in the requirements?
- How is the code planned to be implemented, what changes will roughly be made, and why was this plan chosen?
- What will be changed, and what is the scope of impact?
- What scenarios need to be tested, and what counts as passing the test?
- How to record and archive this change long-term?
But after listing these, I found that OpenSpec actually fits my needs quite well, so the grand undertaking of building my own SDD has been put on hold for now. This is just a sharing of an idea to spark discussion. I might introduce the TDD process later, but we'll see.
Top 1 from juejin.cn, machine-translated. The original thread is authoritative.
Very well written.