Skip to content

Domain Language in Code

The names from the brief survive the trip into the codebase.

The discipline is small and load-bearing. The same words the named person uses for the things in their work appear in the code, the API, the database, the analytics events, and the support documentation.

When this discipline holds, the trio can read each other's work without translation. When it breaks, every conversation between PO, developer, and QA carries a small tax — what we call X, the code calls Y — and over time, the tax compounds into bugs, support escalations, and refactors that should never have been necessary.

The trip from brief to code

If at any node the word changes — submission becomes exam becomes paper becomes attempt — the chain leaks. Six months later, a CS rep tells a customer they have 17 attempts pending, and the customer has no idea what attempts are because they call them submissions.

Three places the leak begins

  1. The codebase already has a different word. The new feature uses submission; the codebase calls it attempt because it was named that way in 2018. The fix is a rename — small, scary, almost always worth it. A small ADR captures the decision.
  2. A developer translated unconsciously. Submission felt long; the function got named getSub(). The fix is a code review with the brief in hand.
  3. A different system imposes its name. A third-party LMS calls submissions attempts; the integration introduces the name into our code. The fix is to keep the third-party name only at the boundary; translate inside the boundary to our domain word.

Reviewing for domain language

The corpus's pattern: code review (Part 11 — Code Review in the Master Areas) explicitly asks the question do the names in this PR match the brief. If not, the PR returns. This is not pedantry. The naming review is the cheapest moment to catch a mismatch. Every later moment costs more.

The PR reviewer reads the brief. Then reads the diff. The first question is not does this work. The first question is do the names match.

What "domain language" includes

  • Entity namessubmission, grader, rubric, cycle.
  • Action namesgrade, return, finalise, not update, patch, post.
  • State namespending, in-progress, graded, not active, processing, done.
  • Quantity names — what the field actually represents. Cycle time in seconds is honest; duration is vague.
  • Error messages — when something goes wrong, the message uses the same vocabulary.

The boundary translation pattern

When a third-party system uses different vocabulary, translate at the integration boundary. The corpus pattern:

typescript
// LMS adapter — translates LMS vocabulary to ours

interface LMSAttempt { id: string; learner_id: string; ... }
interface Submission { id: string; student_id: string; ... }

export function fromLMS(attempt: LMSAttempt): Submission {
  return {
    id: attempt.id,
    student_id: attempt.learner_id,
    // ...
  }
}

The rest of the codebase never sees attempt or learner. They are LMS words; we use submission and student. The translation is in one file, named clearly, and the boundary is visible.

Why this matters

Three reasons.

  1. The brief becomes the spec. The trio agrees in domain language; the code matches. There is one source of truth.
  2. Support reads what the code says. When a CS rep looks at logs, the words match the customer's vocabulary. Triage is faster. Escalations are clearer.
  3. The next developer onboards faster. A codebase whose words match the product is one a new person can read.

When the codebase has bad names

Renaming is hard. The corpus pattern: rename with the brief. When a brief lands that introduces a word the codebase calls something else, the trio decides at Epic kickoff whether to rename now or after the cycle. Either decision is fine. Don't rename and don't decide is the failure mode.

A codebase that has had submission and attempt meaning the same thing for two years is a codebase carrying tax. The tax is paid in every onboarding, every PR review, every support escalation. The interest is small per transaction; the principal compounds.

Part 2 — Trunk-Based Development →

200apps · How We Work · NWIRE