Lazy Class¶
The code smell in which a class (or function, module, package) carries too little responsibility to justify the overhead it imposes — the defect living in the economy, not the behaviour, so a correct, clearly-named boundary can still be flagged when its carried value fails to clear its enumerable tax.
Core Idea¶
Lazy class is the code-smell pattern in which a component — typically a class in object-oriented code, but the diagnosis extends to functions, modules, and packages — exists in the codebase while carrying too little responsibility to justify the overhead it imposes. Every abstraction has a cost: a name to learn and remember, a file to navigate to, a vocabulary item to teach new contributors, an interface to keep stable across change, a test surface to maintain. That overhead is justified when the abstraction does real work — hiding genuine complexity, providing genuine reuse across multiple callers, naming a domain concept that would otherwise be scattered and unnamed, or enforcing a contract that prevents coupling. When an abstraction's earned value falls below its overhead, it is anti-economical: more cognitive and maintenance tax is paid to maintain the boundary than is saved by having it.
The diagnostic test is not about correctness or naming clarity but about the ratio of responsibility carried to overhead imposed. A class that wraps a single method and forwards all calls to one implementation, a data class with fields but no behaviour, a single-method module called from exactly one place, a utility function that performs one non-reusable line of work — these are lazy classes not because they are wrong but because they would be cheaper to inline. Martin Fowler catalogued the pattern in Refactoring: Improving the Design of Existing Code (1999), and the canonical resolution is the inline class (or inline function) refactoring: fold the abstraction back into its caller and delete it.
Lazy class is the structural complement of god class, which concentrates too much responsibility in one abstraction. Both represent violations of the same underlying balance — a healthy abstraction sits in a middle band of responsibility, enough to amortise its overhead but not so much that it becomes unmanageable. Lazy class names the failure mode at the low end of that spectrum: not enough responsibility for the boundary to earn its keep. It commonly originates as speculative generality — an abstraction introduced in anticipation of a future variation that never materialised — and persists because, once created, the boundary is preserved by inertia even after the rationale for it has dissolved.
Structural Signature¶
Sig role-phrases:
- the candidate abstraction — a class (or function, module, package) that exists as a structurally distinct boundary in the codebase
- the enumerable overhead — the fixed tax every boundary levies: a name to learn, a file to navigate, an import, a stack-trace layer, an interface to keep stable, a test surface
- the carried responsibility — the earned value that could justify the tax: complexity hidden, reuse across multiple callers, a domain concept named, a contract enforced
- the overhead-vs-value ratio — the diagnostic quantity: whether carried responsibility clears the overhead, decoupled from correctness and naming (a correct, clearly-named class can still fail it)
- the responsibility axis with two poles — the single spectrum on which lazy class (too little) and god class (too much) are the same balance failing in opposite directions, healthy abstraction occupying the middle band
- the hidden-currency exception — a constraint invisible to the local view (stable public API, plugin seam, versioning boundary) that pays down the overhead in another currency, justifying a boundary that looks anti-economical
- the fix menu — read off the component's position: inline-and-delete, merge with a sibling, grow deliberately, or keep for an external constraint
What It Is Not¶
- Not a correctness or naming defect. A lazy class can be perfectly correct and clearly named and still be a defect, because the defect lives in the economy, not the behaviour — more cognitive and maintenance tax is paid to keep the boundary than is saved by having it. Reviewers who check only that it works and reads well will miss it entirely.
- Not "thin abstraction is automatically wrong." A boundary carrying little local responsibility may still be earning its keep in a currency the local view does not show — a stable public API other teams compile against, a plugin seam, a versioning boundary. The diagnosis is the overhead-vs-value ratio, not thinness alone; inlining a boundary that is paying down its overhead externally is the error in the other direction.
- Not god class. God class is the opposite pole of the same responsibility axis — too much responsibility concentrated in one abstraction. Lazy class is the low-end failure: too little for the boundary to earn its keep. Same balance failing in opposite directions, with healthy abstraction in the middle band.
- Not speculative generality or premature abstraction. Those name a frequent origin of a lazy class — a boundary introduced in anticipation of a variation that never arrived — describing why it was created. Lazy class names its present state: an abstraction that, whatever its history, now carries too little to justify its overhead.
- Not lava flow. Lava flow is unused-code residue retained under deletion-risk uncertainty; lazy class is an under-loaded abstraction that is still very much in use. Both are code smells, but one is about dead code kept because removal is risky, the other about live code whose boundary does not pay for itself.
Scope of Application¶
Lazy class lives across the code-quality and maintainability subfields of software and computing, recurring at every construct level a codebase exposes; its reach is bounded to that domain (the more general abstraction-overhead-must-earn-its-keep balance that recurs in organisational design, statute drafting, and curricula is carried by parent primes such as abstraction, parsimony, and minimalism, not by this named smell).
- Object-oriented code — the canonical home: wrapper classes that forward to one implementation, data classes with fields but no behaviour, single-method classes called from one place.
- Functional and procedural code — the function-level twin diagnosed and fixed by inline-function: a one-line non-reusable helper used by a single caller.
- Module and package structure — a single-function module or a configuration package holding one constant, an under-loaded boundary at coarser granularity.
- Public-API and plugin surfaces — the hidden-currency exception lives here: a thin boundary that looks lazy locally but is correctly retained because a stable API, plugin seam, or versioning contract pays down its overhead.
Clarity¶
Naming lazy class makes legible a cost that the prevailing instinct treats as zero: abstraction itself has a price, and the price must be earned. The default move in many teams is to extract a class for symmetry, for tidiness, or in anticipation of a future variation; lazy class supplies the counter-question — does this boundary carry enough responsibility to amortise the name, the file, the import, the stack-trace layer, and the test surface it imposes? Crucially, it decouples this from the question reviewers usually ask. A lazy class can be perfectly correct and clearly named and still be a defect, because the defect is not in the code's behaviour but in its economy: more cognitive and maintenance tax is paid to keep the boundary than is saved by having it. That reframing is what licenses inlining as a legitimate, even virtuous, move rather than vandalism against a "working" class.
The label also organises code-quality reasoning by giving the abstraction-sizing problem two poles instead of one. Practitioners readily see god class — too much responsibility — as pathological; lazy class names the symmetric failure at the low end, so a healthy abstraction becomes visibly a middle band of responsibility, with both under-loading and over-loading recognised as the same balance failing in opposite directions. That, in turn, sharpens the diagnostic question and the menu of responses: a thin class is not automatically wrong, so the practitioner asks whether to inline it, merge it with a sibling, grow its responsibility deliberately, or keep it precisely because some other constraint (a stable public API, a plugin seam, a versioning boundary) is paying down its overhead in a currency the local view does not show.
Manages Complexity¶
Judging whether each small component in a codebase earns its place is, untreated, an open and contentious case-by-case argument — a wrapper that forwards one method, a data class with fields and no behaviour, a single-method module called once, a one-line utility — with no shared criterion, so reviewers reach for correctness and naming, which a thin abstraction can satisfy while still being a defect. Lazy class compresses that scatter of judgments to a single quantity: the ratio of responsibility carried to overhead imposed. Every abstraction levies a fixed, enumerable tax — a name to learn, a file to navigate, a vocabulary item to teach, an interface to keep stable, a stack-trace layer, a test surface — and the diagnostic is simply whether earned value (complexity hidden, reuse enabled across multiple callers, a domain concept named, a contract enforced) clears that tax. Crucially this decouples the question from correctness and naming, which is exactly what lets a perfectly correct, clearly named class be flagged: the defect lives in the economy, not the behaviour, so the analyst tracks one number per component rather than relitigating each from scratch.
The compression deepens by giving the abstraction-sizing problem a single axis with two poles, which both locates any given component and fixes the response. Practitioners already read god class — too much responsibility — as pathological; naming lazy class as the symmetric failure at the low end makes a healthy abstraction visibly a middle band of responsibility, so under-loading and over-loading become the same balance failing in opposite directions rather than two unrelated smells. Placing a component on that axis then reads off the branch of fixes: a class below the band is a candidate to inline into its caller and delete, or to merge with a sibling carrying related responsibility, or to grow deliberately by absorbing related logic — unless some constraint invisible to the local view (a stable public API, a plugin seam, a versioning boundary) is paying down its overhead in another currency, in which case it is correctly kept. A sprawling, criterion-less debate about which components deserve to exist thereby collapses to one ratio, one responsibility axis, and a short decided menu — inline, merge, grow, or keep-for-an-external-constraint — read off where the component sits.
Abstract Reasoning¶
The first characteristic move is diagnostic on an economy, not on correctness: from a component's structure, infer whether the responsibility it carries clears the overhead it imposes, and flag it as a defect when it does not — regardless of whether its code is correct and clearly named. The signature being read is a thin abstraction whose tax is fully enumerable (a name, a file, an import, a stack-trace layer, a test surface) set against earned value that is near zero (no complexity hidden, no reuse across multiple callers, no domain concept named, no contract enforced): a wrapper forwarding one method, a data class with fields and no behaviour, a single-method module called once, a one-line non-reusable utility. So the analyst reasons FROM "this PricingResultWrapper exposes only a toString() that delegates to the wrapped result, hides nothing, names nothing, reuses nothing" TO "overhead exceeds earned value; this is a lazy class even though it works and reads fine" — the defect located in the economy, not the behaviour, which is precisely what the move must establish to license inlining a correct class.
The second move is boundary-drawing on a single responsibility axis with two poles, which both classifies a component and selects its fix. Because god class (too much responsibility) and lazy class (too little) are the same balance failing in opposite directions, a healthy abstraction is a middle band, and the analyst places any component on that axis and reads the remedy off its position. The reasoning runs FROM "this component sits below the band" TO "the candidate fixes are inline-into-caller-and-delete, merge-with-a-sibling carrying related responsibility, or grow-deliberately by absorbing related logic," and FROM "this component sits inside the band" TO "leave it." The boundary also discriminates lazy class from its catalogue neighbours by mechanism: it is under-loaded abstraction, distinct from lava flow (unused-code residue retained under deletion-risk), from speculative generality and premature abstraction (its frequent origins — a boundary created for a variation that never arrived — rather than its present state), and from god class (the opposite pole) — so the analyst names which smell is in play, and therefore which refactoring applies, by where on the axis the component falls.
The third move is interventionist with an explicit hidden-currency exception, guarding against inlining a boundary that is in fact earning its keep invisibly. The default inference runs FROM "this thin class carries too little local responsibility" TO "inline it," but the move requires a check for overhead paid down in a currency the local view does not show: a stable public API, a plugin seam, or a versioning boundary can justify a boundary that looks anti-economical from inside the file. So the analyst reasons FROM "this thin class is the published interface other teams compile against, or the extension point plugins hook" TO "keep it — an external constraint amortises its overhead even though local responsibility does not," and only FROM "no such external constraint is paying for it" TO "inline and delete." This is the move that converts inlining from reflexive deletion into a justified one, and it carries the corollary prediction that a boundary correctly retained for an external constraint will re-accrue the same lazy-class diagnosis from any reviewer who sees only the local economy — so the constraint, not just the code, is what must be recorded.
Knowledge Transfer¶
Within software the smell transfers as mechanism across the whole of code-quality work, and across construct levels within it. The diagnostic — does carried responsibility clear the enumerable overhead (name, file, import, stack-trace layer, test surface)? — is the same whether the under-loaded unit is a class, a function, a module, or a package, so the class-level "lazy class" and the function-level "inline function" are one diagnosis at two granularities. The single responsibility axis with its two poles (lazy class at the low end, god class at the high end, healthy abstraction in the middle band), the decoupling from correctness and naming, the menu of fixes (inline-and-delete, merge with a sibling, grow deliberately, or keep for an external constraint), and the hidden-currency exception (a stable public API, plugin seam, or versioning boundary paying down overhead invisibly) all carry intact. The intervention vocabulary that travels is software-internal but fully portable across the field: Fowler's inline-class and inline-function refactorings, the code-smell catalogue, responsibility-driven design, component-cost analysis. This is one diagnosis recognised across object-oriented, functional, and modular codebases — mechanism, not analogy.
Beyond software the situation is the shared-abstract-mechanism case. A genuinely more-general balance recurs across substrates — the overhead of a boundary must be paid for by the value the boundary carries; under-loaded units are net-negative — and its instances are real and recognisable: ceremonial offices and redundant management layers in organisational design, tautological or purely-delegating statutory sections in legal drafting, curriculum units with too little distinct content to justify their slot in pedagogy. In each the diagnosis lands, but "lazy class" as named does not transport: the intervention vocabulary differs (consolidate departments, strike redundant clauses, combine course units), the abstraction unit is no longer a code construct, and domain-specific dynamics — political constraint, the political economy of statute drafting, accreditation rules — dominate rather than the clean overhead-versus-value calculation the term carries in software. What travels is the abstract balance, and the catalogue already distributes that across parent primes: abstraction (the cognitive move of suppressing detail, whose under-powered failure this is), parsimony / Occam's razor (do not multiply entities beyond necessity), and minimalism (under-loaded structure as anti-pattern). So a reviewer who strikes a do-nothing committee is enacting the parsimony/abstraction-economy principle, not doing lazy class; the cross-domain lesson should carry those general primes, while "lazy class" stays the software code-smell name — calibrated against class size, test surface, and refactoring economics, with its Fowler/Beck/Martin genealogy — for one substrate's version of the balance (see Structural Core vs. Domain Accent).
Examples¶
Canonical¶
Fowler's own inline-class example in Refactoring (1999) is the defining instance. A Person class holds a telephone number, and the number lives in a separate TelephoneNumber class carrying two fields — areaCode and number — with getters/setters and nothing else. TelephoneNumber was perhaps introduced expecting phone-handling logic to grow, but that logic never arrived, and the class is used only by Person. It hides no complexity, is reused by no second caller, enforces no contract, and names no concept that Person could not carry directly — yet it still costs a file, a name, an import, and a test surface. The prescribed resolution is Inline Class: fold areaCode and number back into Person and delete TelephoneNumber.
Mapped back: TelephoneNumber is the candidate abstraction; its file, name, import, and test surface are the enumerable overhead. Its carried responsibility is near zero — no complexity hidden, no reuse across callers, no contract enforced — so the overhead-vs-value ratio fails even though the class is correct and clearly named. That failure sits at the low pole of the responsibility axis, and the reading off the fix menu is inline-and-delete.
Applied / In Practice¶
Large teams operationalize the smell through automated code-quality gates. Static-analysis tools (SonarQube and similar) ship rules that flag classes with too little behavior — pure data classes, single-method wrappers — as maintainability issues in continuous integration, so a thin abstraction surfaces automatically at review time rather than by chance. But experienced reviewers routinely override the flag for one recognized reason: a thin class that is the module's published interface — the type other teams compile against, or the extension point plugins implement — is correctly retained even though its local responsibility is minimal, because a stability/versioning contract is paying its overhead in a currency the analyzer cannot see. The decision recorded is not just "keep," but why.
Mapped back: The linter enforces the overhead-vs-value ratio mechanically, catching under-loaded boundaries reviewers would miss. The override is the hidden-currency exception: a stable public API or plugin seam amortizes the overhead externally, so the fix menu resolves to keep-for-an-external-constraint. Recording the constraint matters because, per the concept, a boundary kept for an invisible reason will otherwise re-accrue the lazy-class flag from the next reviewer who sees only the local economy.
Structural Tensions¶
T1: Economy versus correctness (flagging a class that works and reads fine). The concept's decisive move is to decouple the defect from correctness and naming — a lazy class can be perfectly correct, clearly named, and still be a defect, because the fault lives in the economy, not the behavior. This is what licenses inlining a working class as a virtuous act rather than vandalism. But the same decoupling makes the smell invisible to the checks reviewers actually run: a reviewer confirming that a class works and reads well will pass it, and the proposal to delete correct, well-named code is inherently contestable in a way that fixing a bug is not. The tension is that the defect sits in a dimension (maintenance economy) that ordinary review does not inspect, so diagnosing it requires overriding the intuition that working, legible code is good code. Diagnostic: Is the objection that the class is wrong or unclear, or that it is correct and clear yet costs more overhead than the responsibility it carries returns?
T2: The local ratio versus the hidden currency (thinness that may be paying externally). The diagnostic is a ratio — carried responsibility against enumerable overhead — read at the component. But a boundary carrying little local responsibility may be earning its keep in a currency the local view cannot see: a stable public API other teams compile against, a plugin seam, a versioning boundary. So the clean local test has a standing escape hatch that reintroduces exactly the judgment it seemed to remove, and a correctly-kept boundary will re-accrue the lazy-class flag from every reviewer (and every linter) that sees only the local economy. The tension is that the value side of the ratio is not always local, so the component-level diagnostic is systematically blind to the one reason a thin boundary might be right, and the fix is to record a constraint the code itself does not show. Diagnostic: Has the check confirmed that no external constraint (public API, plugin seam, versioning contract) is paying this boundary's overhead, or is it reading value from the local view alone?
T3: Present-economy calculation versus the boundary's option value (inlining as premature de-abstraction). The default remedy is inline-and-delete, priced on the boundary's current overhead against its current value. But a boundary also carries option value: it is a seam where a future variation can be absorbed without disturbing callers, and deleting it forecloses that. Lazy class commonly originates as speculative generality — a boundary created for a variation that never arrived — but the diagnosis cannot distinguish a boundary whose variation will never come from one whose variation is merely late, and aggressive inlining is the mirror error of premature abstraction: premature de-abstraction. The tension is that the present-economy calculation ignores the flexibility a boundary holds against unknown future change, so the same inlining that correctly removes dead generality can also destroy a seam that would have paid for itself. Diagnostic: Is this boundary's variation genuinely never coming (inline it), or is the anticipated change merely late, so deleting the seam trades present tidiness for foreclosed future flexibility?
T4: The enumerable tax versus the uncountable value (a ratio whose sides are not commensurable). The concept's rhetorical force comes from the overhead being fixed and enumerable — a name, a file, an import, a stack-trace layer, a test surface — which makes the tax look computable, almost addable. But the value side is not enumerable in the same way: complexity hidden, reuse enabled, a domain concept named, a contract enforced are qualitative goods that resist a common unit. So the "ratio" is really a comparison of a countable tax against an uncountable value, and its apparent quantitativeness is one-sided. The tension is that the diagnostic presents as a clean numeric threshold while one of its two terms is a judgment call, so the precision of the overhead figure can lend false objectivity to a verdict that still turns on an unquantified assessment of what the boundary is worth. Diagnostic: Is the value side being genuinely weighed (complexity hidden, concept named, contract enforced), or has the enumerable overhead been allowed to dominate simply because it is the only side that counts cleanly?
T5: Autonomy versus reduction (a code smell or an instance of abstraction-economy). "Lazy class" carries software-specific apparatus that makes it operational — class size, test surface, stack-trace layers, the inline-class/inline-function refactorings, SonarQube-style gates, the Fowler/Beck/Martin genealogy — and across software it transfers as mechanism at every construct level (class, function, module, package). Beyond software the underlying balance genuinely recurs — ceremonial offices, tautological statutory sections, curriculum units with too little content — but "lazy class" does not travel: the intervention vocabulary differs and domain dynamics (political constraint, statute-drafting economy, accreditation) dominate. What carries is the abstract balance, already held by abstraction (whose under-powered failure this is), parsimony, and minimalism. The tension is between a named smell whose refactoring economics earn its own study and an abstraction-overhead-must-earn-its-keep principle that belongs to those parents. Diagnostic: Resolve toward abstraction / parsimony / minimalism when the under-loaded unit is not a code construct; toward named lazy class when class size, test surface, and inline-refactoring economics are the operative currency.
Structural–Framed Character¶
Lazy class sits at mixed on the structural–framed spectrum, leaning framed — a near-twin of the lava flow anti-pattern in profile: a genuine structural core (an abstraction is warranted only when its carried value clears its overhead) wrapped in software-engineering practice, a "code smell / defect" evaluative charge, and a named-catalogue origin.
Evaluative weight is moderate. "Code smell" and "defect" are value-laden — the concept marks a boundary as one that should be removed — but the diagnostic itself is a neutral cost–value ratio, and the entry is careful that a lazy class "can be perfectly correct and clearly named and still be a defect" because the fault lives in the economy, not the behaviour. So a neutral balance underlies a normatively-tinted label.
Human-practice-bound points firmly framed. The smell exists only inside software-engineering practice: classes, refactorings, test surfaces, code review, linters. Nothing here runs observer-free; the concept is constituted by how developers build and maintain codebases, and its whole point is to guide a review-time decision (inline, merge, grow, keep).
Institutional origin is mixed: the named smell is Fowler's (Refactoring, 1999), part of the code-smell catalogue with a Fowler/Beck/Martin genealogy and its own tooling — clearly discipline furniture — but the balance it enforces (an abstraction must earn its overhead; do not multiply entities beyond necessity) is a general parsimony principle, not an institutional invention.
Vocab-travels is low: class, test surface, stack-trace layer, inline-class refactoring — software-bound. Import-vs-recognize is bimodal exactly as the entry maps: within software the ratio diagnosis transfers as recognition of the same mechanism across every construct level, while beyond software the balance still diagnoses (ceremonial offices, tautological statutes, thin curriculum units) but the named smell does not transport — those are the parent principle at work, not lazy class.
The portable structural skeleton is a boundary's overhead must be paid for by the value it carries; an under-loaded abstraction is net-negative — carried by abstraction (whose under-powered failure this is), parsimony/Occam's razor (do not multiply entities beyond necessity), and minimalism. As the entry establishes, that skeleton is what lazy class instantiates from those umbrella primes in a code-quality register, not what makes "lazy class" itself travel: the cross-domain reach belongs to abstraction/parsimony/minimalism, while the domain-accented apparatus — class size, test surface, inline-refactoring economics, the linter gates — stays home. Its character: a structurally real abstraction-economy balance dressed in software refactoring apparatus and a "code smell" label, structural in skeleton but pinned by practice-boundness, catalogue origin, and evaluative framing to mixed-leaning-framed rather than a free-floating prime.
Structural Core vs. Domain Accent¶
This is the section that decides why lazy class is a domain-specific abstraction and not a prime, and it carries the case for its domain-specificity in the same breath.
What is skeletal (could lift toward a cross-domain prime). Strip the software away and a thin, portable balance survives: a boundary imposes an overhead that must be paid for by the value it carries; an under-loaded boundary — one whose carried responsibility fails to clear its tax — is net-negative even when it is otherwise sound. The portable pieces are abstract: an overhead-versus-value ratio decoupled from correctness, a single responsibility axis with two poles (too little / too much) and a healthy middle band, and a hidden-currency exception (value paid in a currency the local view cannot see). That skeleton is genuinely substrate-portable, which is why the catalogue already carries it as parent primes — abstraction (the cognitive move of suppressing detail, whose under-powered failure this is), parsimony/Occam's razor (do not multiply entities beyond necessity), and minimalism (under-loaded structure as anti-pattern). But this is the core lazy class shares, not what makes it lazy class.
What is domain-bound. Almost everything that makes the diagnosis operational is software-engineering furniture that does not survive extraction. The overhead is enumerated in code-specific units — a file to navigate, an import, a stack-trace layer, an interface to keep stable, a test surface to maintain. The carried responsibility is code responsibility — complexity hidden behind a class, reuse across callers, a contract enforced against coupling. The construct being sized is a code construct (class, function, module, package); the hidden currency is a stable public API, plugin seam, or versioning boundary; the fix menu is Fowler's inline-class and inline-function refactorings, enforced by SonarQube-style linter gates, with a Fowler/Beck/Martin genealogy. The decisive test: remove the class/test-surface/refactoring economics and it is no longer lazy class but the bare parsimony balance — which is exactly what a ceremonial office, a tautological statute, or a thin curriculum unit is, diagnosed by the general principle with none of the software apparatus.
Why this does not clear the prime bar. A prime's vocabulary travels and its transfer is recognition of the same mechanism, not analogy. Lazy class's transfer is bimodal. Within software the ratio diagnosis travels intact as mechanism — the overhead-versus-value test, the two-pole responsibility axis (lazy class opposite god class), the decoupling from correctness, the inline/merge/grow/keep menu, and the hidden-currency exception — recurring as recognition across object-oriented, functional, and modular code and at every construct level, the class-level "lazy class" and function-level "inline function" being one diagnosis at two granularities. But these are variants of one substrate. Beyond software the underlying balance still diagnoses — redundant management layers, purely-delegating statutory sections, curriculum units with too little distinct content — but "lazy class" as named does not transport: the intervention vocabulary differs (consolidate departments, strike clauses, combine units) and domain-specific dynamics (political constraint, statute-drafting economy, accreditation rules) dominate rather than the clean overhead-versus-value calculation. And when the bare structural lesson is wanted cross-domain, it is already supplied, in more general form, by the parents — abstraction, parsimony, and minimalism — of which lazy class is the software code-smell instance. A reviewer who strikes a do-nothing committee is enacting the parsimony/abstraction-economy principle, not doing lazy class. The cross-domain reach belongs to those umbrella primes; "lazy class," as named, carries the class size, test surface, inline-refactoring economics, and linter gates as software-engineering baggage that stays home.
Relationships to Other Abstractions¶
Current abstraction Lazy Class Domain-specific
Parents (2) — more general patterns this builds on
-
Lazy Class is a kind of Code Smell Domain-specific
Lazy Class is the code-smell species whose low responsibility-to-overhead ratio suggests that an abstraction boundary has not earned its maintenance cost.It inherits a defeasible structural trace, contextual defeaters, predicted maintenance cost, and a refactoring probe. The child adds enumerable boundary overhead, carried responsibility, legitimate stable-seam or domain-name value, and Inline Class as the discriminating intervention.
-
Lazy Class is a decomposition of Economies of Scale Prime
Lazy class is the software-boundary case in which fixed abstraction overhead is spread over too little carried responsibility to reach an efficient scale.A class, function, module, or package imposes a fixed boundary cost—a name, navigation hop, interface, test surface, and change contract—while each responsibility it carries contributes the denominator over which that cost is amortized. The lazy-class verdict marks the low-scale region where the average boundary cost exceeds the value saved, and inlining removes the uneconomic unit.
Hierarchy paths (6) — routes to 6 parentless roots
- Lazy Class → Code Smell → Evidence → Provenance → Traceability → Observability
- Lazy Class → Economies of Scale → Increasing Returns
- Lazy Class → Economies of Scale → Scale
- Lazy Class → Code Smell → Evidence → Provenance → Attestation → Authentication
- Lazy Class → Code Smell → Evidence → Provenance → Traceability → Transformation → Function (Mapping)
- Lazy Class → Code Smell → Evidence → Provenance → Custody Transfer → State and State Transition → Phase Space
Not to Be Confused With¶
-
God class. The opposite pole of the same responsibility axis — an abstraction concentrating too much responsibility to remain manageable. Lazy class is the low-end failure (too little to earn its overhead); god class is the high-end one. Same balance failing in opposite directions, healthy abstraction in the middle band. Tell: is the class overloaded and doing too much (god class), or under-loaded and not paying for its boundary (lazy class)?
-
Speculative generality / premature abstraction. These name a common origin of a lazy class — a boundary introduced in anticipation of a variation that never arrived — describing why it was created. Lazy class names its present state: an abstraction that, whatever its history, now carries too little to justify its overhead. Tell: is the point about the misjudged decision to abstract for an imagined future (speculative generality), or about the current under-loaded boundary regardless of how it got there (lazy class)?
-
Lava flow anti-pattern. Unused, dead, or no-longer-understood code retained because removal is risky — dead code kept out of deletion-uncertainty. Lazy class is live, in-use code whose boundary simply does not pay for itself. Tell: is the code dead/orphaned and kept because proving it safe to remove is costly (lava flow), or actively used but under-loaded relative to its overhead (lazy class)?
-
Middle man (delegation smell). The Fowler smell of a class that delegates most of its work to another object, doing little itself but forwarding calls — its defect is excessive indirection through delegation. Lazy class is the broader economy defect (too little responsibility to justify overhead), diagnosed by the overhead-versus-value ratio rather than by delegation specifically; a forwarding wrapper is often both. Tell: is the complaint that the class merely relays to a collaborator (middle man), or that its carried responsibility — however implemented — fails to clear its tax (lazy class)?
-
The parent primes it instantiates (abstraction, parsimony, minimalism). The substrate-neutral balance — a boundary's overhead must be earned by the value it carries; an under-loaded boundary is net-negative — that lazy class instantiates for code constructs. Ceremonial offices, tautological statutes, and thin curriculum units are other instances. Tell: strip away class size, test surface, and inline-refactoring economics and what remains is bare abstraction-economy/parsimony, carried by these parents, not lazy class. (Treated fully in an earlier section.)
Neighborhood in Abstraction Space¶
Lazy Class sits in a crowded region of the domain-specific corpus (28th percentile for distinctiveness): several abstractions share nearly its structure, so a description that fits it tends to fit its neighbors too.
Family — Adversarial Exploits & Structural Boundaries (12 abstractions)
Nearest neighbors
- Cohesion (software / module-level) — 0.87
- Greenspun's Tenth Rule — 0.87
- Primitive Obsession — 0.86
- Long Parameter List — 0.86
- Inner-Platform Effect — 0.85
Computed from structural-signature embeddings · 2026-07-12