What is the Principle of Least Astonishment (also called Least Surprise) in software design, and how does it drive naming decisions?
answer
- gap between apparent and actual behavior
- expectations: name, platform, domain, local convention
- get* = cheap and read-only
- surprises go in the name, not the docs
- audience-relative heuristic, not a law
basics
~20 sIt says a component should behave the way a reasonable reader expects from its name and the surrounding conventions. If people are surprised by what a function does, the design is wrong even when the code is correct. Names must promise exactly what happens.
solid answer
~50 sThe Principle of Least Astonishment (PoLA) states that a system's behavior should match the mental model its interface creates in the mind of whoever uses it — usually another developer reading or calling the code, not an end user. Expectations come from four sources: the name itself, platform/language conventions, domain vocabulary, and conventions already established in this codebase. So `getBalance()` must not open a network socket, `size()` should not be O(n) when the rest of the API is O(1), `save()` should not also send email, and if one repository method is `findById` the next must not be `retrieveByIdentifier`. When behavior genuinely must surprise, encode the surprise in the name — `refreshAndGetBalance()`, `deleteOrThrow()` — so the astonishment lands at read time instead of at 3am in production. PoLA is a heuristic for lowering cognitive load, not a formal law; its payoff is fewer defects from callers who never read the docs.
go deeper
Define it plainly — behavior should match what the name and conventions suggest — and give one concrete example such as a getter that secretly writes to the database.
Enumerate the sources of expectation (name, platform convention, domain language, local codebase style) and show how you fix an astonishing API by renaming or splitting it.
Extend beyond naming to defaults, side effects, error semantics, and performance contracts; discuss consistency as a system-level property and how you enforce it in review.
Frame it as cognitive-load and defect-rate economics across many teams, discuss audience-relative expectations, deliberate exceptions with an explicit budget, and how conventions are governed and evolved over time.
## The idea **Astonishment** is the gap between what an interface *appears* to do and what it *actually* does. The Principle of Least Astonishment (PoLA) says: minimize that gap. If a caller must read the implementation or the documentation to predict behavior, the interface has already failed, because most callers read neither. The "user" being astonished is normally a **programmer** — the person calling your function, wiring your service, or editing your config file. (The same principle exists in UI design for end users; in software design we mostly mean the API consumer.) ## Where expectations come from 1. **The name.** `get*` implies cheap and read-only. `is*`/`has*` implies a boolean with no side effect. `create*` implies a new thing exists afterwards. `validate*` implies it may reject but not mutate. 2. **Platform/ecosystem convention.** In most ecosystems `equals`/`==` is symmetric, `toString` is safe to call from a debugger, `close()` is idempotent, and comparison is consistent with equality. Breaking one of those breaks code you never see. 3. **Domain vocabulary.** In banking, "settle" and "authorize" are distinct operations; using them interchangeably astonishes any domain expert reading the code. 4. **Local convention.** Whatever your codebase already does becomes the expectation. Consistency inside one system often beats an objectively "nicer" name introduced in isolation. ## Classic astonishments - A getter that lazily performs I/O, mutates a cache, or can throw a timeout. - `size()` or `count()` that is O(n) or hits the database. - A method that returns `null` sometimes, an empty collection other times, and throws in a third case. - Ambiguous units: `setTimeout(30)` — seconds or milliseconds? Name or type the unit (`timeoutSeconds`, or a Duration type). - Boolean parameters: `createUser(name, true)` — the call site tells the reader nothing. - The same word with two meanings across modules (`Order` = shopping cart in one module, fulfilment record in another). ## Remedies - Name the full effect, not the happy part (`saveAndPublish` beats a `save` that quietly publishes). - Keep a **glossary / ubiquitous language** so one concept has one word system-wide. - Prefer types over primitives for units and identifiers, so the compiler carries the expectation. - Where a surprise is unavoidable and valuable, make it **loud**: a distinct name, an explicit parameter, or a separate method rather than a hidden flag. ## Limits PoLA is a heuristic and can conflict with other goals — performance, security defaults, or a genuinely novel abstraction with no established convention. It is also **audience-relative**: what astonishes a newcomer may be idiomatic to an expert. Resolve conflicts by asking *who* the primary reader is and what they already know; document the deliberate exceptions, and keep them few.
- If two conventions conflict — the wider ecosystem's convention and your codebase's existing convention — which do you follow?Usually the local one for internal code, because readers calibrate on what surrounds them and inconsistency inside one codebase is the bigger cost. For a public or published API, follow the ecosystem convention, since your callers' expectations were formed elsewhere. Either way, pick one and apply it uniformly rather than mixing.
- Is naming enough to satisfy the principle?No. Naming addresses only the expectation the identifier creates. Defaults, side effects, error behavior, performance characteristics, and thread-safety all create expectations too, and any of them can astonish independently of the name.
A light switch by the door: you flip up, the light comes on. A switch that also starts the garbage disposal is technically functional and completely astonishing — and someone will lose a finger.
saying these in an interview costs you the question
- Treating it as an end-user-UI-only rule and ignoring it for internal APIs
- "It's documented, so it's not surprising" — documentation does not remove astonishment for callers who never read it
- Claiming it means "never do anything clever", rather than "make the clever part visible"
- Using it to justify freezing a genuinely bad convention forever
- Assuming expectations are universal instead of relative to a specific audience