Ubiquitous Language
Ubiquitous language is the shared vocabulary between domain experts and engineers — reflected in code, docs, and conversation without translation layers.
Introduction
Ubiquitous language is the shared vocabulary between domain experts and engineers — reflected in code, docs, and conversation without translation layers. Stripe payment terminology (charge, capture, dispute, payout) must match API names and class names or money-moving bugs follow.
Real production story
A Stripe internal team built "PaymentRequest" while treasury called the same concept "SettlementIntent." Support tickets referenced disputes that engineering tracked as "chargebacks" in code and "exceptions" in dashboards. A $2M reconciliation gap traced to a field mapped "amount" in one context and "value" in another with different currency assumptions. The ubiquitous language initiative produced a glossary blessed by treasury, lint rules on API naming, and banned synonym list in code review. Requirement-to-code mismatch defects dropped 60% in two quarters.
Business problem
Stripe operates in regulated financial language where precision is legal and monetary. Translation gaps between product, support, legal, and engineering create wrong implementations that tests miss because everyone uses different words for the same thing.
- Correctness: Misnamed concepts hide wrong invariants — "refund" vs "reversal" have different ledger rules.
- Onboarding: New engineers learn two languages — business and code — doubling ramp time.
- Cross-team integration: Partner APIs fail when Stripe's published language diverges from internal models.
Architecture overview
Ubiquitous language evolves with the domain through event storming, glossary workshops, and continuous refinement — not a one-time dictionary. Code is the source of truth when glossary and implementation conflict; fix the mismatch immediately.
- Within bounded context: One language per context — "Customer" in Connect ≠ "Customer" in Billing.
- In code:
Charge.capture()notPayment.finalize()if domain says capture. - In APIs: Public REST paths mirror glossary — breaking changes are language changes.
- Anti-pattern: Anemic "Manager/Handler/Processor" names that erase domain meaning.
Architecture motivation
Ubiquitous language is executable: Class names, event names, and API paths are the glossary. Staff architects reject PRs that introduce synonyms without domain expert approval.
- Force: Hundreds of microservices must integrate on shared payment vocabulary.
- Constraint: Legacy synonyms exist — document deprecated terms and migration, don't pretend purity day one.
- Outcome: Glossary linked from repo; code matches terms product uses in specs.
Internal architecture
Stripe language pipeline — from domain expert mouth to running code:
- Deprecated terms listed with replacement and sunset date — "chargeback" → "dispute".
- Each bounded context has glossary section — link from CONTEXT.md in repo root.
Domain workshop (treasury + eng)↓Glossary (markdown in /docs/domain — versioned)↓Code model (Charge, Dispute, Payout — no synonyms)↓OpenAPI / protobuf (published language)↓Partner docs + support macros (same terms)↓CI: banned-term linter + glossary drift check
Data flow
Language flows through artifacts: Product spec uses glossary term → ticket links glossary ID → PR must use same term in class/event names → API schema generated from model → support sees same word in admin UI.
- Spec → code: BDD scenarios written in ubiquitous language — executable acceptance.
- Events:
dispute.creatednotexception.opened. - Metrics: Dashboard panels named like glossary — on-call speaks same language as PM.
System design diagram
Two diagrams show the Ubiquitous Language topology and the primary request/event path used in production at scale.
Production code example
Glossary drift linter — TypeScript CI script for Stripe-style domain repos:
- Pair linter with glossary PR requirement when introducing new domain terms.
- Proto and OpenAPI files included — language leaks start at schema boundaries.
import fs from "node:fs";import fg from "fast-glob";const BANNED = [{ term: /PaymentRequest/g, use: "ChargeIntent" },{ term: /chargeback/gi, use: "Dispute" },{ term: /finalizePayment/g, use: "captureCharge" },];const GLOSSARY = JSON.parse(fs.readFileSync("docs/domain/glossary.json", "utf8")) as Record<string, string>;async function lint(): Promise<void> {const files = await fg(["src/**/*.ts", "proto/**/*.proto"]);const errors: string[] = [];for (const file of files) {const text = fs.readFileSync(file, "utf8");for (const { term, use } of BANNED) {if (term.test(text)) {errors.push(`${file}: use "${use}" instead of banned ${term}`);}}}for (const [canonical, definition] of Object.entries(GLOSSARY)) {if (!definition) errors.push(`Glossary entry empty: ${canonical}`);}if (errors.length) throw new Error(errors.join("\n"));}lint();
Enterprise case study
Stripe dispute terminology unification: Synonyms caused reconciliation gaps and support errors.
- Before: Three terms for dispute lifecycle; dashboard ≠ API ≠ treasury reports.
- Decision: Canonical glossary, class rename program, OpenAPI alignment, banned synonyms lint.
- After: Reconciliation gap class eliminated; defect rate on dispute flows down 60%; partner docs single term.
Trade-offs
- Purity vs migration: Rename incrementally with @Deprecated aliases — big-bang rename breaks integrators.
- English vs locale: Code in English; UI translated — glossary defines canonical English term.
- Precision vs brevity: Long accurate names beat short ambiguous ones in money domains.
- Context-specific terms: Same word forbidden across contexts without prefix — use ConnectMerchant vs BillingCustomer.
Security considerations
Language precision supports security audits: Regulators ask what a "hold" means — glossary and code must agree.
- Authorization: Permission names match domain actions —
dispute:resolvenotadmin:fix. - Audit logs: Log event names match glossary — SIEM rules depend on consistent vocabulary.
- Social engineering: Support tools use same terms customers hear — reduces mis-routed actions.
Scalability analysis
Language scales via tooling: Automated lint, glossary search, and onboarding quests — not quarterly meetings only.
- 500+ services: Central glossary API; IDE plugin suggests canonical term on typo.
- Partner ecosystem: Published language versioned semver — language breaking change is API major bump.
- AI assist: RAG over glossary for spec review — flag non-canonical terms in design docs.
Failure scenarios
Language failures are production bugs: Wrong amount field, wrong state machine transition, support actions hitting wrong API.
- Synonym drift: New team introduces "PaymentIntent" duplicate — banned-term CI catches on PR.
- Legacy UI: Admin shows "chargeback" while API returns dispute — user error during Sev-1.
- Translation layer: BFF renames fields "for convenience" — kill BFF mapping that hides domain terms.
Staff engineer insights
- If product and engineering need a translator in the meeting, your ubiquitous language failed — fix the glossary before fixing the code.
- Renaming a class is cheaper than explaining the wrong name in every incident for five years.
- Ubiquitous language is per bounded context — forcing one enterprise glossary without context prefixes creates new ambiguity.
Interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1AdvancedQuestionHow do you introduce ubiquitous language in a legacy codebase with wrong names?+
Answer
Follow-up
2AdvancedQuestionSame word means different things in two bounded contexts. What do you do?+
Answer
Follow-up
3AdvancedQuestionHow does ubiquitous language affect event-driven architecture?+
Answer
Follow-up
Architecture review questions
- Does glossary exist per bounded context and link from repo README?
- Do class, event, and API names match glossary without unapproved synonyms?
- Are deprecated terms listed with migration timeline?
- Do BDD or acceptance tests use domain language from specs?
- Are support and admin UIs aligned with public API terminology?
- Does CI lint block banned synonyms on changed files?
Summary
Ubiquitous language at Stripe aligns treasury, product, and code on precise payment vocabulary — enforced by glossaries, naming lint, and bounded-context-specific terms so requirements translate directly into correct implementations.