A Core Skill in the Age of AI Coding Agents: Writing Effective Software Specs

From Vibe Coding to Spec-Driven Development—when code is no longer the source, intent is.

Introduction: Why the “Requirements Document” You Write for AI Matters More Than Ever

In 2025–2026, AI coding agents underwent a qualitative transformation. Claude Code, Codex CLI, Cursor, and Kiro are no longer merely autocomplete tools that predict the next line of code. They have evolved into autonomous agents capable of understanding an entire repository, making changes across multiple files, running tests, and iterating independently. In his widely circulated workflow guide, Google’s Addy Osmani describes this new mode as “AI-augmented software engineering”—software engineering augmented by AI, not automated by AI.

That distinction is critical. The developer’s role is shifting from “the person who writes code” to “the person who directs AI to write code.” The primary vehicle for that direction is the Software Spec.

In September 2025, GitHub open-sourced the Spec Kit toolkit and formally introduced the methodology of Spec-Driven Development (SDD). AWS released the Kiro IDE with a built-in SDD workflow. Martin Fowler’s technology blog also began a systematic discussion of SDD’s definition, tools, and limitations. A clear industry consensus is emerging: in the age of AI agents, the specification is replacing code as the source of truth.

Drawing on industry practices from 2025–2026, this article explains what a Software Spec is, why it matters, how to write one well, and presents a best-practice template you can use directly.


1. Defining a Software Spec

In the context of AI coding agents, a Software Spec has a more specific meaning than it does in traditional software engineering. The SDD article series on Martin Fowler’s website gives a precise definition:

A spec is a structured, behavior-oriented artifact that describes software functionality in natural language and serves as a working guide for an AI coding agent.

More intuitively: a spec is the contract between you and the AI. It precisely states what you want, what you do not want, and which constraints apply, enabling the AI to produce code as close as possible to what you envision with minimal back-and-forth.

If you have backend engineering experience, think of it this way: when a product manager gives you an ambiguous PRD, you have to ask repeatedly for clarification and redo work. An AI coding agent is your “super junior engineer”—extremely capable of execution and extraordinarily fast, but it will not proactively fill in an unstated requirement the way you intended. The better the spec, the closer the AI’s output will be to the picture in your mind.

Spec ≠ Prompt

A common confusion needs to be cleared up here. A spec is not a one-off prompt. A prompt is a message that disappears into the chat history after it is sent. A spec is a persistent, version-controlled document anchored in the repository, available across sessions, and updated as the project evolves.

As Addy Osmani recommends, save the spec as a SPEC.md file in the project so that both you and the AI can consult it at any time. When the conversation grows long or the agent needs to restart, the spec file becomes the anchor that prevents the AI from “forgetting.”


2. Why Specs Matter More in the Age of AI Agents

1. AI Has No Implicit Context

When you collaborate with a colleague, they know the team’s technology stack, coding style, and business context. AI knows none of this unless you tell it. Every assumption that seems “obvious” to you is a constraint that must be stated explicitly for the AI.

2. AI’s “Creativity” Is a Double-Edged Sword

When a spec contains gaps, AI does not necessarily stop to ask you; it fills them itself, often in ways you did not want. Ask it to “write a user-authentication module,” and it may build a complete JWT + OAuth2 + social-login solution when all you needed was API-key validation. Overengineering is AI’s default mode.

3. The “Curse of Instructions”

Research shows that when a prompt contains too many instructions, a model’s compliance rate for each instruction declines significantly. This is known as the “curse of instructions.” It means you cannot pile every requirement, constraint, and style rule into a single message. You need a structured, layered spec to manage the information.

4. A Spec Is the Yardstick for Accepting AI Output

Without an explicit spec, you do not even have a standard for judging whether the AI’s code is good. A spec provides a checklist, allowing you to review systematically instead of relying on intuition.

5. A Spec Enables Multi-Agent Collaboration

When you use sub-agents or parallel agents, each agent needs to know its scope and boundaries. The spec defines those boundaries. Without one, multiple agents step on one another and produce conflicting code.


3. The Six Core Areas of a Spec

After analyzing more than 2,500 agents.md files, GitHub’s AI team identified a clear pattern: the most effective specs cover six core areas. The same finding applies to specs written for any AI coding agent.

1. Commands

Put executable commands near the beginning of the spec—not merely tool names, but complete commands with flags and arguments. Agents will refer to these frequently.

- Build: `npm run build` (compile TypeScript and output to dist/)
- Test: `npm test` (run Jest; must pass before committing)
- Lint: `npm run lint --fix` (automatically fix ESLint errors)

2. Testing

Explain how to run tests, which framework is used, where test files belong, and what coverage is required.

3. Project Structure

State explicitly where source code, tests, and documentation belong:

- `src/` — application source code
- `tests/` — unit and integration tests
- `docs/` — project documentation

4. Code Style

One real code example is more effective than three paragraphs of prose. Include naming conventions, formatting rules, and an example of what good code should look like.

5. Git Workflow

Specify branch naming, commit-message format, and pull-request requirements. Agents can follow all of these when you state them clearly.

6. Boundaries

Identify what the agent must never touch: secret files, vendor directories, production configuration, or specific folders. GitHub’s study found that “do not commit secrets” is the most common and one of the most effective constraints.

Express boundaries with a three-tier system:

- ✅ Always: run tests before committing; follow naming conventions
- ⚠️ Ask first: database schema changes; adding dependencies
- 🚫 Never: commit secrets; modify node_modules/; change CI configuration

4. The Spec Workflow: A Four-Stage Gated Model

GitHub’s Spec Kit proposes a four-stage workflow validated in practice. Its central idea is to place a human checkpoint at every stage and proceed only after the current stage has been verified.

Stage 1: Specify the Intent

Provide a high-level description of what you are building and why. This is not about the technology stack or architecture, but about the user journey, experience, and success criteria. Who will use the feature? What problem does it solve? How will users interact with it?

Ask the AI agent to expand your high-level description into a detailed specification. This spec becomes the first artifact you and the AI build together.

Practical recommendation: use Claude Code’s Plan Mode (Shift+Tab) to have the agent analyze the repository and create a detailed plan in read-only mode. Do not let it write code until you have confirmed the plan.

Stage 2: Plan the Technical Design

Now move to the technical level. Provide the desired technology stack, architecture, and constraints, and let the agent generate a comprehensive technical plan. If the team standardizes its technology choices, state them here. Compliance requirements and legacy-system integration needs also belong in this stage.

Stage 3: Break Down the Tasks

The agent divides the spec and plan into concrete work units that can be completed and tested independently. The task should not be “build an authentication system,” but “create a user-registration endpoint that validates email format.” Each task should be small enough to implement and verify independently. This is essentially a mapping of TDD thinking onto an AI-agent workflow.

Stage 4: Implement

The agent executes the tasks one by one, or in parallel. Instead of reviewing a huge diff containing thousands of lines, you review small changes that solve specific problems. The agent knows what to build from the spec, how to build it from the plan, and what it is currently doing from the task.

This gated workflow solves the problem of “house-of-cards code”—fragile AI output that appears to run but collapses as soon as it is pushed.


5. Best Practices from the Industry Frontier

Practice 1: Let AI Draft First, Then Refine It Yourself

Do not try to write a perfect spec from scratch. A more effective approach is to give the AI a clear high-level objective, ask it to generate a detailed draft, and then review and correct it yourself. This uses AI’s strength in expanding details while keeping directional control in your hands.

In practice, when beginning a new project, tell the agent:

“You are an AI software engineer. Draft a detailed specification for [Project X], covering its objectives, feature list, constraints, and step-by-step plan.”

Then review the result, correct any deviation or hallucination, and save it as SPEC.md.

Practice 2: Structure Is King

Use Markdown headings or XML tags to separate sections of the spec. AI models process structured text far more effectively than free-form prose. Anthropic’s engineering team also recommends organizing prompts into distinct blocks such as <background>, <instructions>, <constraints>, and <output_format>.

The key principle is: “concise” does not necessarily mean “short.” If a detail matters, do not omit it; make sure it appears in the right place.

Practice 3: Writing What Not to Do Matters More Than Writing What to Do

This is the part most people overlook, yet it is one of the most effective ways to control an AI agent. Reworking something the AI added but you did not want costs much more than asking it to add something it missed.

Examples of a good “do not” list:

  • This version does not need fuzzy search
  • Do not add a caching layer; this is V1
  • Do not modify the existing memory_store.py; add new files only
  • Do not introduce dependencies absent from the current requirements.txt

Practice 4: One Code Example Beats Three Paragraphs of Prose

When you want AI to follow a particular coding style or architectural pattern, point it directly to an example file. This is much more efficient than describing the style in words. For example:

## Code Style Reference
Follow the structural pattern in src/handlers/digest.py.

Practice 5: Modularize—Divide and Conquer

Do not place everything in one enormous prompt. Divide the spec by component or module, and give the AI only the part it needs for the current task. Research confirms that as the number of instructions increases, compliance with each instruction decreases—the curse-of-instructions effect.

Advanced technique: create an Extended TOC. Ask the agent to generate a condensed summary of the spec containing only the key point of each section. This summary can remain in the prompt as a “mental map,” while details are supplied on demand.

Practice 6: A Spec Is a Living Document, Not a One-Off Artifact

When AI makes a design decision during implementation, or when you decide to remove a feature, update the spec. Commit the spec to Git and version it. A spec is not only for AI; it also helps you, the developer, retain a global understanding of the project.

Practice 7: State Exact Technology Versions

Say “React 18 with TypeScript, Vite, and Tailwind CSS,” not “a React project.” Include version numbers and key dependencies. An ambiguous spec produces ambiguous code.

Practice 8: Acceptance Criteria Must Be Testable

The best acceptance criteria can be translated directly into test cases:

## Acceptance Criteria
1. Send /recall without arguments → return usage instructions
2. Send /recall <existing keyword> → return 1–3 results with timestamps and summaries
3. Send /recall <nonexistent keyword> → return a friendly no-results message

Even better, have the AI generate the tests at the same time, creating an automatic verification loop.


6. A Best-Practice Software Spec Template

Combining GitHub Spec Kit’s four-stage model, Addy Osmani’s hybrid PRD+SRS approach, Kiro’s three-document structure, and the findings from more than 2,500 agents.md files, the following is a practical template for mainstream AI coding agents such as Claude Code, Codex CLI, and Cursor:

# [Feature/Project Name] — Software Spec

## 1. Goals and Context (Why & What)
### Goal
One-sentence summary: [What problem should this feature/project solve?]

### Context
- Users: [Target user profile]
- Pain point: [What problem do users face without this feature?]
- Success criteria: [How will “success” be defined after completion?]

### Scope
- This is a [new project / new feature in an existing project / bug fix]
- Current version: V[x], focused on [core value] rather than perfection

---

## 2. Technical Constraints
### Technology Stack
- Language: [e.g., Python 3.11]
- Framework: [e.g., python-telegram-bot 21.x]
- Database: [e.g., PostgreSQL 15 via Supabase]
- Key dependency: [e.g., mem0ai SDK 0.1.x]

### Runtime Environment
- [e.g., Docker container on GCP Cloud Run]
- [e.g., local development on macOS + Python venv]

### Existing Architectural Constraints
- Place new files in: [e.g., src/handlers/]
- Follow the existing pattern: [e.g., use the structure of src/handlers/digest.py]
- Entry-point registration: [e.g., register the new handler in src/main.py]

### Known Technical Hazards
- [e.g., Mem0's search API returns at most 10 results and does not support pagination]
- [e.g., python-telegram-bot v21 has breaking API changes; do not use v20 syntax]

---

## 3. Command Reference
```bash
# Build
npm run build        # or python -m build

# Test
pytest -v            # all tests must pass before committing

# Lint
ruff check --fix .   # automatically fix formatting issues

# Run locally
python -m src.main   # start the development server
```

---

## 4. Interface and Data Contract
### Input
[Precisely describe the shape, source, and format of the input]

### Processing Logic
[Pseudocode or flow description of the core processing steps]

### Output
[Precisely describe the shape and format of the output, including an example]

### Error Cases
- When [Condition A] → [Behavior A]
- When [Condition B] → [Behavior B]

---

## 5. Boundaries
### ✅ Always
- Run tests before committing
- Follow the existing code style
- Every new file must have a corresponding test file

### ⚠️ Ask First
- Database schema changes
- Adding a third-party dependency
- Modifying a public API

### 🚫 Never
- Never commit secrets or tokens
- Never modify node_modules/ or vendor/
- Never modify CI/CD configuration files
- Never introduce dependencies absent from the current dependency list unless approved under ⚠️

### Functional Boundaries (Out of Scope for This Version)
- [e.g., do not add a caching layer]
- [e.g., do not support pagination]
- [e.g., do not implement internationalization]

---

## 6. Code Style Reference
[Provide a real code sample as a style reference; it is more effective than three paragraphs of prose]

```python
# Reference: src/handlers/digest.py
async def handle_digest(update: Update, context: CallbackContext):
    """Handle the /digest command and return today's summary."""
    user_id = update.effective_user.id

    try:
        digest = await digest_service.get_daily(user_id)
        await update.message.reply_text(
            format_digest(digest),
            parse_mode="Markdown"
        )
    except ServiceError as e:
        logger.error(f"Digest failed for {user_id}: {e}")
        await update.message.reply_text("Failed to retrieve the summary. Please try again later.")
```

---

## 7. Acceptance Criteria
[Use GIVEN-WHEN-THEN or directly describe testable scenarios]

1. **Happy path:** [Input X] → [Expected output Y]
2. **Edge case:** [Empty/invalid input] → [Expected error handling]
3. **Integration verification:** [Confirm interaction with existing modules]
4. **Structural compliance:** [Confirm new code follows the specified architectural pattern]

---

## 8. References
- [Relevant API documentation excerpt]
- [Path to an existing code example]
- [Context for design decisions]

7. A Practical Example Using the Template

Here is a spec for a real project using the template above: a memory-retrieval feature for a Telegram bot.

# /recall Command — Software Spec

## 1. Goals and Context
### Goal
Implement a /recall command for the hey!stalker Telegram bot so users can actively retrieve information from semantic memory.

### Context
- Users: individual users of hey!stalker
- Pain point: users can currently only wait passively for the bot to push information and cannot search past memories themselves
- Success criteria: return the top three relevant memory snippets within three seconds after a user enters keywords

### Scope
- A V1 feature for an existing project
- Focus on basic search, not advanced semantic understanding

---

## 2. Technical Constraints
### Technology Stack
- Python 3.11, python-telegram-bot 21.x
- Mem0 via mem0ai SDK, PostgreSQL via Supabase

### Existing Architectural Constraints
- Place the new handler in src/handlers/recall.py
- Register it in src/main.py
- Follow the structural pattern in src/handlers/digest.py
- The Mem0 client is already initialized in src/clients/mem0.py

### Known Technical Hazards
- The Mem0 search API returns at most 10 results per call; the limit argument is supported
- python-telegram-bot v21 uses async handlers

---

## 3. Command Reference
```bash
pytest -v tests/
python -m src.main
```

---

## 4. Interface and Data Contract
### Input
The user sends the following in Telegram: `/recall <query_string>`

### Processing Logic
```
1. Parse query_string (the text after /recall)
2. Call mem0_client.search(query=query_string, user_id=user_id, limit=3)
3. Format the returned results
```

### Output Format
```
📌 Memory 1 (2025-06-15)
Discussion about machine learning: covered the essence of the Transformer attention mechanism...

📌 Memory 2 (2025-06-10)
Reading notes: the central contribution of the paper Attention Is All You Need...
```

### Error Cases
- /recall without arguments → “Usage: /recall <keyword>, for example /recall machine learning”
- No search results → “No relevant memories found. Try another keyword?”
- Mem0 API error → “Memory retrieval is temporarily unavailable. Please try again later.”

---

## 5. Boundaries
### 🚫 Functional Boundaries (Out of Scope for This Version)
- Do not add caching
- Do not add pagination
- Do not implement fuzzy spelling correction
- Do not modify existing files except to register the route in main.py
- Do not introduce new dependencies

---

## 6. Code Style Reference
Refer to src/handlers/digest.py, as specified under Technical Constraints

---

## 7. Acceptance Criteria
1. /recall without arguments → return usage instructions
2. /recall machine learning (assuming relevant memories exist) → return 1–3 results, each with a timestamp and summary
3. /recall xyzabc123 (a nonexistent keyword) → return a friendly no-results message
4. The new handler's structure matches digest.py
5. tests/test_recall.py covers the three scenarios above

8. Common Pitfalls and Responses

Pitfall 1: An Overly Long Spec Can Be Harmful

AI does not distribute its attention evenly: the beginning and end receive more attention than the middle. If a spec exceeds one page, consider breaking it into multiple tasks, each with a concise spec.

Response: create an Extended TOC for a long spec. Condense each section into one or two key sentences that remain in context as the agent’s “mental map,” and provide the details only when needed.

Pitfall 2: Putting Implementation Details in the Spec

A spec should describe What and Why, not How. State the objective and constraints, and let the AI choose the implementation. That is part of the reason for using an AI agent.

Exception: when you know the AI is likely to choose the wrong implementation, you must specify How explicitly. Use this test: would an experienced engineer taking over the project for the first time make a mistake here? If so, the AI will too, so include it in the spec.

Pitfall 3: Assigning Too Large a Task at Once

Even with a good spec, AI output degrades significantly when the scope is too large, such as “implement the entire user system.” Break the work into small tasks, give each task its own spec, and execute them in dependency order.

Pitfall 4: Ignoring the Spec After Writing It

A spec must remain a living document. When AI makes a design decision you did not anticipate during implementation, or when requirements change, update the spec. Otherwise, the spec and code diverge and the spec ceases to be the source of truth.

Pitfall 5: SDD Tools Can Overengineer

An evaluation on Martin Fowler’s technology blog identified a practical problem: tools such as Kiro and Spec Kit sometimes generate large numbers of Markdown files for a simple bug fix, including multiple user stories and more than a dozen acceptance criteria. For small tasks, this is using a sledgehammer to crack a nut.

Response: adjust the level of detail to the size of the task. A simple bug fix may need only a few lines; a complex feature may justify the complete four-stage workflow. The form of the spec serves its purpose—it is not the purpose itself.


9. The State of the SDD Ecosystem (April 2026)

The Spec-Driven Development tool ecosystem is developing rapidly:

GitHub Spec Kit is currently the most mature open-source SDD framework. It provides CLI tools and templates, supports mainstream agents including Claude Code, Codex, Cursor, and Copilot, and structures development through a four-stage gated workflow: Specify → Plan → Tasks → Implement. It is agent-agnostic, so you can use whichever agent you prefer.

AWS Kiro is an IDE based on VS Code/Code OSS with a built-in SDD workflow. Its three-document structure—requirements.md → design.md → tasks.md—is more intuitive, and it supports “steering files” as persistent project-level configuration. Kiro also supports agent hooks: automated agent tasks triggered by file changes or commits.

OpenSpec positions itself as a lighter alternative, emphasizing flexible iteration rather than strict stage gates, which makes it better suited to brownfield projects with existing codebases.

As an open standard, AGENTS.md has been adopted by more than 60,000 open-source projects. It is not an SDD framework, but a common format that helps agents understand project conventions—effectively a “README for AI.”

One caveat: the term SDD still lacks a unified definition, and implementations differ greatly among tools. The core principle—write the spec before the code—is widely accepted, but the industry is still exploring the right spec structure, level of detail, and long-term maintenance strategy.


10. The Career Value of Writing Good Specs

If you are transitioning into AI application engineering, spec-writing ability offers three kinds of value:

First, it directly improves the quality of portfolio projects. A better spec enables you to use an AI agent more efficiently to produce higher-quality code, bringing portfolio projects to a demo-ready state faster.

Second, it is prompt engineering practiced as engineering. When an interviewer asks about prompt-engineering experience, saying “I use spec-driven development to direct AI agents, including structured requirement definition, explicit technical constraints, boundary control, and testable acceptance criteria” is far more persuasive than saying “I know how to tune temperature.”

Third, it is one of an engineer’s core skills in the AI era. More and more teams are incorporating AI coding agents into their daily workflow. “Can you direct AI to write code effectively?” is becoming a dimension of engineering capability as important as “Can you write good code yourself?”


References

  • Addy Osmani, “How to write a good spec for AI agents,” January 2026
  • GitHub Blog, “Spec-driven development with AI: Get started with a new open source toolkit,” September 2025
  • GitHub Blog, “How to write a great agents.md: Lessons from over 2,500 repositories,” November 2025
  • Martin Fowler (Birgitta Böckeler), “Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl,” 2025
  • Kiro Documentation, “Specs,” 2026
  • Zencoder Docs, “A Practical Guide to Spec-Driven Development,” 2025