skip to content

In Domain-Driven Design, what is an 'intention-revealing interface' for a class or method, and why does a name like isOverdue() help more than a name like checkFlag() when other developers need to keep changing the code?

level: juniorimportance: must knowfreq 65%

answer

  1. name = contract, not implementation
  2. caller reads signature only
  3. ubiquitous language in names
  4. isOverdue() vs checkFlag()
  5. safe to refactor internals

basics

~20 s

A name (of a class, method, or interface) should tell you what it does and why, not how it's built inside, so you can use it correctly without reading its guts. isOverdue() says exactly what you get back; checkFlag() forces you to go read the code.

solid answer

~40 s

Intention-revealing interfaces mean naming classes, methods, and parameters after the domain concept and outcome they represent, not their implementation. A caller should be able to write correct code by reading the signature and doc comment alone, without inspecting the method body. This matters because in DDD the domain model is the shared language between developers and domain experts (ubiquitous language); vague or implementation-leaking names force everyone touching the code to re-derive intent from internals, which is exactly the friction that makes a model rigid and resistant to change. Good names also make refactoring safer -- if isOverdue() is well named, its internals can change freely as long as the contract holds.

go deeper

for a junior

Can explain that names should say what a method does, and can spot an obviously bad name like process() or doStuff() when shown one.

for a middle

Applies the principle when writing new code: picks domain-vocabulary names, avoids boolean control-flag parameters, and can rename a poorly-named method safely using IDE tooling.

for a senior

Recognizes when a name is technically accurate but still misleading (e.g., a getter that has a side effect), and pushes back on APIs whose names don't match the domain's ubiquitous language, connecting the fix to encapsulation and refactoring safety.

for a principal

Sets naming conventions and review standards across a codebase or team, balances descriptive naming against verbosity, and knows when to invest in a rename sweep versus when the churn cost outweighs the clarity gain.

## What the discipline is An **intention-revealing interface** is a naming discipline applied to classes, methods, and parameters: pick names that describe the **effect and purpose** of an operation in the vocabulary of the domain, not the mechanics of how it's implemented. Concretely, this means asking 'if a new developer sees only this method's signature and a one-line doc comment, can they use it correctly without opening the body?' A method named `isOverdue()` passes that test; one named `checkFlag()` or `process()` fails it, because the caller has to read the source to know what 'checking' or 'processing' actually produces or changes. The same test applies to: - **constructors and factory methods** -- `Money.dollars(100)` versus `new Money(100, 0)` - **boolean parameters** -- avoid unlabeled flags like `save(true)`; prefer an enum or two named methods - **return types** -- returning a well-named domain type instead of an unlabeled boolean when the meaning isn't self-evident from the name alone ## Why the discipline exists This practice exists because Domain-Driven Design treats the code itself as an expression of the **'ubiquitous language'** -- the shared vocabulary domain experts and developers use so a conversation about the business maps directly onto class and method names in the code. When names leak implementation detail instead of expressing intent, two costs compound: 1. Domain experts can no longer read the code (or its tests) as a description of the rules they care about. 2. Developers modifying the internals have to worry that some caller depends on an implementation detail the name never promised. Intention-revealing interfaces are what let you change the inside of a method freely -- that's the entire point of **supple design**, a model that stays easy to reshape as understanding deepens -- because callers only ever depended on the contract implied by the name, not on how it's computed. ## What it costs The cost is real: - Good names take longer to find than lazy ones, and as the team's understanding of the domain deepens, a name that seemed accurate six months ago can become misleading, which means renaming -- and renaming has a blast radius across every caller. - Modern IDE refactoring tools make project-wide renames close to free for well-encapsulated code, but the discipline of actually doing the rename (rather than leaving a stale, confusing name in place because 'it's used everywhere') is a **cultural cost**, not just a technical one. - There's also a tension with brevity: chasing maximal descriptiveness can produce names so long they hurt readability at call sites, so in practice teams balance precision against a name that still reads naturally in context, often relying on the surrounding class or namespace to carry some of the meaning so the method itself can stay short. ## How it shows up in production code In production code, the anti-pattern shows up as generically named classes (`OrderManager`, `DataUtil`, `Helper`) that become dumping grounds for unrelated logic because nothing about the name constrains what belongs there, and as methods whose name describes an algorithm rather than an outcome. - A subtler and more dangerous failure is a name that's technically accurate but incomplete -- a `save()` method that also sends a notification email as a side effect no caller expects, discovered only when a test environment starts spamming real customers. - Boolean control-flag parameters are a recurring source of production incidents: a call site like `shipOrder(order, true)` reads fine to the author but is opaque six months later to anyone who has to guess what `true` means without opening the method. ## The Money illustration A concrete illustration is Eric Evans's own Money example in Domain-Driven Design: instead of exposing a raw numeric field and letting callers do arithmetic and rounding themselves, scattering tax-rounding logic across the codebase, the `Money` type exposes methods like `add(Money): Money` and `applyTax(Percentage): Money`. Every call site reads as a domain statement, the rounding and currency rules live in exactly one place, and if the rounding policy changes, no caller needs to change at all. That single naming and encapsulation choice is what makes the rest of supple design's tools -- side-effect-free functions, closure of operations, assertions -- effective: they all assume the interface already tells the truth about what's happening, so a reader trusts the name enough to build on it without re-verifying it every time.

  • How does an intention-revealing interface relate to encapsulation?
    Encapsulation hides implementation details behind a boundary; an intention-revealing interface is what you put on that boundary so the hiding doesn't cost usability. Without good naming, encapsulation just moves the confusion from 'read the internals' to 'guess what the black box does' -- the caller still can't use the class correctly without extra research. The two together let you change internals freely as long as the named contract still holds.
  • What should you do when the current name of a method no longer matches what the team now understands the domain concept to mean?
    Rename it, and propagate the rename everywhere it's used -- DDD treats the model as a living artifact, and a stale name is worse than an ugly one because it actively misleads. Modern IDEs make a project-wide rename cheap, so the excuse to leave a misleading name in place because 'too many callers' rarely holds for well-encapsulated code.
  • Can an interface be intention-revealing but still be a bad design?
    Yes -- a method can be perfectly named for what it does and still have too many responsibilities, hidden side effects, or take a boolean flag that silently switches behavior. Intention-revealing naming is necessary but not sufficient; it needs to pair with side-effect-free functions and small, well-bounded operations to actually keep the model easy to change.

Like a well-labeled kitchen appliance: a button labeled 'Espresso' tells you what you'll get without needing to know the pump pressure or timing inside; a button labeled 'Mode 3' forces you to read the manual (or the source code) every time.

saying these in an interview costs you the question

  • method/class names like process, handle, manager, util, doStuff
  • boolean parameters whose meaning you must guess, e.g. save(true)
  • needing to open the implementation to know what a method returns
  • names that describe the algorithm instead of the outcome
  • renaming resisted because 'too many callers', even though tooling can do it safely

context