skip to content

The Common Closure Principle and the Common Reuse Principle pull component boundaries in opposite directions. Explain that tension and how you decide where to land.

level: seniorimportance: should knowfreq 32%

answer

  1. CCP inclusive vs CRP exclusive
  2. tension triangle REP-CCP-CRP
  3. abandon CCP -> too many components change
  4. abandon CRP -> too many unneeded releases
  5. early = developability/CCP; mature = reuse/CRP

basics

~20 s

CCP is inclusive - it pulls classes together so one change hits one component. CRP is exclusive - it splits out classes that consumers don't use, so nobody depends on code they don't need. You balance them, favouring CCP early and CRP once code is widely reused.

solid answer

~50 s

CCP, CRP, and REP form a tension triangle. CCP is inclusive: it makes components larger by pulling in everything that co-changes, optimising maintainability. CRP is exclusive: it makes components smaller by ejecting classes that aren't reused with the rest, so downstream consumers aren't forced to re-integrate, retest, and redeploy when an irrelevant class changes. REP (the granule of reuse is the granule of release) also pulls toward larger, well-versioned components. Each edge of the triangle names the cost of abandoning the opposite vertex: neglect CCP and too many components change per requirement; neglect CRP and consumers suffer too many unneeded releases; neglect REP and reuse becomes unmanageable. The resolution is contextual and temporal: in a young, internally-consumed codebase developability dominates, so weight CCP; as a library gains external consumers, reuse pressure grows and you split along CRP lines. Boundaries are re-cut as the project moves around the triangle.

go deeper

for a junior

It's enough to say CCP groups things together while CRP splits things apart, and that you cannot maximise both - a bigger component is easier to change but heavier for its users.

for a middle

Name all three cohesion principles, describe the inclusive/exclusive forces, and give a concrete example of a class that CCP would keep in and CRP would split out.

for a senior

Draw the tension triangle with the cost on each edge, argue the temporal migration from developability to reusability, and give tie-breakers (consumer count, co-change data, transitive dependency weight).

for a principal

Frame it as an economic decision about who absorbs cost - our change fan-out versus consumers' forced revalidation - and set an explicit, revisitable policy for when boundaries get re-cut, including the source-module vs published-artifact split.

## The three principles restated - **REP - Reuse/Release Equivalence Principle**: the granule of reuse is the granule of release. Classes reused together must be released and versioned together, with release notes and a stable version number, so consumers can depend on them coherently. - **CCP - Common Closure Principle**: gather into a component the classes that change for the same reasons at the same times. Minimises the number of components touched by one change. - **CRP - Common Reuse Principle**: don't force users of a component to depend on things they don't need. Classes that are not reused together should not be in the same component. CRP is the component-level analogue of the Interface Segregation Principle. ## The tension | Principle | Force | Effect on component size | Optimises | |---|---|---|---| | REP | inclusive (weak) | larger | reusability / versioning sanity | | CCP | inclusive | larger | maintainability, developability | | CRP | **exclusive** | smaller | reusability, consumer stability | CCP and CRP are directly antagonistic. Suppose class `PdfExporter` always changes together with the invoicing rules (CCP: keep it in `invoicing`), but half the consumers of `invoicing` only need the invoice model and never export PDFs (CRP: split `PdfExporter` out). Both readings are correct; they optimise different stakeholders. ## Why CRP's cost is real, not theoretical When a class you don't use is bundled into a component you depend on, you inherit: - **Its transitive dependencies** - the PDF library, its native bits, its CVEs, its licence. - **Its release churn** - every time it changes, the component gets a new version. Even if you never call it, a diligent consumer must re-integrate, re-run the test suite, re-certify, and redeploy. In regulated or heavily-verified environments that is expensive. - **Its build weight** - larger artifacts, longer install and cold-start times. ## Martin's tension diagram Draw a triangle with REP, CCP, CRP at the vertices. Each **edge** describes the price of ignoring the vertex opposite it: - Ignore **CCP** (over-split for reuse): too many components must change for a single requirement. - Ignore **CRP** (over-group): consumers get too many unneeded releases and unnecessary dependencies. - Ignore **REP** (split without coherent release/versioning): reuse becomes impractical - consumers can't tell what version of what works with what. A project sits **somewhere inside** the triangle, and the right position **moves over time**. ## The temporal rule of thumb Early in a project's life, no one is reusing your components; developability dominates, so bias hard toward CCP - fewer, feature-shaped components, easy to change. As the system matures and other teams or external users consume it, reuse pressure rises and the structure migrates toward REP/CRP - splitting out stable, narrowly-scoped, well-versioned components. Martin's point is that component structure is not designed once at the start; it *evolves*, and refusing to re-cut it is the actual mistake. ## Practical tie-breakers 1. **Who feels the pain?** If the pain is mostly ours (edits fan out, releases coordinate), weight CCP. If it is mostly the consumers' (they redeploy for changes irrelevant to them), weight CRP. 2. **Count the consumers.** One internal consumer -> CCP. Many independent consumers, especially external -> CRP. 3. **Look at the co-change data.** If VCS history shows the candidate class changes in the same commits as the rest of the component, splitting it will create a permanent two-component change - a real, measurable CCP cost. 4. **Look at the dependency weight.** If the candidate class drags in a heavy or risky transitive dependency, CRP wins - that is the classic 'optional feature in its own artifact' pattern (core + core-pdf, core + core-jackson). 5. **Compromise shape**: keep the co-changing code together in one *source* repo/module for developability, but publish two artifacts, so consumers get CRP-shaped dependencies while developers keep CCP-shaped editing. This is common in multi-module builds. ## Edge cases - **Cycles trump both.** If a split would create a dependency cycle between components, the Acyclic Dependencies Principle takes precedence; break the cycle first (dependency inversion or extracting a shared component). - **A 'common'/'util' component is usually a CRP win and a CCP loss** - it is depended on by everyone and changes for everyone's reasons. Keep it tiny and stable, or it becomes the component that forces the whole system to be rebuilt. - **Microservices are the extreme CRP position** - maximal splitting for independent deployability. If a business change routinely requires editing five services together, CCP has been sacrificed too far and you have a distributed monolith.

  • Can you satisfy CCP and CRP at the same time?
    Not perfectly - they are opposing forces, so you land at a balance point rather than satisfying both. A partial trick is separating the source-editing unit from the published artifact: keep co-changing code in one repo/module for CCP, but publish narrow artifacts so consumers get CRP-shaped dependencies.
  • What happens if you ignore REP while chasing CCP and CRP?
    You get components that are individually well-shaped but not coherently released or versioned. Consumers cannot tell which versions work together, changelogs mean nothing, and reuse becomes guesswork - the reason REP sits on the third vertex of the tension triangle.
  • How does the balance point change over a product's life?
    Early on, with no external consumers, developability dominates and you sit near CCP: fewer, feature-shaped components. As independent consumers appear, the cost of forcing them through irrelevant releases rises and you migrate toward REP/CRP by splitting stable, narrow components out.

Packing for a trip. One big suitcase (CCP) means everything you need for a day is in one place, but everyone who borrows it must carry the whole thing. Many small pouches (CRP) mean borrowers take only what they need, but your morning routine now means opening five pouches.

saying these in an interview costs you the question

  • Claiming a partition can satisfy CCP, CRP and REP simultaneously - they are explicitly in tension
  • Treating the balance point as fixed for the life of the project instead of migrating as consumers appear
  • Confusing CRP with CCP - CRP is about consumers not depending on unused code, not about change frequency
  • Justifying a giant 'common' or 'util' component with CCP, when it in fact changes for everyone's reasons and forces global rebuilds
  • Splitting a component so aggressively for reuse that a single feature change now spans several artifacts, and calling that 'good modularity'
  • Ignoring that a split can create a dependency cycle, which the Acyclic Dependencies Principle forbids regardless of cohesion arguments

context