A coding agent can explore a repository, modify several modules, run commands and prepare a pull request in a single session. That ability dramatically reduces the cost of implementing a solution. It does not reduce the cost of deciding which solution we need, or of proving that the result is correct, to the same degree.
When a task arrives as an ambiguous request, the model fills in the gaps. It decides what the terms mean, which cases seem important and how the solution should fit the system. Many of those decisions will be reasonable. Some will contradict rules the team never wrote down.
AI-assisted development therefore needs more than better prompts. It needs a chain of references and checks that connects intent to code:
Problem → specification → implementation → tests → continuous integration → review
Each part answers a different question. The specification explains what we want and which boundaries exist. Tests check concrete behaviours. Continuous integration makes sure those checks run before a change is accepted.
A specification reduces improvised decisions
Spec-Driven Development uses an explicit specification as the reference for planning, implementing and validating a change.
This does not mean generating all code automatically from a formal document. Nor does it require dozens of pages before anyone edits a line. A specification becomes valuable in agentic work when it prevents a temporary conversation from being the sole source of intent.
A useful specification should clarify at least:
- The problem experienced by the user or the business.
- The behaviour the system must provide.
- The cases and constraints we already know.
- The decisions that have already been made.
- What remains outside the scope of the change.
- How we will verify the result.
The level of detail depends on the work. A small fix may need only a short description and a reproducible case. A feature affecting several parts of the system needs to preserve more decisions and survive across sessions and people.
What is Spec-Driven Development? It is a way of building software in which a specification guides planning, implementation and validation. For an agent, it provides a stable source of intent and reduces the decisions it has to improvise.
Not every change needs the same process
Too much specification also has a cost. A long document can become stale, introduce decisions nobody made or take longer to read than the change it describes.
Before writing a complete specification, ask:
- Can the work be completed in one session and verified easily?
- Does it affect several modules or teams?
- Does it contain functional decisions that can be interpreted in several ways?
- Will another person or agent need to resume the work later?
- Would a mistake have significant consequences?
The longer the work, the more participants it involves and the greater the cost of error, the more valuable it is to preserve the specification outside the conversation.
A spike serves a different purpose. It reduces uncertainty and may use disposable code to do so. Once the investigation ends, its findings should inform the later decision or specification; that does not turn the prototype into a product.
Who should write and review a specification
A functional specification should not be a technical design in disguise. Its first duty is to describe the behaviour the user needs.
The person who knows the business contributes the problem, rules and exceptions. Product defines the scope and orders the use cases. The person who understands the architecture best checks that the proposal uses the language of the system, respects its boundaries and does not create a second solution for something that already exists.
That architectural review must happen before implementation is delegated. Agents are very good at completing gaps: when a convention is undocumented, they apply another reasonable one; when they do not know an invariant, they may build around it. The result can work and still make the system less coherent.
Keep two questions separate:
- What must the user be able to do? The functional specification answers from outside the system.
- What must the implementation respect? Architectural decisions establish boundaries, invariants and interfaces.
Both perspectives need to meet before the agent writes code.
Small Skills instead of a closed process
Some approaches to SDD attempt to govern the complete cycle through a mandatory chain of documents and steps. That can work in the right context, but it can also make a small task pay the cost of a process designed for much larger changes.
Matt Pocock’s Skills for Real Engineers takes a more composable approach. Its Skills address concrete failure modes: clarifying a problem, preserving decisions in a specification, dividing large tasks, building a prototype, implementing with TDD, diagnosing failures or reviewing code.
One of its most useful decisions is that a specification is not always required. Work that is already decided and fits in one session can be implemented directly. Work that must survive across sessions turns the conversation and repository context into a reference another agent can resume.
The useful idea is not to copy every Skill. It is to build a modular process: add discipline where the team has an observable failure mode and avoid ceremony that does not change the outcome.
Tests are a limited executable guarantee
A specification uses language to express intent. A test takes part of that intent and turns it into a check the system can run.
That guarantee has limits. A test only protects the behaviour we managed to express. The entire suite may pass while an error remains in a case nobody considered. Even so, it gives an agent something reasoning alone cannot provide: an external, repeatable signal.
Without tests, the agent inspects the code and estimates whether its change is correct. With tests, it can work in small steps, check the result and discover whether it has broken earlier behaviour.
Tests that work well with agents share several traits:
- They check observable behaviour rather than accidental internals.
- They produce understandable errors when they fail.
- They can run repeatedly and in isolation.
- Their expected outcomes do not depend on the implementation.
- They live close to the level at which the protected decision is made.
A test that breaks because a private function was renamed makes change harder without protecting the user. A test that checks a purchase, an authorisation or a calculation protects a real capability of the system.
TDD as a feedback loop
Test-Driven Development proposes a simple cycle: write a failing test, implement what is needed to make it pass and improve the code while keeping the suite green.
With agents, this cycle makes each bet smaller. The model does not generate a large amount of code only to discover at the end that the direction was wrong. It works on one behaviour, receives feedback and uses what it learned in the next step.
TDD should not become a mechanical obligation. Research, visual changes and some refactorings may begin differently. The important principle is that an agent needs frequent feedback loops and must be able to demonstrate the behaviour it is building.
There is also a common trap: asking an agent to write every imagined test first and then produce the entire implementation. That separation can create tests for an assumed structure rather than the real behaviour. Vertical slices — one behaviour verified from end to end — provide feedback earlier.
What is TDD? Test-Driven Development is a cycle in which we write a failing test, implement the code needed to make it pass and then improve the solution without breaking it.
BDD and Gherkin as a bridge to the business
Behavior-Driven Development is useful when several parts of an organisation need to discuss behaviour using the same language. Instead of starting with classes, functions or endpoints, it begins with recognisable examples.
Gherkin expresses those examples with a simple structure:
Given a customer has an active subscription
When they cancel its renewal
Then they keep access until the end of the paid period
The business can recognise the rule. Product can ask about exceptions. Architecture can decide where it belongs. The agent can use the scenario as an implementation criterion and, when automated, as an executable test.
Gherkin adds less value when it describes internals or merely translates tests that were already easy to understand. Its strength lies in connecting a functional conversation to a system check.
What are BDD and Gherkin? BDD describes expected behaviour through examples that business and technology can discuss together. Gherkin is a common syntax for writing them with structures such as “Given”, “When” and “Then”.
Continuous integration turns tests into a rule
A test suite does not protect the product if running it depends on individual memory or discipline. Validation needs to be part of the normal path for every change.
Continuous integration automatically runs tests and other checks when someone opens or updates a pull request. With GitHub Actions, a workflow can install the project in a clean environment, execute the checks and report the result to GitHub.
Required status checks can prevent integration until every mandatory check succeeds. Tests then stop being a recommendation and become a shared condition.
This matters especially with agents. Even if an agent says it ran the tests, the system checks the change again outside its session. Continuous integration reduces differences between environments and leaves a visible result for the reviewer.
A useful minimum often includes:
- Automated tests.
- Type checking, where the language supports it.
- Formatting and static rules.
- Compilation or packaging.
- Migration checks when data is affected.
- Security controls relevant to the project.
Not every check has to block every change. The team should distinguish an informative warning from a condition without which the code cannot be accepted.
What is continuous integration? It is the automatic execution of tests and other controls when a change is proposed. It guarantees that configured checks run; it does not guarantee that those checks cover every possible error.
A practical chain for working with agents
A reasonable flow for a product change is:
- Clarify the problem with the person who understands the need.
- Explore the repository and its existing decisions.
- Write the functional specification and relevant cases.
- Review the architectural fit and boundaries.
- Agree where and how the behaviour will be checked.
- Implement in small steps, running tests throughout the work.
- Review the change against the specification and project rules.
- Open a pull request and run continuous integration.
- Require mandatory checks to pass before integration.
- Turn any later regression into a new test.
This flow does not need the same intensity for every change. Discipline should grow with risk, duration and the number of decisions involved.
Common mistakes
Treating a conversation as a specification. A conversation contains doubts, alternatives and rejected decisions. Before implementation, summarise what was actually agreed.
Letting the agent define behaviour and validate it with its own tests without review. The same mistaken interpretation can appear in both code and test.
Testing internal details. This creates a brittle suite that makes the implementation harder to improve.
Keeping stale specifications as permanent documentation. Some specifications are temporary delivery records. Durable decisions belong in the appropriate project documentation.
Running tests only at the end. Long feedback loops delay information and increase the cost of correcting direction.
Configuring CI without making important checks mandatory. The result appears on the pull request, but can still be ignored.
Confusing a green suite with correct software. Tests confirm what was checked, not every possible behaviour.
Agents need freedom inside a system
The answer to agent mistakes cannot be manually reviewing every generated line or limiting agents to small helper functions. That would waste much of their capability.
We can grant more autonomy when the system preserves intent, provides feedback and stops changes that violate known conditions. Specifications, tests and continuous integration form part of that system.
The specification reduces ambiguity. Tests give the agent an executable reference. Continuous integration applies the rule outside its session. Human review can then focus on what is difficult to automate: whether we solved the right problem, whether the architecture remains coherent and whether the risk makes sense.
This discipline does not slow down AI-assisted development. It allows us to use it on more important work without depending solely on trust in the model.
If you want to introduce this approach in your team, read the service page Standardise how your team builds software with AI.
Glossary
BDD Behavior-Driven Development. An approach that uses understandable examples to define and verify expected behaviour.
CI or continuous integration Automatic execution of tests and other checks whenever a change is proposed or integrated.
Gherkin A syntax for describing behaviour through scenarios built around “Given”, “When” and “Then”.
Pull request A proposal to integrate changes into shared code. It allows review and automated checks before acceptance.
Skill A reusable set of instructions that teaches an agent how to perform a task using a defined method.
Spec-Driven Development A development approach in which an explicit specification guides planning, implementation and validation.
TDD Test-Driven Development. A cycle based on writing a failing test, implementing the behaviour and then improving the solution.
References