We’ve all experienced that magical moment with modern AI coding assistants like Claude Code, Antigravity, or Codex: you prompt a high-level feature, watch files stream into existence, and within thirty seconds, you have a running prototype.

And then the hangover sets in.

You asked for a straightforward user management feature:

“As an administrator, I want to edit user profiles so that account details stay up to date.”

The AI produced 800 lines of code. It built a slick modal. But under the hood:

  • It missed the invariant that changing an email address must invalidate active authentication sessions.
  • It invented its own ad-hoc error payload instead of following your existing API envelope.
  • It generated unit tests that only mocked internal functions rather than asserting real database state mutations.
  • When an edge case occurred—like a duplicate email collision—it silently crashed with an unhandled 500 error.

Searching the web for solutions, you get flooded with “prompt engineering” tricks: 10-page mega-prompts, few-shot prompt chaining, or complex autonomous multi-agent frameworks.

I write this post to show you how straightforward the root cause really is, and how a pragmatic, decades-old software engineering methodology completely eliminates this friction when working with AI coding agents. You don’t need magic prompt spells or recursive agent loops—you just need the right specification primitives.


Step 1: Understand the Communication Gap

Why do LLMs struggle with standard Agile User Stories?

User Stories were deliberately designed as “a promise for a conversation” between human product managers and human developers. Humans bring implicit context: domain knowledge, security instincts, unstated invariants, and common sense. When a story says “manage users”, a human developer naturally checks for unique email constraints and session lifecycles.

LLMs do not have human intuition. When given a loose requirement, an LLM fills the gaps by sampling probabilities. It guesses boundaries, hallucinates error-recovery paths, and silently drifts from your architecture.

The agent didn’t fail because it’s bad at coding. The agent failed because User Stories are conversational notes, not deterministic behavioral contracts.


Step 2: Formal Use Cases

Decades ago, software engineering pioneers like Alistair Cockburn formalized the Use Case specification for RUP (Rational Unified Process). While agile teams later streamlined them into lightweight user stories for human standups, formal use cases happen to be the exact mathematical shape AI agents need to execute deterministically:

                  ┌───────────────────────────────┐
                  │      PRECONDITIONS (Guards)   │
                  └──────────────┬────────────────┘
                                 │
                                 ▼
    Actor Action ───► System Verification ──────────────► Persisted State Mutation
                                 │
                                 ▼ (if check fails)
                  ┌───────────────────────────────┐
                  │    ALTERNATIVE FLOW (Branch)  │
                  └───────────────────────────────┘
                                 │
                                 ▼
                  ┌───────────────────────────────┐
                  │     POSTCONDITIONS (Tests)    │
                  └───────────────────────────────┘

A formal Use Case contains four essential guardrails that an LLM can parse without ambiguity:

  1. Explicit Preconditions (PRE1, PRE2...)
    Guards that define the mandatory system and session state before execution begins (e.g., “Active session with role Admin exists”).
  2. Atomic Alternating Steps
    Step 1: Actor performs action → Step 2: System validates & responds → Step 3: Actor proceeds. No conversational multi-step paragraphs.
  3. Verifiable System Checks
    Every system validation states exact conditions and links directly to a numbered Alternative Flow if the condition fails.
  4. Verifiable State Postconditions (POST1, POST2...)
    Explicit assertions about persistent state mutations (“User record updated”“Audit event logged”“No persistent data modified on failure”). These map 1:1 to automated test assertions.

Step 3: Meet the Use Case Triad

To make this workflow seamless, I’ve packaged this methodology into a composable suite of three agent skills that work in lockstep:

1. use-case-expert (Authoring & Auditing)

Acts as your requirements engineering partner. It interviews you, extracts business boundaries, and authors structured UC-XXX Markdown specifications. Crucially, it audits requirements against a built-in de-vagueing dictionary (vague-terms.md), replacing fuzzy words like “fast”“secure”, or “user-friendly” with concrete, testable criteria.

2. use-case-implementer (TDD & Vertical Slices)

The construction engine. It takes an approved UC-XXX specification, verifies that all preconditions and postconditions are complete, and generates a strict Test-Driven Development (TDD) test suite followed by clean vertical slice code.

3. use-case-reverse-engineer (Legacy Code Reconstruction)

Working on an undocumented or legacy system? This skill analyzes your controllers, routes, validation schemas, and database calls to reverse-engineer clean, consolidated UC-XXX specifications, giving you a safe baseline before refactoring.

A full walkthrough on Spec Driven Development with Use Cases can by found in this post: https://www.processworks.ch/specification-driven-development-with-use-cases-and-ai/


Step 4: Concrete Example — Before vs. After

Let’s see the difference in practice with a classic scenario: Password Reset Flow.

Before: The Loose User Story

"As a user, I want to reset my password via an email link so I can regain access to my account.
The link should expire after 15 minutes. Show a nice error if it expires."

What the AI agent typically generates:

  • Forgets to check if the user account is active or locked.
  • Returns “Email not found” on the reset endpoint, leaking registered user emails (user enumeration vulnerability).
  • Fails to revoke existing JWT/session tokens after the password is changed.

After: The Formal Use Case

# UC-042 Reset Account Password

## Preconditions
- PRE1: The system authentication service is reachable.

## Basic Flow
1. The Customer enters [Email Address] and submits the reset request.
2. The System checks that an account matching [Email Address] exists in status ACTIVE.
3. The System generates a cryptographically secure reset token with a 15-minute TTL.
4. The System sends a reset notification email to [Email Address] containing the token link.
5. The Customer opens the link and submits [New Password].
6. The System checks that [New Password] complies with [Password Policy].
7. The System persists the updated credential and revokes all active session tokens for the account.
8. The System displays a confirmation message and redirects the Customer to the login screen.
*The use case ends.*

## Alternative Flows
- 2.1 Non-existent or inactive account:
  1. The System displays a generic confirmation message (preventing email enumeration).
  2. The System writes an audit log entry `AUTH_RESET_ATTEMPT_UNKNOWN`.
  *The use case ends.*
- 5.1 Expired or invalid token:
  1. The System displays [Token Expired Error] with a link to request a new reset email.
  *The use case ends.*
- 6.1 Password does not meet policy:
  1. The System displays validation errors on [Password Field].
  *Resume at Step 5.*

## Postconditions
- POST1 (Success): Credential updated; all active session tokens revoked; audit event `AUTH_PASSWORD_RESET_SUCCESS` persisted.
- POST2 (Failure): No user credentials or session states modified.

When an AI coding agent receives this specification, the ambiguity drops to zero. The agent creates tests for POST1 and POST2, implements handlers for steps 1–8, and implements explicit guards for branches 2.15.1, and 6.1.


Frequently Asked Questions

Isn’t writing formal use cases too much overhead?

It used to be when humans had to write every line manually in Word documents. With use-case-expert, the agent does the tedious formatting and interviewing for you. You provide the high-level intent, review the generated spec in 60 seconds, and approve it. In practice, I can personally testify to a ~30% effort reduction in overall requirements engineering time compared to traditional manual drafting or endless PRD editing.

Shall I create frontend and backend use cases?

NO. The strength of use cases is to unite all aspects of a functionality in one place. So use cases cover front- and backend functionality.

The contained use case template is designed for AI assisted coding of modern architectures, that often contain the frontend – backend division. So there is an API section that supports exactly this division.

In implementation of a frontend/backend architecture, I reccomend to separate frontend and backend implementation in two sessions. This creates a clear context, where implementation languages and test frameworks do not get mixed.

How does this actually eliminate hallucination?

LLMs hallucinate when they encounter under-specified branches. A formal use case is a complete state machine: every path either completes successfully with defined POST conditions or branches into an explicit Alternative Flow. There are no unspecified gaps left for the model to guess.

What languages are supported for specificaton and implementation?

The skills are intentionally designed language agnostic. If you like to have your requirements specification in German, just put something like “Generate al Use Case and Supplementary Specification documents in German. Do not alter the Headlines” in your AGENTS.md. Note that the “de-vague” feature for precise wording currently only supports english language.

The use-case-implementer is not linked to any coding language. It just reinforces completeness of implementation and test. If you have specialized coding skills, combine them with the implementer.

I have successfully done implementations with TypeScript, Python and Java.

Why are there no Gherkin scenarios in the templates?

Almost every AI tool reflexively suggests generating Given / When / Then Gherkin scenarios alongside the specification. My use case templates explicitly omit them. In an agentic workflow, Gherkin scenarios would just be AI-generated intermediate fluff—and nothing more than what the agent extracts anyway when you prompt it to generate actual automated tests. Save your context tokens and cognitive bandwidth for real test generation rather than synthetic Gherkin bloat.

How do the skills relate to databases?

The skills intentionally focus on behavioral contracts, system boundaries, and verifiable invariants rather than raw database tables. However, if database artifacts are already available in your repository (for example, a schema.dbmlschema.prisma, or SQL DDL file), you should explicitly point the agent to them in your prompt (e.g., “When defining the use case, consider also database.dbml). The agent will align entity attributes, foreign keys, and constraints directly with your persistent model.

Does it support my Figma mockups?

It depends. I tried to integrate pixel precise FIgma mockups in in one project. It turned out that they were accurate only for a very short time, then the implementation was changed and the spec became outdated.

This is why I reverted to a simple text only reppresentation of the UI. It can be easily verified or updated in either direction.

What I personally do is to use the Figma mockups in the moment they are fresh and valid and integrate the contents (not the image) into the use case. LLMs are amaizingly good in analyzing images and will populate the use case with all the required details.

What AI models do I need to use?

The use-case-expert skill operates on the critical boundary between human domain understanding and machine specification. To get crisp, human-readable, and verifiable specifications, you need a high-quality reasoning model during the initial definition and refinement phase. I’ve had consistently strong results using Gemini 3.7 Flash and Anthropic Opus 4.6+.

Check with your preferred model whether the generated specifications are sharp and unambiguous: quality degrades quickly with smaller or less capable models. You never want fuzzy or vague phrasing baked into your foundational requirements.

Can I use this with my existing AI coding tool?

Yes. The skills are standard Markdown files and instruction bundles compatible with:

  • Google Antigravity IDE (.agents/skills/)
  • Claude Code (.claude/skills/)
  • Cursor / Codex / Windsurf / ChatGPT (via custom rules or direct system context)

Future Work

End-to-End (E2E) UI and browser test automation (e.g. Playwright or Cypress) is not yet covered as a standalone skill.

In my own day-to-day workflow, I’ve realized automated E2E test suites against approved specs using ad-hoc prompts with optimum results. A dedicated E2E test-generation skill might accelerate this process further and provide even more reproducible, browser-level test coverage out of the box.

The de-vague feature is language specific. I plan to support additional languages as soon as the need arises.

When it comes to changing requirements and code, special attention is required to keep both in line. Current workaround is to change use case and implementation in one session. This does not fit teams that are not fully T-shaped. This should be made easier by use case versioning and git integration.


Getting Started

The complete Use Case Triad is open-source and available on GitHub:

👉 github.com/dominikenkelmann/skills

To install it in your workspace:

# Clone the repository
git clone https://github.com/dominikenkelmann/skills.git

# Copy the skills into your agent's configuration directory
mkdir -p .agents/skills
cp -r skills/skills/requirements/* .agents/skills/

Give it a try on your next non-trivial feature. You’ll never go back to vibe coding with vague user stories again.

Please share your feedback, ideas, or experiences so I can continuously improve this skill suite.

Have fun!
Dominik

Serious Requirements Engineering in an AI World

Leave a Reply

Your email address will not be published. Required fields are marked *