My day job sits somewhere between software engineering and DevOps: building APIs, automating infrastructure, wiring complex systems together. I’ve worked with Node.js, C#, cloud services, and CI/CD pipelines long enough that starting a new project usually feels mechanical.
Then AI coding tools arrived, and something became obvious fast:
AI is only as reliable as the structure you give it.
Vague requirements produce vague code. Fuzzy architecture produces improvised implementations.
After enough frustrating sessions with Claude Code, Copilot, and Cursor, I started wondering: what if I stopped treating the spec as an afterthought and made it the foundation?
That’s the core idea behind Spec‑Driven Development (SDD). To test it, I picked a project that was complex enough to be real but contained enough to finish — a Rubik’s Cube solver and teaching app. And to make it a genuine test, I built it in Dart and Flutter, a stack I’d never touched.
What Spec‑Driven Development Actually Is

SDD isn’t a framework or a tool. It’s a discipline built on one rule:
Write the full specification first. Then implement it exactly as written.
No improvising. No “I’ll figure this out as I go.” No writing code that doesn’t map to a task.
The spec becomes everything:
- Architecture
- Requirements
- Acceptance criteria
- Documentation
- Source of truth
Every implementation step references a task. Every change gets justified by updating the spec first. Think of it like TDD, but for behaviour and architecture rather than unit tests.
The spec drives. The implementation follows.
Why Flutter? Why a Rubik’s Cube?
The Rubik’s Cube problem has properties that make it a perfect SDD test case:
- Deterministic
- Visually verifiable
- Multi‑layered
- Demands strict separation of concerns
There’s a clear domain layer (cube model + solver), a clear application layer (use cases), and a clear UI layer (what the user sees). It’s hard to muddy those boundaries without the system breaking.
Flutter and Dart were intentional friction. When you know a framework well, you fill gaps from memory. When you don’t, every gap sends you to documentation or Stack Overflow — exactly the kind of drift SDD is supposed to prevent.
I wanted to know whether a complete spec could substitute for framework familiarity.
The answer, mostly, was yes.
How I Structured the Spec
Before touching any code, I wrote the full specification across three layers.

Domain Layer (D1–D8) — pure logic, no UI, no side effects
- D1: Cube representation
- D2: Move application
- D3: Debug UI (for early visual testing)
- D4: State validation
- D5: Solver
- D6: Scramble generator
- D7: Teaching mode
- D8: Full application UI
Application Layer (A1–A3) — orchestrates the domain
- A1: Validate cube
- A2: Solve cube
- A3: Generate scramble
UI Layer (U1–U4) — Flutter screens
- U1: Cube editor
- U2: Validation feedback
- U3: Solver playback
- U4: Teaching mode
Writing this took longer than expected — not because the tasks were hard to define, but because SDD forces you to think clearly before you’ve built anything. A few times I started speccing out a task and realised I didn’t actually know what it needed to do.
The spec exposed gaps in my thinking before they became gaps in the code.
What Building It Actually Looked Like

The Domain Engine (D1–D2)
The first step was an immutable cube model and the logic to apply moves.
class Cube { final Map<MoveFace, List<List<CubeColour>>> faces; Cube applyMove(Move move) { // Returns a new Cube with the move applied }}
Immutability was deliberate. It made the solver and teaching mode dramatically easier to reason about because any given cube state could be inspected without worrying about what happened to it later.
Debug UI (D3)
Before building the full UI, I created a simple debug view that displayed all six faces in a flat layout and let me apply moves with buttons.
It caught a rotation bug that would’ve been nearly impossible to trace through the solver.
Validation (D4)
A gatekeeper that checked:
- Colour counts
- Structural integrity
- Basic solvability
Solver (D5)
A beginner‑method solver that output:
- A flat move list
- A structured breakdown for teaching mode
Teaching Mode (D7)
The domain doesn’t render anything — it returns structured highlight data:
class Highlight { final MoveFace? activeFace; final Set<StickerRef> stickers;}
This single design choice made the UI layer dramatically simpler. The domain told the UI what to show. The UI just showed it.
The Rough Patches
My initial Flutter layout logic was wrong in ways I didn’t understand until I’d already built three screens on top of it. Fixing it meant revisiting the spec, updating tasks, and re‑implementing.
Frustrating? Yes. But it kept the architecture honest.
With SDD, a bad decision can’t quietly metastasize through the codebase. It’s visible in the spec, which forces a real decision:
- Change the spec and re‑implement
- Or live with the constraint
What It Did to the AI Tools
This was the part I was most curious about.
With a complete spec, Claude Code behaved differently. Not occasionally — consistently.
It stopped hallucinating features. It stopped inventing architecture. It stayed within the bounds of the task I referenced.
Without a spec, AI tools fill ambiguity with plausible‑sounding improvisation. With one, there’s nothing to improvise.
The practical upside: debugging became faster. When something didn’t work, I wasn’t chasing a moving target. I compared the behaviour against the spec, found the task that defined it, and checked whether the implementation matched.
Usually it did — and the bug was a logic error. Occasionally it didn’t — and the fix was obvious.
What I’d Do Differently
- I underestimated the upfront cost of writing a complete spec.
- Some early tasks were too vague and needed rewriting mid‑build.
- I’d push the debug UI (D3) even earlier — it was invaluable.
One thing I wouldn’t change: the strict separation between domain logic and UI. It feels bureaucratic on day one and invaluable on day ten.
When SDD Is and Isn’t the Right Call
SDD is the wrong tool for early‑stage prototyping where the problem isn’t well understood. If you’re still discovering what you’re building, writing a complete spec first is premature.
SDD is the right tool when:
- The domain is complex and deterministic
- Separation of concerns matters
- You’re working with AI coding tools
- You’re learning a new framework
- The system will be maintained over time
For this project, it was the right call.
Whether it holds up in messier real‑world environments — legacy codebases, multi‑agent workflows, API‑driven systems — is what I want to find out next.
Leave a Reply