A comment in the September r/dataengineering discussion thread stopped me for a moment. A team had embraced spec-driven development so completely that people were proud of not reading the code their agents produced.
I like specifications. They force a team to describe the behaviour it wants before a coding session starts inventing behaviour on its behalf. GitHub's Spec Kit makes the idea concrete: define intent, plan the work, break it into tasks, implement, and converge on evidence.
A useful specification becomes dangerous when the team treats it as proof that the implementation is correct.
A spec tells us what should happen. The code still decides what can happen, including the error paths, permission boundaries, resource usage, rollback behaviour, and accidental side effects that never made it into the happy-path requirements.
If nobody can explain the resulting change, the team did not remove engineering work. It moved that work to production.
A good spec improves the review
Spec-driven development gives reviewers something better than a vague ticket. A useful spec should name the user-visible behaviour, constraints, acceptance criteria, and conditions that must remain true.
For example, "add bulk retry" is not much of a contract. A better spec would say:
- Only failed items are eligible for retry.
- A retry must not duplicate a successful write.
- The caller may retry the same request after a timeout.
- Authorization is checked again when the retry begins.
- The operation records which items succeeded, failed, or were skipped.
That list makes the implementation easier to challenge. A reviewer can trace each condition into code and tests. It also exposes questions before code exists. What identifies the same request? How long is that identity retained? What happens when authorization changes between attempts?
The spec does not answer those questions unless someone puts the answers into it. Even then, the implementation may violate them.
GitHub's own agentic SDLC guide is more restrained than the popular interpretation of spec-driven development. It describes agents layered onto a conventional lifecycle, with small changes using the normal issue, pull request, test, and review path. Larger changes may use deeper specification and planning. Code still faces automated checks and maintainer review.
That model keeps the useful part of spec-driven development. The specification sharpens the review. It does not cancel it.
There are four different contracts
I would separate the work into four contracts:
Intent
What behaviour do we want?
Implementation
How does this change create that behaviour?
Verification
What evidence shows that it works and remains safe?
Operation
How will we detect, contain, and recover from failure?
A feature spec mostly owns intent. An implementation plan connects intent to the repository. Tests and analysis provide verification. Runbooks, telemetry, deployment controls, and rollback plans cover operation.
An agent can help produce every artifact in that chain. It cannot make the artifacts equivalent.
A passing unit test may prove that a retry helper returns the expected result for three inputs. It does not prove that two workers cannot process the same item concurrently. A correct implementation may still need a database constraint, idempotency key, or conditional write. Those decisions live below the level of the feature sentence.
This is why reviewing only the final agent summary is risky. The summary is another generated artifact. It can be clear, persuasive, and wrong in exactly the same direction as the code.
Review the repository state: the diff, tests, dependency changes, generated files, migrations, permissions, and runtime configuration. Narration is useful for navigation. It is not evidence.
Make the change small enough to understand
AI makes it cheap to generate a large change. It does not make a large change cheap to review.
A 2,000-line pull request that modifies 100 data models may contain correct code. It is still a poor unit of review if nobody can reason about its blast radius. The problem is not an arbitrary line limit. It is the number of independent claims the reviewer must verify at once.
I would slice work by behaviour and risk:
specification
↓
schema or interface change
↓
core behaviour
↓
integration path
↓
operational controls
Each slice should leave the repository in a valid state and produce its own evidence. A database migration can be reviewed before application logic depends on it. A new API can land behind a disabled route or feature flag. Observability can be added with the behaviour rather than appearing in a final "production readiness" pull request that nobody wants to delay.
Small changes also make agent mistakes easier to diagnose. If a test fails after one bounded edit, the search space is manageable. If an agent rewrites a subsystem, updates dependencies, changes infrastructure, and reformats adjacent files in one pass, the team has created a debugging project.
The agent is not the only source of this problem. Humans have always produced oversized pull requests. Faster generation simply removes the natural friction that used to slow us down.
Verification must come from outside the implementation
If the same prompt produces the code, the tests, and the explanation, all three may share the same blind spot.
That does not make agent-written tests useless. It means the acceptance evidence should not depend only on the implementation's own assumptions.
For the bulk retry example, I would require evidence at several levels:
| Evidence | What it checks | What it can miss |
|---|---|---|
| Unit tests | Local state transitions and edge cases | Database and concurrency behaviour |
| Integration tests | Persistence, transactions, and authorization | Production scale and timing |
| Static analysis | Known unsafe patterns and policy violations | Business correctness |
| Load or fault tests | Contention, timeouts, and retry storms | Rare production combinations |
| Human review | Design fit, hidden coupling, and maintainability | Errors outside the reviewer's context |
No row is sufficient by itself. Together they make a much stronger argument.
NIST's current DevSecOps reference model takes the same position: AI-generated output should pass through peer review, security validation, automated testing, and approval workflows. The model treats generated corrective actions as proposed inputs, not self-approving changes to software or configuration.
Calling the output a proposed input keeps accountability with the engineering process.
Review based on risk, not authorship
I would not create one ceremonial review policy for "AI code" and another for human code. The author does not determine the blast radius.
A one-line IAM wildcard can be more dangerous than a generated client with 500 lines. A documentation edit may need little more than link and rendering checks. A payment calculation deserves domain review whether a staff engineer, a junior developer, or an agent wrote it.
Classify the change instead:
- Does it alter authentication, authorization, secrets, or network access?
- Does it modify a schema, public API, or event contract?
- Can it destroy, duplicate, or expose data?
- Does it change infrastructure or deployment behaviour?
- Can failure affect money, compliance, or customer access?
- Is rollback tested and practical?
Higher-risk answers require stronger evidence and named reviewers. Low-risk work can move quickly with automation. This avoids turning review into theatre while preserving scrutiny where it matters.
GitHub's responsible-use guidance for Copilot agents explicitly presents generated changes, CI results, review activity, and session history so people can inspect work before merge. The workflow expects a human decision. Enabling automatic agent merge does not change who owns the outcome.
Keep a short evidence note with the pull request
I would ask every non-trivial agent-authored change to include a compact evidence note:
Behaviour changed:
- Failed items can be retried without duplicating successful writes.
Constraints preserved:
- Authorization is evaluated at retry time.
- The existing single-item endpoint is unchanged.
Evidence:
- Unit and integration suites passed.
- Concurrent retry test produced one committed write.
- Migration was applied and reversed in a clean environment.
Not verified:
- Retry behaviour above 500 requests per second.
Rollback:
- Disable `bulk_retry_enabled`; no schema rollback required.
This should be a set of claims a reviewer can inspect, not an essay about what the code probably does. The "not verified" section is especially useful because it stops missing evidence from being disguised as confidence.
The note also gives operations a head start. If the feature fails, the team already knows the intended boundary and the first containment action.
Specifications should make ownership clearer
Spec-driven development is worth adopting when it makes intent explicit, catches ambiguity early, and gives agents a bounded problem. It becomes dangerous when the organization uses the spec as permission to stop understanding its software.
My preferred workflow is simple:
reviewed intent
↓
small implementation slice
↓
independent verification
↓
risk-based human review
↓
controlled release and observation
Agents can accelerate every step. They can draft the spec, explore the repository, implement a slice, run tests, and assemble the evidence. That is useful work.
The engineering team still has to decide whether the evidence is sufficient. It still has to understand the failure modes and support the result after deployment.
A specification can tell an agent what to build. It cannot take the pager.
Top comments (0)