DEV Community

Nikita Kitaychik
Nikita Kitaychik

Posted on • Originally published at nikitakitaychik.com

Contracts, Not Tasks

I timed where my week actually goes. Decomposing one feature into precise tasks — which files, which methods, what order — costs me hours. Then I watched what a developer did with my decomposition.

He pasted it into Claude.

Sit with that for a second; it took me embarrassingly long. A detailed task list is, functionally, a prompt. I was writing prompts by hand, with the most expensive hours in the team — for a model that sits next to the code and regenerates the same plan in minutes.

The duplication

The repository my team ships from has its own agent tooling: given a well-formed requirement, it produces the implementation plan — files, layers, order — as a matter of course.

So every file-by-file task I wrote was a duplicate: my guess at an implementation plan, produced farther from the code than the tool that would redo it anyway.

Two passes over the same work. And of the two, mine was the expensive one.

The bottleneck math

Here's why this isn't a cosmetic inefficiency:

Step Who pays Typical cost
Fine-grained decomposition the owner hours per feature
Implementation from it developer + agent often less than that

When specification costs more than production, the queue forms in front of the owner. Not occasionally — structurally. Features stop waiting for hands and start waiting for my write-up.

And the trap has a cruel twist: the more diligent I am — the finer I decompose — the worse it gets. Every extra hour of pre-chewing lands on the expensive side of the ledger and removes nothing from the cheap side. Diligence digs the hole deeper.

So the fix cannot be "decompose better." It has to be "decompose less — and still stay safe." Here's what that looks like.

The same feature, two handoffs

As a task list: add the column, patch the handler, extend the validation, don't forget the migration, update the client call. Five predictions about how — every one of them re-derivable by the tooling next to the code, none of them checkable by me.

As a contract: a rename operation exists; a duplicate name is rejected with its own error; only the scope owner may call it; when the parent object dies, the record dies with it; done means these three checks pass.

Five promises about what must become true — every one of them checkable, none of them my guess about someone else's file.

That's the entire replacement. The unit I hand over is no longer a change to make but a promise to keep. What goes into one:

Section The question it answers Why it survives contact with reality
Interface What exists when you're done? Abstract schema, named by the API's own conventions — nothing invented
Behavior What happens on success, on failure, at the edges? In text, not code — the implementer owns the translation
Access Who may call this, under which privilege and scope? Includes the cases everyone forgets: the admin bypass, the machine identity
Lifecycle What happens on parent delete / update / detach? "We forgot the cascade" is the most boring production incident there is
Acceptance What does "done" mean? Runnable without asking the author what they meant
Impact radar What must not break, and what depends on this? Ordering becomes data instead of tribal knowledge

What a contract deliberately omits: file paths, method names, SQL, step-by-step instructions. They're the implementer's jurisdiction, they're regenerated better closer to the code — and every such line in a spec is a line I paid for twice.

The fear, and the law that answers it

Handing over a coarse promise instead of five chewed steps feels reckless. The fear is legitimate — and it has an exact answer:

Decomposition granularity should scale inversely with the strength of your acceptance gate.

If the consumer's gate is strong — review against the contract, tests written by the author as a norm, a script that checks the contract's own structure — coarse units are safe: one contract bundles what would have been six tasks, and the gate catches what goes wrong.

If the gate is weak, big pieces really are reckless. You must decompose finer — not forever, but until the gate grows.

I run both regimes at once, which is how I know the law holds:

Track Gate today Unit size today
Backend Strong: senior reviews against the contract, author writes the tests, a script checks structure Coarse — one contract instead of six tasks
Frontend Weaker, still growing Fine-grained, layer by layer

The backend contracts got harder over time, not softer: the senior who consumes them kept pushing back — a missing lifecycle clause, a field name that drifted from the API doc, an access row that forgot the admin case. Each ruling became a permanent check. That's the gate literally strengthening — and each time it did, I could afford to hand over a slightly bigger piece.

Frontend stays fine-grained not because frontend is harder, but because that gate is younger. Same law, other end of the dial.

Where the hours went

The payoff closes the loop this essay opened with:

Before After
My hours per feature pre-chewing an implementation plan writing promises + judging acceptance
The developer gets my prediction to paste into a model room to own the how, with a gate that catches failure
The queue forms in front of me forms in front of the gate — which scales

Two contracts that share an interface boundary can even proceed in parallel — frontend against the schema, backend against the behavior — because the contract, not a meeting, is the source of truth at the boundary. Pre-chewed tasks can't do that; they serialize on shared files even when the promises are independent.

Honest prior art, dated: design-by-contract is forty years old — preconditions and postconditions are Meyer's, not mine — and 2026 work revives it underneath task decompositions to verify codegen. The move here is different: the contract replaces the task as the unit of handoff at the human↔agent boundary, and the granularity law that comes with it I haven't seen stated anywhere. As of mid-July 2026 — check the date before quoting me.

The uncomfortable implication, which I'll take head-on later in the series: if granularity is a function of gate strength, then improving your gates — not your prompts, not your models — is the highest-leverage investment in an AI-assisted team. The gate is the product.

The owner stops being the bottleneck on the day the unit of handoff is a promise the consumer's gate can check — not a plan the owner had to write twice.

Next: The Two-Layer Compiler — why a dumb procedural layer plus a learning layer compounds, and hand-tuned cleverness doesn't.

Top comments (1)

Collapse
 
deanlee profile image
Dean Lee

This framing matches the constraint I keep running into with agents. If implementation gets cheaper, the scarce asset moves upstream into well-specified acceptance criteria and blast radius. File-level tasks start looking like expensive prework.