← Back to the section

From a Product Slice to a UCP Contract an AI Can Build

A product engineer has a smallest valuable slice — the piece of product that solves a real user problem and tests a hypothesis with minimal effort. It's the output of product thinking: you've already cut the extras, deferred the "we'll finish it later," and kept a single end-to-end line of value. How to cut down to such a slice is its own conversation, in prioritization of the smallest slice.

Then comes the question that makes a product engineer a product engineer: how do you hand this slice to an AI agent so it builds exactly that, and not something similar.

That's the bridge. On one side, product: problem, user, value. On the other, assembly: an agent that writes code. Between them is a crossing, and it's usually right there that things fall apart.

Why a vague task yields a plausible "not quite it"

The temptation is strong: the slice is clear in your head, the agent is smart — why not describe the task in two sentences and get the result?

Because "in two sentences" means the agent fills in the gaps itself. And it fills them not from your product but from the average of everything it has seen. You say "build order checkout" — and it decides what an order is, which fields are required, what happens on a double click, where the money goes on failure. Each of those decisions it will make reasonably. But reasonable in general is not reasonable for your slice.

The pattern is familiar: code arrives that looks convincing, compiles, is even covered by tests — and does the wrong thing. Not wildly wrong, just a couple of steps off: boundaries wider than you wanted, edge behavior different, the wrong contract exposed outward. You notice it not at acceptance but two weeks later, when you build the next slice on top of it.

And the more capable the agent, the more plausible the "not quite it" becomes — it hides better and better behind "it." A vague task isn't offset by a smart executor; it's amplified by one.

A contract before code generation

The fix isn't to write better code but to turn the slice into a UCP contract before the agent starts building. A contract isn't a hundred-page specification or a formal requirements document. It's four things, pinned down in words precisely enough that you can both build from them and later check against them. Exactly the four that make up Use Case Pattern as a unit of work.

1. Boundaries — what's in and what's out. The most underrated part, and the most useful. You spell out what the slice includes and — as a separate list — what it deliberately does not. "Card payment — yes; saved cards, installments, refunds — no, not this slice." The "out" list is worth more than the "in" list: it's exactly where the agent wanders by default, filling in the plausible. A closed boundary is what separates a slice from "the feature in general."

2. The scenario in the user's words. One use case, described the way a person lives it, not the way a system executes it. "The buyer clicks pay, sees confirmation, the money is charged once." Not "the controller accepts a POST and calls a service." The scenario keeps you and the agent on the same page about meaning, before mechanics enter the conversation. If the scenario can't be stated in one clear sentence, the slice isn't cut down yet.

3. The interface — input, output, errors, idempotency. Here the slice becomes a contract at the seam. Not the implementation — the signature: what the operation accepts, what it returns, which errors it answers with, and what happens on a retry. Idempotency is pinned down right here, at the contract level, not "we'll sort it out in code": a repeated call with the same key yields the same result, not a second charge. This is a boundary that neighboring pieces of the product see, so it can't be left to the agent's discretion.

4. Acceptance criteria — checkable. Not "works correctly," but a list of statements, each verifiable yes/no. "A repeated call with the same key produces no second charge." "When the gateway is unavailable, the order stays unpaid and the user sees a clear error." Criteria are what you'll accept the result against. If a criterion can't be checked, it isn't a criterion — it's a wish, and the agent will quietly ignore it.

What it looks like

The contract is short. It sits outside any language and any stack — the craft the agent knows itself; your job is to set the frame:

UseCase: Pay for an order

Boundaries:
  in     — card payment for a single order, one-time charge
  out    — saved cards, installments, partial payment, refund

Scenario:
  The buyer confirms payment for an order in "awaiting payment" status
  and receives a confirmation; the money is charged exactly once.

Interface:
  input  — order identifier, idempotency key
  output — order status, payment reference
  errors — order not found; order already paid; gateway unavailable
  idempotency — a retry with the same key returns the first result,
                no second charge

Acceptance criteria:
  1. A successful payment moves the order to "paid" and returns a reference.
  2. A retry with the same key does not charge a second time.
  3. An unavailable gateway leaves the order "awaiting payment"; error is clear.
  4. Paying an already-paid order is rejected and does not charge.

Twenty lines. But from them the agent builds predictably: the boundaries keep it from sprawling, the scenario holds the meaning, the interface pins the seam, the criteria give it something to finish against. And — more importantly — from those same twenty lines you later accept the result. The contract before assembly is exactly what the assembly is checked against afterward.

Why it's a bridge, not a separate stage

It's tempting to treat writing the contract as an extra step between "thought it up" and "built it." In fact it isn't an insertion but a translation. Left of the bridge is the product language: problem, value, hypothesis. Right of it is the language of assembly: command, port, error handling. The contract is the one place where one turns into the other without loss of meaning.

Without it, the crossing still happens — just inside the agent, silently, at its discretion. You hand a product decision to someone who doesn't know your product. With a contract, the decision stays with you, and the agent gets what it does well: build to a clear frame.

And this same contract closes the loop at the other end. The acceptance criteria you wrote before code generation become what you accept the result against after — more on acceptance itself in accepting the AI's output. The contract is written once but works twice: as the task on the way in and as the check on the way out.

In short

  • The smallest valuable slice is a product decision; handing it to an agent as a vague phrase hands that decision to the agent.
  • A vague task yields a plausible "not quite it," and the more capable the agent, the more convincing the disguise.
  • The bridge is to turn the slice into a UCP contract before code generation: boundaries (including "out"), the scenario in the user's words, the interface (input-output-errors-idempotency), checkable acceptance criteria.
  • The contract is short and language-neutral — a frame, not an implementation.
  • It works twice: as the task for the agent on the way in and as the basis for acceptance on the way out.