Engineering Atlas › Problem Solving & Incidents (AI era)

Translating a Domain into Known Problems

12 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Step 1: collect the nouns and verbs

Sit with the domain expert, read the support tickets and the existing code, and write down every noun and verb people use. This is the knowledge-crunching and shared-language work of domain-driven design (Fowler on ubiquitous language; Domain-Driven Design (Evans, 2003), Ch 1, "Crunching Knowledge", and Ch 2, "Communication and the Use of Language"). In a warehouse: SKU, bin, pick list, wave, picker, cart, replenishment. In an agent platform: task, tool call, plan, sub-agent, budget, approval. Do not translate yet; just collect.

Step 2: give each noun a role

Most nouns fall into four roles. Resources are things that can be scarce or owned: bins, stock units, GPU slots, budgets. Actors act on resources: pickers, customers, agents. Events are facts that happened: "picked", "tool returned", "approved". Processes are multi-step activities: a wave, an order fulfilment, an agent's plan. Roles tell you which shapes can appear. Resources invite contention and hot spots. Events invite duplicates and ordering problems. Processes invite partial failure and multi-step consistency problems.

Step 3: write the invariants in domain words

An invariant is something that must always be true. Write it in the expert's words and get them to sign it off. "A bin's count never goes negative." "A picker works on one wave at a time." "An agent never spends more than its task budget." "A tool that sends email runs at most once per approval." Experts can tell you which invariants matter and how much a violation costs, which is the information engineers usually lack.

Step 4: turn invariants into constraints

Each invariant maps to a known mechanism, chosen by shape:

Step 5: translate failures back

For each mechanism, ask how it fails and describe that failure in domain words. A compare-and-set that loses under contention means "the picker is told the bin is busy and gets another one". A saga step that fails means "the customer is refunded within an hour". The compensation itself can fail, so it must be an idempotent command, and some steps (a sent email, a legally binding action) cannot be undone at all; the Azure pattern says to mark those points of no return and place them after the checks that can still fail (Azure: compensating transaction). Experts can then say whether that is acceptable, which turns arguments about technology into decisions about the business.

Down the stack

Layer 1The contract

Translation produces a document with four tables: glossary (noun, role), invariants (in domain words, owner, cost of violation), mechanisms (invariant, shape, mechanism, test), and failure translations (mechanism failure, domain effect, accepted or not). A reviewer can check that every invariant has a mechanism and a test, and every mechanism has a domain-language failure.

Layer 2The mechanism

The mappings are concrete. UPDATE bins SET qty = qty - $1 WHERE id = $2 AND qty >= $1 enforces "never negative" in one statement; zero rows updated means the pick must go elsewhere. In Postgres, CREATE UNIQUE INDEX one_active_wave ON assignments (picker_id) WHERE state = 'active' enforces "one wave at a time"; a second insert fails with error code 23505 (unique_violation; Postgres error codes). An idempotency table with PRIMARY KEY (approval_id) and INSERT ... ON CONFLICT DO NOTHING enforces "at most once per approval"; AWS also stores the original parameters with the token, so a retry with changed parameters is rejected instead of silently replayed (AWS Builders' Library). Each mechanism converts a sentence into something the database refuses to violate, which is stronger than application code that checks first and writes second.

Layer 3The machine

Pushing invariants into the database works because the database serialises the conflicting part on the machine. A conditional UPDATE takes a row lock. Postgres records row locks in the row itself rather than in a shared-memory table, so locking a row can cause a disk write but there is no limit on how many rows one transaction can lock (Postgres explicit locking). Checking and writing therefore happen while no other writer can touch the row. A unique index check happens inside the B-tree insert: the inserting backend locks the leaf page, finds any equal key, and waits if another transaction inserted the same key and has not committed yet. These are microsecond operations on one machine, against millisecond round trips if your application tries to coordinate the same thing across services.

Why it's built this way

  1. Domain experts know which rules matter but not how systems fail; engineers know the reverse.
  2. Invariants written in domain words are something both sides can read and agree on.
  3. Each invariant falls into a known shape, and each shape has known mechanisms.
  4. The strongest mechanisms put the invariant where conflicting writes are serialised anyway, usually the database.
  5. Translating failures back into domain words lets the people who own the cost decide what is acceptable.

Diagram

flowchart LR
  N["Nouns and verbs"] --> R["Roles: resource, actor, event, process"]
  R --> I["Invariants in domain words"]
  I --> S["Shape per invariant"]
  S --> M["Mechanism and test"]
  M --> F["Failure in domain words"]
  F --> X["Expert decides: accept or redesign"]

Worked example

An AI platform team is asked to let agents call paid external tools: web search at 0.5 cents a call, a code sandbox at 2 cents a minute, and an email tool. Nouns: task, agent, sub-agent, tool call, budget, approval. Roles: budget is a resource, agents are actors, tool calls are events, the task is a process.

Invariants from the product owner: a task never spends more than its budget (default 5 dollars); an email is sent at most once per human approval; a task's final answer never reflects a tool result from a cancelled sub-agent.

Translation: the budget is contention, because 20 sub-agents may spend in parallel, so each spend is a conditional decrement on the task's budget row (UPDATE budgets SET remaining = remaining - $1 WHERE task_id = $2 AND remaining >= $1). The email rule is duplicates: the email tool takes the approval ID as its idempotency key. The cancelled sub-agent rule is ordering: each sub-agent result carries the plan version it was produced under, and results from an older version are dropped.

Failure translations: under heavy contention a sub-agent's spend can be refused, so "the agent reports it ran out of budget and asks the user". Accepted. A crash between sending the email and recording it would mean "the email may be sent but marked unsent", so the team sends via a provider that also honours idempotency keys. At 2 million tool calls a day, about 23 a second, the budget row for one task sees at most 20 concurrent writers: trivial for a row lock.

In the wild

Trade-offs

Where the invariant livesStrengthsWeaknessesChoose when
Database constraintCannot be bypassed; atomicLimited to one database; harder to changeThe rule is critical and data is in one place
Application check then writeFlexible, easy to expressRaces between check and writeLow-stakes rules, or behind a lock
Single-writer service per entitySerialises any logic, across storesThroughput per entity is limitedComplex rules on one entity (an account, a task)
Reconciliation after the factWorks across systems and vendorsDetects late, needs repairCross-company flows such as payments

Pitfalls in production

Leadership lens

Check yourself

  1. Translate "a hotel room is never assigned to two guests on the same night" into a mechanism.

    Show answer

    Contention on a room-night resource: a unique constraint on (room_id, night) in the assignments table, or a conditional update on a room-night row, so a second assignment fails atomically.

  2. Why translate failure modes back into domain language?

    Show answer

    Only domain owners know the cost of a failure, such as how bad it is for a rider to wait or a payment to be delayed, so they need it described in their terms to decide what is acceptable.

  3. An agent platform must ensure a task never exceeds its budget while 20 sub-agents run in parallel. Which shape, and why is "read the remaining budget, then spend" wrong?

    Show answer

    Contention. Two sub-agents can both read 1 dollar remaining and both spend 80 cents; the check and the write must be one atomic conditional update.

  4. When is reconciliation a better home for an invariant than a database constraint?

    Show answer

    When the data that must agree lives in different systems or companies, such as your ledger and a bank's, so no single transaction can enforce it.

Sources and further study

Primary

Systems docs

Books

Other curricula

Free resources

Open in the interactive app: practice, quiz, mock interview →