What I Learned Using AI to Write a Production Feature Spec
Lessons from speccing a POPIA-constrained document pipeline with AI: two-session structure, adversarial reviews, and reading the code first.
I’ve been using AI in my dev workflow for a while now. This isn’t a post about discovering it. Instead, it’s about a recent session that forced me to get completely deliberate about how I use it—specifically for business analysis and feature specs, which is vastly messier and harder to evaluate than generating code.
Here’s the context: I was speccing out a greenfield service for a document processing pipeline. The service takes OCR-extracted text from PDF documents (up to 1,000 pages), runs it through a map-reduce LLM extraction pipeline, validates the output against cross-section business rules, and drops structured JSON onto a downstream queue. It runs on Azure, entirely within a private network, with zero public ingress or egress.
That last constraint isn’t a preference; it’s a strict POPIA requirement. In South Africa, personal info cannot leave the country’s Azure region. Every architectural decision touching data flow suddenly has a massive compliance dimension. Azure OpenAI in South Africa North, private endpoints everywhere, no external API calls.
Constraints like that change how you design a system, and they drastically change what your spec actually needs to cover. Vague infrastructure diagrams don’t survive a POPIA audit.
Splitting the Process: The Two-Session Structure
I ended up using Gemini for the conceptual heavy lifting and Claude Code for grounding the spec against the actual codebase.
This was a purely practical choice: I didn’t want to burn Claude tokens on the exploratory phase before I even understood the problem or had a candidate solution. Conceptual design is messy. You run down dead ends, pivot the tech stack, and throw away whole paragraphs. Running that through a tool with direct codebase access—and a tighter token budget—before you know what you’re building is just wasteful.
Gemini handled the exploration. By the time I opened a Claude session, I already had a coherent V1 spec and a highly specific list of things to validate against the code. Claude spent its tokens on grounding and correcting, not on figuring out the architecture from scratch.
- Conceptual sessions are about reasoning. What does this system need to do? What’s the riskiest step? What fails at 1,000 pages that works perfectly fine at 10? You don’t need codebase access for this. You need a model that can hold a complex architecture in its context window and actively push back on your weak assumptions.
- Grounding sessions are about evidence. You have a draft spec. Now you need to check every single assumption against the real system it’s going to live in. What does the existing failure handler actually do? What are the queue field names, and are they camelCase or snake_case? Doing this without reading the source files means you’re speccing from memory. And memory is usually wrong in ways you won’t notice until a developer asks a question you can’t answer.
My biggest mistake initially was collapsing these into one session. The result? Two full rewrites. Not because the design was terrible, but because the context came in way too late. Iteration is part of the job; rewriting from scratch because you forgot to ask a foundational question is entirely avoidable.
Adversarial Persona Reviews
The highest-leverage technique I used was asking Claude to review the spec, acting as a grumpy, battle-scarred solutions architect. The prompt wasn’t to “review this”—it was to genuinely hunt for flaws and tear it apart.
I ran this loop four times, and each round surfaced completely different gaps:
- Round one: Missing RBAC roles, undefined failure modes.
- Round two: Incorrect blob permissions, wrong queue naming conventions.
- Round three: The CI/CD section described a branching strategy that didn’t actually match our real workflows.
- Round four: The failure cleanup behaviour directly contradicted the existing codebase.
Each round found something new because the previous round’s fixes created a new version of the spec. A fresh adversarial pass on the updated version found things the previous pass couldn’t see. If I had stopped after round one, I would have shipped a spec with massive holes in it.
I also ran a separate pass acting as a strict Business Analyst, which caught totally different issues: ambiguous scope boundaries, implicit assumptions, and dependencies without named owners. SA and BA reviews are fundamentally different jobs. Running them as isolated passes with specific briefs gave me infinitely cleaner feedback than a generic “tell me what’s wrong with this” prompt.
Read the Code Before You Write the Spec
This is the one rule I am carrying into every future grounding session.
Every single spec correction I made after reading the relevant source file was right the first time. Every correction I made based on an assumption required at least one follow-up fix.
The failure handling section was the perfect example. I wrote it based on what I assumed the existing Python worker did. When I actually opened the exception handler, the real behaviour was different in three distinct ways (what gets deleted on failure, queue-level retries, and job status updates). My drafted spec was highly plausible, but completely wrong.
The fix is mechanical: before you write a section claiming how an existing system works, read the code that implements it. Do not ask the AI to infer it. Do not rely on your memory. The source file takes thirty seconds to read and eliminates an entire category of rework. Now, I front-load this. I read the failure handler, data contracts, and deployment workflows before I even open the spec document.
The Decision Log
In most spec processes, the reasoning behind a technical decision is either undocumented or buried in a Teams meeting that nobody will remember in six months. The spec records what the system does. The why disappears.
During these sessions, I forced myself to produce an architectural decision log right alongside the spec. Not as a post-mortem retrospective, but rather as a live document written at the exact moment of decision. Every entry logs what was chosen, what was rejected, and why.
On a project with strict POPIA constraints and a long-term client handoff, this matters way more than it does on a typical greenfield startup build. Why South Africa North? Why private endpoints for everything? Why zero public surface? None of that is self-evident just from reading the spec. The decision log is where the compliance reasoning actually lives. Going forward, this isn’t optional for me.
The Process Methodology as Output
The final thing I did, which I haven’t normally done, was write down the actual process while it was still fresh. Not what I built, but what I did, in what order, and why certain steps produced value while others just generated noise.
That document now lives in my company’s “How-To’s” repo. The next feature I spec will start from that methodology, rather than a blank page. If you write down the process, the process improves. If you just grab the final output and move on, you’re starting from zero every single time.
The New Baseline: What Changes Going Forward
Concretely, here is my new default workflow:
- Strict two-session structure: Design session for conceptual reasoning, grounding session for codebase validation. Clear handoffs, never one massive, messy conversation.
- Front-load the context: Before drafting a single word, define the problem, scale constraints, hard non-negotiables (regulatory, infra), and the long-term owner’s tech stack.
- Sequential adversarial reviews: Technical correctness first, then developer clarity, then gap analysis. Repeat the technical review until the AI comes back clean.
- Evidence-based claims: Read the source code before writing any spec section that touches an existing system.
- Simultaneous decision logging: Written at the time of the decision, updated during codebase grounding, kept forever.
- Process as a deliverable: Treat the methodology document as a living output that gets updated after every major spec session.
AI in spec work is not a shortcut. It is a thinking partner that can hold a massively complex system in context, apply consistent adversarial pressure, and read source code on demand. But the quality of its output is determined almost entirely by the discipline of your session structure.
Get the structure right, and two sessions will produce something a developer can actually build from. Get it wrong, and you’ll just move really fast through a terrible design.
At One Eleven, we build software the same way we think about it: code is the medium, value is the point. We work to make sure clients never walk out of a review wondering what it was all for.
Start a conversationMichael Shepherd
COO / CTO
Lives between the business problem and the build, keeping operations tight and the technology pointed at outcomes.