Kamil Józwik

SDD in practice

A practical guide to implementing Spec-Driven Development in real-world projects.

dev

We've all heard the promise of AI-powered development: faster cycles, automated coding, and a hyper-efficient workflow. But to get there, we need to speak the AI's language - the language of precision.

Spec-Driven Development (SDD) is the key here. How do you actually use it on a Monday morning when you're starting a new project?

This guide will walk you through the entire lifecycle of an SDD project, using a familiar example: a new full-stack Next.js application. I'll cover how to start, how to keep the spec alive and useful, and how to scale it without losing your mind or your AI's context window.

All the below is based on my own experience, so consider it a starting point. Your mileage may vary, and the best practices will evolve as tools improve.

Laying the foundation on a fresh project

You're at git init. Your project is an empty folder. Your first task isn't to write app/page.tsx, but to create spec.md. This initial document is your project's constitution. It doesn't need to be perfect or exhaustive, but it must establish the core vision.

For our Next.js app, your initial spec.md would be a high-level overview. Forget implementation details for now; focus on the big picture. You'd define sections like:

  • Project vision & goals: What is this app? A simple social media dashboard. What's its primary goal? To allow users to post short messages and see a feed. This is the "why" that guides every future decision.
  • Core user stories: Briefly outline the main user journeys. "As a user, I want to sign up for an account." "As a user, I want to post a message of up to 280 characters." "As a user, I want to see a reverse-chronological feed of posts."
  • Architectural principles: State your key technical decisions. We're using Next.js with App Router. Authentication will be handled by NextAuth.js. We'll use Prisma as our ORM with a PostgreSQL database. The styling will use TailwindCSS. This gives the AI guardrails.
  • Initial data models: A simple sketch of your database schemas. A User table (id, email, name) and a Post table (id, content, authorId). This establishes the fundamental data structures.

At this stage, the spec is a concise, high-level brief. It’s just enough information for you or an AI to start scaffolding the project without getting lost in the weeds.

The spec as a living, breathing partner

A spec that isn't updated is worse than no spec at all. Its value comes from it being a perpetually accurate reflection of the codebase. How do you achieve this without it becoming a chore?

When adding a new feature, the cycle is Spec First. Before writing a line of code for, say, a new "likes" feature, you first update spec.md. You'd add a new user story ("As a user, I want to like a post") and update the data model (add a Like table). Now, the spec contains the blueprint for the feature, which you can hand to your AI assistant with a prompt like, "Implement the 'likes' feature as defined in the updated spec."

But what about keeping it in sync during refactors or when a human writes the code? This is where modern tooling shines.

  • Human-in-the-Loop sync: If you write the code manually, the process is reversed. After your code is complete and tested, your last step before committing is to ask your AI assistant: "Based on the latest git diff, please update the spec.md to reflect the changes made." The AI can parse the changes and propose updates to the relevant sections, turning a documentation task into a quick review.
  • Automated sync with CI/CD: For a more advanced setup, you can integrate this into your CI pipeline. A GitHub Action could trigger on a pull request, using an AI to compare the code changes against the spec. The action could fail the check if the code implements something not specified, or it could even auto-generate a suggested spec update and post it as a comment on the PR. This enforces discipline and ensures the spec and code never drift apart.

Taming complexity as your project grows

A single spec.md for a large application would be a nightmare. It would be impossible to navigate and would quickly exceed the context window of any AI model, leading to poor and out-of-context results. The key to scaling is to modularize your spec.

Think of your main spec.md as a hub. It contains the core vision, architecture, and principles, but it links out to more detailed documents.

  • Split by feature: Create a /specs directory. For each major feature, create a dedicated file, like /specs/features/authentication.md or /specs/features/posts.md. The main spec file would simply link to these. This keeps each document focused and manageable for the AI's context.
  • Separate cross-cutting concerns: Create dedicated files for concepts that span the entire application. You might have /specs/architecture.md for infrastructure details, /specs/database.md for the full schema, and /specs/design_system.md for UI components and styling rules.
  • Maintain a project structure manifest: A very useful, AI-friendly document is PROJECT_STRUCTURE.md. This file isn't about what the code does, but where it is. It's a simple, tree-like representation of the file and directory structure with a one-line description for each. This gives the AI a map of the codebase, helping it to place new files correctly and find existing code to modify. You can easily generate and update this file with a simple script or ask the AI to do it.

This hub-and-spoke model keeps individual spec files small and focused, making them perfect for an AI's limited context while still providing a comprehensive overview of the entire system.

From whiteboard to actionable tasks

Before you can build a feature, you need to design it. This brainstorming phase is a perfect opportunity for AI collaboration. You can start a conversation with your AI agent like a virtual whiteboarding session.

Prompt it with: "Let's design a notification system for our app. The requirements are X, Y, and Z. What database schema changes would we need? What API endpoints should we create? What are the potential edge cases?"

During this conversation, the AI helps you explore ideas, refine requirements, and define the technical approach. The crucial step is at the end. Your final prompt should be: "Based on our conversation, please generate the content for a new spec file at /specs/features/notifications.md and summarize the required changes for /specs/database.md." This ensures your creative brainstorming session is immediately captured in the formal spec before any implementation begins.

Once the spec is updated, the next step is to create a task list. Manually breaking down a spec into Jira tickets or GitHub Issues is tedious. Instead, you can ask the AI: "Read /specs/features/notifications.md and generate a list of actionable development tasks with clear acceptance criteria."

You can then decide where to store these tasks. For maximum efficiency, this should be automated. By granting the AI agent access to your project management tool's API (often via a secure intermediary like an MCP server), it can automatically create the tickets in Jira, issues in GitHub, or tasks in Linear, assigning them and setting priorities based on your instructions.

Weaving quality into the fabric

A spec is also your ultimate tool for ensuring quality through tests, design patterns, and code reviews.

  • Tests driven by the spec: When a feature is defined in the spec, you can use it to generate the tests first. Your prompt could be: "Based on the user stories and acceptance criteria in /specs/features/authentication.md, generate a complete set of unit and integration tests using Jest and React Testing Library." When the spec changes, you repeat the process, ensuring your test suite is always aligned with the documented requirements.
  • Defining design patterns: Your spec is the perfect place to enforce consistency. In your main spec.md or a dedicated /specs/coding_standards.md, you can define the patterns you want to use. For example: "All Next.js API route handlers must follow the repository pattern for database access." or "All React components should use custom hooks for stateful logic." By stating this in the spec, you can instruct the AI to adhere to these patterns when generating code, ensuring a clean and maintainable codebase.
  • AI-powered code reviews: Code reviews become faster and more effective. Instead of just human review, you can incorporate an AI step in your PR process. The AI's job is to act as a compliance officer. Its primary task is to answer one question: "Does the code in this pull request correctly and completely implement the requirements laid out in the relevant spec file?" It can flag deviations, point out missed edge cases defined in the spec, and verify that the code follows the prescribed design patterns. This frees up human reviewers to focus on more nuanced aspects like logic, readability, and overall strategy. Nowadays we can use Claude Code, Gemini and of course GitHub Copilot for this, which make AI-powered code reviews fairly easy to implement.

The architect of intent

In a world of Spec-Driven Development, your role isn't diminished; it's elevated. You spend less time on the mundane mechanics of writing boilerplate code and more time on what truly matters: thinking, designing, and architecting elegant solutions. You become the author of the blueprint, the master of intent, who guides powerful AI tools to execute your vision with precision and speed. The spec is your instrument, and with it, you conduct the symphony of software development 🎶