DEV Community

Cover image for Spec-Driven Development with GitHub Spec Kit
rosidotidev
rosidotidev

Posted on

Spec-Driven Development with GitHub Spec Kit

As a software architect and senior software engineer, I find myself in the middle of a change that I can only describe as epochal. The way we think about software development and enterprise architecture is shifting, and so are the roles that humans and "intelligent systems" play today and will play tomorrow.

My angle is Spec-Driven Development (SDD) with GitHub Spec Kit, seen from where I work every day: software architecture. In this first article I stay on the theory, with a small example of SDD to make it concrete. In the next ones I build a real application with Spec Kit. GitHub Spec Kit is not the only option: alternatives include Kiro, OpenSpec and BMAD. For now, though, GitHub Spec Kit is the one that best fits my preferences and the way I like to work. This is a personal choice, not a claim that one approach is universally better than the others. I wrote this article to share my view and to set a personal starting point that I can come back to (and probably correct) while things evolve.

What Is Spec-Driven Development?

Let's start from the problem. Ask an AI agent to "add user registration to my app" and it has to guess: which fields, which password rules, is the email verified, how are accounts stored? The result is often plausible-looking code resting on dozens of unstated assumptions, many of them wrong. This is what people call vibe coding.

To me, SDD is the answer. It is an approach where a detailed, human- or AI-validated specification becomes the single source of truth before any code is written. Instead of documentation as an afterthought, the process is inverted: the specification is an executable blueprint that drives the AI coding agents.

Not every SDD approach keeps the spec alive for the same amount of time. I've found a classification featured in numerous articles to be incredibly helpful to distinguish three levels of rigor:

  • Spec-first: a well-thought spec is written before coding to guide the initial implementation. Once the code exists, the spec may be discarded or drift: its value is the initial clarity, which makes it a good fit for AI-assisted first builds and prototypes.
  • Spec-anchored: the spec is kept and evolves with the software, through automated tests and contracts. A change in behavior updates both, and tests derived from the spec (BDD scenarios, or OpenAPI with contract testing) fail when they drift. It suits most production systems well.
  • Spec-as-source: the spec is the main source file and agents generate or update code directly from its changes. Humans edit only the spec and never the generated code, so drift is eliminated by design. It needs mature, trusted generation tooling and today is practical mainly where generation is well defined, such as server stubs from OpenAPI.

My rule of thumb: use the minimum level of rigor that removes ambiguity in your context. Spec-first for AI-assisted initial development, spec-anchored for long-lived production systems, spec-as-source only when the generation tooling is mature and trusted.

The Specification as a Contract

This is the idea I want at the center of this series.

In a code-centric flow, requirements documents drift and design diagrams rot, so the code ends up being the de facto truth. Asking "what should this function do?" means reading the implementation. SDD flips the relationship: the specification becomes the authoritative description that humans and machines use to build and maintain the system. The spec declares the intent, the code realizes it.

I think three properties turn a specification into a contract and not just documentation:

  1. Each phase constrains the next. Specify answers what, plan answers how, then come tasks, implementation and validation. Each phase produces an artifact that guides the following one, creating a chain of accountability from intent to implementation.
  2. The spec stays the authority. When validation finds a gap, you either fix the code or revise the spec. Either way, the specification remains the reference point.
  3. It is checked, not just read. Traditional design documents are read by developers who then write code that hopefully matches them. A contract is meant to be verified: if the code diverges, something should fail.

Contracts are not new in software. API specifications such as OpenAPI have long played this role: once the API contract is agreed, frontend and backend teams can work in parallel, and any implementation that matches the contract is valid by definition. SDD brings the same logic to the whole build process.

Mental Model: the spec declares the intent, the code is an implementation detail. And I would be careful with false confidence: a passing check only proves that the code matches the spec. If the spec is wrong, the code will faithfully implement the wrong thing.

GitHub Spec Kit in a Nutshell

Spec Kit is the open source toolkit from GitHub for spec-driven development. You interact with it through commands in your coding agent, and it plugs into many of them (Copilot, Claude, Gemini, Codex and others). It can also be extended with extensions, presets, workflows and bundles.

The core process is:

Specify -> Plan -> Tasks -> Implement -> Converge

The constitution, the project-wide principles and constraints, is defined once per project. Each phase produces a Markdown artifact that feeds the next one. For production features, three optional quality gates are added.

  • clarify: Asks targeted questions about anything underspecified and folds your answers back into the spec, so you're not planning on top of ambiguity. Run it before planning, optionally with a focus area.
  • checklist: Generates a custom quality checklist These custom checklists are reviewer-owned
  • analyze: Reports conflicts, gaps, and ambiguities across spec.md, plan.md, and tasks.md

A Simple Example: a Todo List

Theory is easier with a concrete case. Take the simplest idea I can think of, a todo list, and look at what goes where. The example is mine, written just to show the separation. The syntax is the real one from the Spec Kit docs, in GitHub Copilot's default skills mode (/speckit-*). You type these commands in the chat of your coding agent, not in the terminal. Other agents may invoke them in a different way, so check the docs for your setup.

Step 1: Constitution (project-wide principles, written once):

/speckit-constitution Todo List is an adaptive web application.
Every API change starts from the REST contract.
Business logic lives in the backend, never in the UI.
Every feature comes with automated tests.
The UI must work on both phone and desktop.
Enter fullscreen mode Exit fullscreen mode

This creates or updates the project constitution: the guiding principles that every later phase is evaluated against. It also keeps the dependent templates in sync. You run it once at the beginning and you update it whenever your principles change.

Step 2: Specify (what and why, no technology):

/speckit-specify Develop a todo list application where a user can add a task,
mark it as done, delete it and filter tasks by status.
A task needs a non-empty title.
Completed tasks stay visible until they are deleted.
Out of scope for now: sharing tasks between users and notifications.
Enter fullscreen mode Exit fullscreen mode

This creates the feature specification (spec.md) from the natural-language description. It focuses on the what and the why, the user-facing behavior and the goals, and not on the tech stack. It may also maintain checklists/requirements.md, a built-in checklist that checks the quality of the spec itself.

Step 3: Plan (how, the technology belongs here):

/speckit-plan Use an Angular web application, a REST API and a backend component
that holds the business logic, on top of a relational database.
Data model: Task(id, title, status, createdAt).
Expose create, read, update and delete endpoints on /tasks.
Enter fullscreen mode Exit fullscreen mode

This runs the planning process and generates the design artifacts from the spec, including plan.md. This is the place for implementation detail: tech stack, architecture and technical constraints.

Step 4: Tasks (break the work down):

/speckit-tasks
Enter fullscreen mode Exit fullscreen mode

No arguments here: the command works from the design artifacts and generates an actionable, dependency-ordered tasks.md. The tasks are organized in phases: Setup, Foundational (the blocking prerequisites), then one phase per user story in priority order, and a final Polish phase for the cross-cutting concerns. Tests are generated inside the phase of a user story when requested, not as a separate phase, and the tasks that can run in parallel are marked. For our todo list I expect one phase for each user story (add, complete, delete, filter). Before implementing, /speckit-analyze can check the consistency of spec, plan and tasks: it is read-only and only reports conflicts and gaps.

Step 5: Implement (build it):

/speckit-implement
Enter fullscreen mode Exit fullscreen mode

This is the command that writes the code. It executes the tasks in tasks.md, running each phase in dependency order and respecting the parallel markers. Before starting, it reads the state of the checklists as a gate: if some items are unchecked it asks before proceeding, and it never changes the checklist markers. For a small feature like our todo list you can run it once and build everything. For a larger one, you can scope each run with an argument (for example only the Setup and Foundational phases), validate the result and then continue.

If you catch yourself writing "use PostgreSQL" in the specification, move it to the plan. This is the whole point: the spec says what, the plan says how. And it is exactly what makes me curious about changing only the plan later.

Step 6: Converge (check that nothing was missed):

/speckit-converge
Enter fullscreen mode Exit fullscreen mode

This assesses the codebase against the spec, the plan and the tasks to confirm that nothing was missed. It is append-only: it never edits or deletes code, and its only possible write is adding tasks to tasks.md. You run it only after implement has run on the current tasks.md. It first prints a summary of the findings graded by severity, then ends in one of two ways:

Converged: no gaps found. tasks.md stays unchanged and you can move on to the review or the pull request.
Tasks appended: gaps found. The new tasks are added under a Convergence section of tasks.md, you run implement again to complete them and then converge once more, until it reports converged.

The Role of the Human

SDD does not take people out of the loop: I think it moves the place where their judgment is applied. Accepting generated changes with minimal resistance is just vibe coding with more files. Machines take over the mechanical work: generating code, keeping artifacts consistent, detecting drift. Humans keep authority over intent, domain meaning, risk tolerance and the legal and ethical constraints that no execution trace can reveal. Those constraints have to be written explicitly into the specification, which is where human judgment is encoded and then enforced.

In practice, for me this means explicit approval boundaries: a breaking schema change, a policy shift, a refactoring proposed by the AI or a compatibility downgrade should never pass without a person's decision. Spec Kit points in the same direction. Every phase produces an artifact you review before moving on, and in its own agentic SDLC tests and scans stay deterministic automation while merge decisions stay with the maintainers.

For the engineer, I believe, this changes where value comes from: no longer the speed of writing lines of code, but the quality of the specifications they refine and validate, as an orchestrator and validator who defines the "what" upstream and approves the "how" at the boundaries.

Mental Model: bounded autonomy, not full automation. Enforcement is automated, meaning stays human.

Takeaways

If I have to summarize my starting point in a few lines:

  1. The specification is a contract. Each phase constrains the next, the spec stays the authority, and it is checked and not just read.
  2. GitHub Spec Kit gives you the workflow: Specify -> Plan -> Tasks -> Implement -> Converge, with clarify, checklist and analyze as quality gates.
  3. Humans move upstream, and their responsibility grows. People decide specifications, guidelines and architectures: engineers and architects evolve from writing code to owning what the system must do and why.
  4. Keep a reality check. Less context drift, fewer hallucinations and no technical debt from chaotic prompting are the declared goals of SDD, not guaranteed results.

In the next articles I move from theory to practice: an adaptive Angular web application that talks over REST to a backend component holding the business logic, on top of a relational database, specified and built with Spec Kit. Then I challenge myself with a port of the backend to another technology, changing only the plan, to see how far "the same specification, different stack" really holds. The REST contract, the tests, the data model and the Angular application are my fixed points: if they stay untouched, the specification survived the move. The code will be on my GitHub (rosidotidev).

Top comments (0)