skip to content

The Common Closure Principle (CCP) says to gather into one component the classes that change for the same reasons at the same times. What concrete cost does it reduce, how do you decide the grouping in practice, and how does it relate to the Single Responsibility and Open/Closed principles?

level: middleimportance: must knowfreq 58%

answer

  1. CCP = SRP at component scale
  2. Cost is per-component, not per-line
  3. Group by feature/stakeholder, not by layer
  4. Co-change history = the empirical test
  5. OCP: closure you can't achieve, at least gather

basics

~20 s

CCP keeps code that changes together in one releasable unit, so a typical requirement change touches one component instead of many — one thing to build, test, version and deploy. It is the Single Responsibility Principle applied to components.

solid answer

~60 s

CCP reduces **change cost**: build, test, revalidate, re-version, release and redeploy work scales with the number of components a change touches, so grouping change-mates minimises that. It is SRP at component scale — a component should have one reason to change, where "reason" means one class of stakeholder or requirement. In practice you group by **reason to change**, which usually means by feature/domain capability, and by **who requests changes**, not by technical layer or class kind. A `billing` component that holds its rules, its persistence mapping and its API contract satisfies CCP better than `all-entities` + `all-repositories` + `all-controllers`, because a billing-rule change hits one artifact instead of three. Good empirical evidence: version-control co-change. If files always change in the same commit, they belong together; if a component's files never co-change, it's really two components. CCP also serves the Open/Closed Principle: you can't be closed against every change, so you choose the changes to be closed against and put the classes *not* closed against the same change into the same component, containing the leakage.

code

text · 9 lines
text
Layered packaging (poor CCP)          Feature packaging (good CCP)
  entities/                             billing/
    Invoice, Product, User                Invoice, InvoiceRepo, InvoiceApi, rules
  repositories/                         catalog/
    InvoiceRepo, ProductRepo, UserRepo    Product, ProductRepo, ProductApi
  api/                                  identity/
    InvoiceApi, ProductApi, UserApi       User, UserRepo, UserApi

"Change VAT rounding" touches 3 components   ->   touches 1 component

go deeper

for a junior

Say it means classes that change for the same reason live in the same package/module, so one change means one thing to rebuild and ship.

for a middle

Add the SRP mapping and the concrete cost model — per-component build/test/version/release/deploy work — and contrast feature packaging with layer packaging.

for a senior

Bring in evidence (co-change/logical coupling from VCS), volatility separation, the OCP 'gathered closure' framing, and the explicit conflict with CRP.

for a principal

Discuss it as an organisational and economic lever: component seams that match stakeholder change axes and team ownership, release cadence effects, and a plan for re-partitioning as the observed change profile drifts.

## Statement > **Common Closure Principle (CCP):** *Gather into components those classes that change for the same reasons and at the same times. Separate those classes that change for different reasons and at different times.* A **component** here is a unit that is independently built, versioned, released and deployed (a library/package/module/service artifact). --- ## The cost CCP attacks Software maintenance dominates cost, and most maintenance is **change**. The dangerous variable is not "how many lines did I edit?" but **how many independently released artifacts did the edit touch?** Each touched component typically means: 1. rebuild it, 2. re-run its test suite, 3. bump its version and write release notes, 4. publish/deploy it, 5. update the version constraints of everything downstream, 6. re-run integration/verification for the combined set, 7. coordinate with whichever team owns it. Steps 3–7 are the expensive ones and they are per-component, not per-line. So a change that spans five components can cost an order of magnitude more than the same change confined to one — and the risk of a version-skew or partial-deploy bug grows with the count. CCP therefore optimises for **developability/maintainability**, which is why it usually dominates early- and mid-life systems where reuse by third parties is not yet the priority. --- ## How to actually decide the grouping **"Same reason to change" ≈ "same requesting stakeholder / same axis of requirement".** Useful heuristics: - **Group by capability, not by technical kind.** Slicing by layer (`entities`, `repositories`, `controllers`, `dtos`) guarantees that *every* feature change crosses *every* component — the worst possible CCP score. Slicing vertically (`billing`, `catalog`, `identity`) means a billing change stays inside `billing`. This is the "package by feature, not by layer" argument in component clothing. - **Ask who asks for the change.** Pricing rules change when Finance asks; report layouts change when Ops asks; those are different reasons, hence different components — even if both touch "invoices". - **Mine the history.** Compute co-change / logical coupling: for each pair of files, how often do they appear in the same commit or PR? Strong pairs that live in different components indicate a bad seam; a component whose files never co-change indicates it should split. This is one of the few *measurable* architecture signals available. - **Look at the release log.** If component X has been re-released 40 times this quarter purely because component Y changed, the seam between them is wrong. - **Beware volatility mixing.** Putting a highly volatile class next to a rock-stable one drags the stable one into every release. CCP says separate them: "same *times*" is part of the statement, not decoration. --- ## Relationship to SRP SRP (class level): *a class should have one reason to change* — one actor/stakeholder it answers to. CCP (component level): *a component should have one reason to change.* Same idea, different granularity. If you understand SRP's "reason = actor" reading, CCP is the direct lift: the component answers to one family of stakeholders. ## Relationship to OCP The Open/Closed Principle wants modules to be **closed** against modification when requirements change (you extend rather than edit). But you can never be closed against *all* changes — closure is always *strategic*: you predict the likely axes of change and design closure against those. CCP is the component-level completion of that thought: for the changes you could **not** close against, at least make sure all the code that must change sits inside **one** component boundary. So CCP is "closure gathered": the change may break the abstraction, but it won't break the *release topology*. --- ## Trade-offs and edge cases - **CCP fights CRP.** Pulling change-mates in makes components bigger; CRP wants unused code out. A component that satisfies CCP perfectly may force consumers to accept classes they never call, triggering needless rebuilds for them. You choose a position in the triangle. - **CCP fights REP** only mildly: a change-cohesive component is usually also a sensible release story, but not always — code that changes together for internal reasons may still be an incoherent public offering. - **Prediction risk.** CCP relies on forecasting *future* change axes. Forecasts are wrong, so treat the partition as revisable; expect to move classes between components as the real change history accumulates. - **Organisational alignment.** Because change requests arrive through people, CCP-cohesive components tend to line up with team ownership (a Conway's-law effect). If two teams must both edit one component for every feature, either the component or the team boundary is wrong. - **Multiple valid answers.** There is no unique correct partition; there is a partition that is cheap given *your* observed change traffic.

  • If CCP says group change-mates together, why not put the whole system in one component — nothing would ever cross a boundary?
    Because CRP and REP pull the other way: a single giant component forces every consumer to depend on everything, makes every change re-release the whole system to everyone, and destroys independent deployability and parallel team work. CCP is one force among three, not an objective to maximise.
  • You inherit a system packaged strictly by layer. What evidence would you gather before repackaging?
    Co-change analysis from version control (which files appear together in commits/PRs), the distribution of components touched per change request, release frequency per component, and which team authored each change. If most changes fan out across all layers and are authored by one team, a vertical repartition is justified.
  • How does CCP interact with team topology?
    Change requests come from stakeholders through teams, so components with one reason to change tend to have one owning team. If a component needs two teams for every change, either merge the responsibility or split the component; otherwise every release becomes a coordination event.

A hospital could organise wards by equipment type (all beds in one ward, all monitors in another) or by condition (cardiology, maternity). The equipment split means every patient's care crosses every ward. CCP is the condition split: whatever changes for one patient tends to happen in one place.

saying these in an interview costs you the question

  • Saying CCP means 'keep the codebase in one big module'
  • Interpreting 'changes together' as 'is called together' — that's CRP, not CCP
  • Defending layer-based packaging as CCP-compliant
  • Claiming CCP is about compile time or performance rather than change/release cost
  • Treating the component partition as a permanent up-front decision instead of something revised from observed change history

context