Chapter 2 of 7 · 8 min read
Five surfaces, and the questions that choose between them
On this page
Two requests landed in the same sprint planning at a mid-market CRM vendor. Sales wanted every new lead enriched with company size, industry and recent funding, because reps were opening thirty leads a day and hand-googling each one. Finance wanted expense filing to stop being a twenty-minute chore, because the report needed eight fields and every one of them was legible on the photo of the receipt the employee had already taken. Both requests arrived with the same proposed implementation attached, which was an assistant the user could talk to, and both got a different answer once the team stopped arguing about assistants and started asking about the work.
The five surfaces worth having names for are the ones that keep coming up. An inline suggestion appears next to what the user is already doing and offers a completion they can accept or ignore. A background job fires on an event — a record created, a file uploaded, a ticket assigned — and writes its result somewhere the user will find it later. A structured form shows the fields and takes input, prefilled where the system can prefill. Batch enrichment sweeps a set of records on a schedule and writes results for all of them at once. Conversation is a turn-taking exchange in a text box. They are not five flavours of the same thing; they differ in who initiates, when the result appears, and what the user has to do to get it.
One question runs before the others and can end the discussion
Five of the six discriminators — the questions whose answers cut the five candidate surfaces down towards one — trade against each other, and one does not. The one that does not is whether a paraphrase is an acceptable answer at all. Some questions can be answered in the system's own words — how do I request time off, what did revenue do last week, which of these leads is worth calling. Others must be answered with the governing artefact itself: the clause, the schedule, the price on the contract, the dosage in the protocol. For those, restating the source in fluent prose is a downgrade even when the restatement is accurate, because the thing that decides is the text, and the user needs to have been shown the text. That usually comes with a second requirement, which is a record of exactly which version of which document was displayed, to whom, at what time.
Legal, medical, compliance and pricing answers all tend to fall on that side. When they do, the question is settled before ambiguity or repetition or urgency gets a vote, because those five are about interaction cost and this one is about what a correct answer is made of. Run it first.
The diagram is a first pass, not a verdict; the five remaining discriminators are what you use to check the branch you landed on, and occasionally to override it.
Six questions, five surfaces
Ambiguity is how varied the incoming requests are and whether the user can state what they want before seeing a response. Repetition is how often the same shape of work recurs — thirty times a day per user, or twice a quarter. Urgency is how long the user can wait for a result and still have it be useful, which is a different thing from how long the computation takes. Human approval asks whether a person must sign off before the result has effect, which forces the result to be reviewable rather than merely correct. Correctability asks what happens to a wrong result: is it fixed on the next run at no cost, or does it propagate into an invoice, an email or a payment. And the paraphrase question sits above all of them.
| Inline suggestion | Background job | Structured form | Batch enrichment | Conversation | |
|---|---|---|---|---|---|
| Ambiguity | Low — one obvious continuation | Low — same task each time | Low to medium | Low | High |
| Repetition | Very high, many times per session | High, once per record | Medium | Very high, whole tables | Low to medium |
| Urgency | Under ~300 ms or it is noise | Seconds to minutes | Immediate, user is waiting | Hours to days | Seconds, user waits |
| Human approval | Implicit, per suggestion | None, or a review queue | Explicit, before submit | None; spot-checked | Awkward — nothing to sign |
| Correctable later | Yes, by not accepting | Yes, if a re-run is cheap | Yes, before submit | Yes, on the next sweep | Only by asking again |
| Paraphrase acceptable | Required | Usually | Usually | Usually | Required |
One question sits earlier than all six and is easy to skip because it does not feel like a design decision: does the user have to initiate at all? Teams reach for interactive surfaces by habit, since an interactive surface is the thing you can show someone, and a background job is invisible by construction. Yet a great many requests that arrive as "let users ask for X" are really "X should already be there when they look", and the difference between those two readings is the entire interaction cost. Ask who the trigger belongs to before you ask anything about ambiguity or urgency.
Two rows do most of the work in practice. Urgency separates the surfaces that must answer inside a keystroke from the ones that can take a night, and it is the row teams most often get wrong, because they read "the user wants it soon" off a stakeholder's enthusiasm rather than off the workflow. Approval separates surfaces where a person has something concrete to sign from surfaces where they do not; a conversation is a poor place to route an approval because there is no artefact to approve, only a transcript, which is why chat-based workflows tend to grow a confirmation card just before anything irreversible happens.
Running the two real requests through it
Lead enrichment clears the paraphrase gate immediately — a rep looking at a lead record wants company size as a number, and nothing about that answer needs to be the source text. Does the user initiate? No: the trigger is a lead being created, and asking a rep to request enrichment for each of thirty leads a day is charging them for something the system already knows it wants. Ambiguity is near zero, since the task is identical every time. Approval is not required; a wrong industry classification is corrected by the rep in place. Correctability is good, because the fields are overwritten on the next run.
That leaves urgency, which decides between the two event-driven surfaces. The team pulled the number rather than guessing at it: the median gap between a lead being created and a rep opening it was 4 hours 20 minutes, and the tenth percentile was 11 minutes. A nightly batch would leave a tenth of leads unenriched at the moment of first contact, which is the moment the enrichment exists for. So the surface is a background job that fires on creation and writes the fields onto the record — one call per lead, results in place before the rep arrives, no interaction cost at all because there is no interaction.
@on_event("lead.created")
def enrich_lead(lead_id: str) -> None:
lead = leads.get(lead_id)
result = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": LEAD_SCHEMA}},
messages=[{"role": "user", "content": build_enrichment_prompt(lead)}],
)
fields = json.loads(result.content[0].text)
leads.update(
lead_id,
employee_count=fields["employee_count"],
industry=fields["industry"],
last_funding=fields["last_funding"],
enriched_at=now(),
enriched_by_version=PROMPT_VERSION,
)The output_config block in that call is what makes the write safe to do unattended: it constrains the model to return JSON matching a fixed schema, so the handler is populating three known columns rather than parsing prose, and a company whose funding history genuinely cannot be found comes back as a null in a typed field instead of a sentence explaining the difficulty.
The expense report goes the other way. A paraphrase is fine — nobody needs the receipt's exact wording, they need eight values. But the user does initiate, because filing is something a person decides to do, and the eight fields are fully enumerable at design time: date, merchant, amount, currency, category, project code, VAT amount, and whether a receipt image is attached. A manager must approve, which means the eight values have to be visible as values before submission rather than buried in a transcript. Urgency is immediate in the sense that the employee is sitting there with their phone out. Extraction from the photo is the model's job; showing eight fields is the interface's job.
So the surface is a prefilled form: the photo goes in, the model extracts, and all eight fields render already populated with the extraction confidence attached to the two that tend to be wrong — currency on foreign receipts, and VAT where the receipt shows a gross total only. The user corrects and submits. The interaction is one photo, a glance, and usually zero edits, and the model's contribution is invisible except in how little typing there is.
Notice what happened to the natural-language input everyone wanted. It survived, as the photo and as the paste box; what did not survive was the turn-taking. Extraction and conversation get conflated constantly, and separating them is most of the value of running the grid.
Write the selection down where the next argument will find it
A decision made in a planning meeting and not recorded gets remade every time a new stakeholder joins, usually less carefully. A short record kept next to the code costs a few minutes and settles the re-litigation:
# surfaces/lead-enrichment.yaml
request: "Enrich every new lead with company size, industry, recent funding"
asked_for: chatbot
selected: background_job
discriminators:
paraphrase_acceptable: true # gate cleared; no governing artefact
ambiguity: low # identical task per record
repetition: ~2,400 leads/week
urgency: p10 rep-opens-lead = 11 min # rules out nightly batch
human_approval: none # rep corrects in place
correctable_later: true # overwritten on re-run
rejected:
conversation: "rep would request enrichment 30x/day for a fixed task"
batch_enrichment: "11-minute p10 leaves 10% unenriched at first contact"
review_when: "p10 open time exceeds 6h, or enrichment cost per lead exceeds $0.02"One feature also frequently needs more than one surface, and the grid tends to be read as though it forces a single choice. The lead example ended up with two: the background job that writes the fields, and an inline suggestion in the rep's note composer that proposes an opening line using them. They are separate decisions with separate discriminators — the enrichment is event-driven and slow, the suggestion is user-driven and must land in under 300 milliseconds — and running the grid twice is the correct response rather than a sign that it failed. What the grid rules out is a single surface carrying jobs with incompatible urgency or approval profiles, which is the actual failure it exists to prevent.
The review_when line is the part that earns its keep. Surface choices are made against numbers that drift, and a decision with no re-open condition attached is a decision that will be defended past its expiry date.
The grid is a first pass and it has known blind spots. It says nothing about how fast an inline suggestion has to be before it becomes an irritation, nothing about how a batch job's staleness compounds when the prompt changes, and nothing about what either surface owes you when it is wrong. Those two surfaces are the ones most often listed and least often built, and they are chosen on properties this grid does not measure — which is where the next chapter goes.
Not everything should be a chatbot — read the current version at https://daviddominguez.dev/learn/en/not-everything-should-be-a-chatbot/five-surfaces-and-the-questions-that-choose-between-them