Explain the component cohesion "tension triangle" formed by REP, CCP and CRP: what does a project suffer if it sits at each edge, and how should its position move over the system's lifetime?
answer
- REP+CCP inclusive, CRP exclusive
- Edge REP–CCP → spurious releases for consumers
- Edge REP–CRP → one change, many components
- Edge CCP–CRP → nobody can reuse it safely
- Young project → CCP; mature platform → REP/CRP
basics
~20 sREP and CCP make components bigger; CRP makes them smaller, so you cannot satisfy all three. Ignore CRP and consumers get needless releases; ignore CCP and one change touches many components; ignore REP and the components are unusable by others.
solid answer
~60 sMartin draws the three cohesion principles as a triangle with a project's position somewhere inside it. Each **edge** is what you get by satisfying the two adjacent principles and sacrificing the third: - **REP + CCP, sacrificing CRP** — big, well-releasable, change-cohesive components. Cost: consumers depend on code they don't use, so they get **too many spurious releases and redeploys**. - **REP + CRP, sacrificing CCP** — coherent, minimal components for consumers. Cost: a single requirement change is **scattered across many components**, so release/deploy churn explodes for the maintainers. - **CCP + CRP, sacrificing REP** — beautifully factored internal structure with no release discipline. Cost: **reuse is unsafe/impossible** for anyone outside; there are no meaningful versions or release notes. The position is not fixed. Young projects prioritise **developability** over reusability, so CCP dominates and components are coarse; nobody outside consumes them yet. As the system matures and real external consumers appear, pressure shifts toward REP and CRP, and components get split and versioned properly. Expect to re-partition as the project ages; a partition that was right last year can be wrong now.
go deeper
Say the three principles pull against each other — two make components bigger, one makes them smaller — so you have to choose a balance.
Name each edge and its penalty: sacrificing CRP causes spurious consumer releases, sacrificing CCP scatters changes, sacrificing REP makes reuse unsafe.
Emphasise that the position moves with project maturity — CCP-dominant early, REP/CRP-dominant as external consumers appear — and give the signals you'd measure to relocate.
Turn it into policy: measurable indicators (components-per-change, irrelevant-release rate, consumer count, release cost), a governance cadence for re-partitioning, and how deployment topology (monorepo vs independently deployed services) reweights the corners.
## The triangle Put the three cohesion principles at the corners: ``` REP Reuse/Release Equivalence (group for reusers/releases) /\ / \ too many / \ hard to spurious / \ reuse releases / \ /__________\ CCP CRP Common Closure Common Reuse (group for maintainers) (split for consumers) \_______________/ too many components change for one requirement ``` - **REP** and **CCP** are **inclusive**: both argue for putting *more* into a component (a coherent release story; all the change-mates). - **CRP** is **exclusive**: it argues for taking things *out* (anything not co-used by consumers). Because of that, the three cannot be simultaneously maximised. A project occupies a **point inside** the triangle, and the edges name the pathology of abandoning the opposite corner. --- ## Edge by edge ### Edge REP–CCP (CRP sacrificed) Components are large: everything that changes together and forms a decent release story is inside. Consumers depend on far more than they use. **Symptoms:** frequent version bumps that are irrelevant to most consumers; consumers stuck on old versions to avoid churn; heavy transitive dependency trees; long consumer rebuild/redeploy cycles; upgrade fatigue. **When acceptable:** internal-only components; few consumers; monorepo with cheap rebuilds; early-stage products. ### Edge REP–CRP (CCP sacrificed) Components are cut to match consumer usage clusters and released cleanly, but they do not match change axes. **Symptoms:** a single feature request opens PRs in six repositories/artifacts; coordinated multi-artifact releases; lock-step version bumps; "which order do we deploy these in?" meetings; long lead time for small changes; high risk of partial deployment inconsistency. **When acceptable:** mature platforms/SDKs whose external consumers vastly outnumber and outweigh the maintenance friction; stable domains where change is rare. ### Edge CCP–CRP (REP sacrificed) Internally excellent partitioning, but no release discipline: no versions, no changelog, snapshot builds, everything built from HEAD. **Symptoms:** nobody outside the team can consume the code safely; "just take main" upgrades break consumers; no way to reason about compatibility; forks and copy-paste reuse appear. **When acceptable:** a single deployable application with no external consumers at all — a very common and often *correct* place to be. The failure appears the moment someone external wants to reuse a piece. --- ## Movement over time — the key senior insight Martin's point is that the right position **is a function of the project's maturity and audience**, and it moves: | Phase | Dominant concern | Position | |---|---|---| | Early / greenfield | **Developability** — ship features fast, nobody reuses you | near **CCP**; few coarse components; REP largely ignored | | Growth | Other internal teams start consuming | drift toward REP: real versions, release notes, compat policy | | Maturity / platform | Many independent consumers, external users | toward **REP–CRP**: split by usage clusters, strict versioning, deprecation policy | | Decline / stabilisation | Change rate falls | CCP pressure fades; consolidation may be fine again | The corollary: **component structure is not a one-time architecture decision.** It is a living partition that should be revisited as change and consumption profiles are measured. Expect to merge components when co-change data says they're really one, and split them when consumer usage data says they're really two. --- ## How to locate yourself deliberately Signals to gather: 1. **Components touched per change request** (high → CCP is being violated; move toward CCP). 2. **Consumer-irrelevant release rate**: fraction of a component's releases that changed nothing a given consumer uses (high → CRP violated; split). 3. **Number and independence of consumers**: many independent consumers raises REP/CRP weight. 4. **Cost of a release** (validation, certification, coordination): expensive releases raise CCP's weight sharply. 5. **Deployment coupling**: if artifacts always deploy together, CRP's benefit is smaller — the version-skew cost mostly disappears (this is why monorepo/single-deployable systems can happily sit near CCP). --- ## Common mistakes - Trying to "comply with all three" — it is a trade-off space, not a checklist. - Copying the position of a famous open-source library (which is a mature, multi-consumer platform) into a two-month-old internal product. - Confusing this triangle with the **coupling** principles (ADP, SDP, SAP), which have their own tension between stability and abstractness. - Never re-measuring: the seams calcify and the organisation grows workarounds instead.
- Your team is three months into a greenfield internal product. Where in the triangle should you sit, and why?Near the CCP corner. There are no external consumers, so REP's release discipline and CRP's consumer isolation buy almost nothing, while developability is everything. Use a few coarse, feature-aligned components, keep the release process light, and revisit once other teams start consuming you.
- How would you measure that a component's boundary is now wrong?Two measurements. First, components-touched-per-change from VCS history: a rising number means CCP violation — merge or re-slice. Second, per-consumer irrelevant-release rate: how often a consumer must upgrade for a change it doesn't use — a high rate means CRP violation and argues for splitting along usage clusters.
- Is a monorepo an escape from the triangle?No, but it changes the weights. Building everything from source removes version-skew and much of CRP's cost, so sitting near CCP is cheaper. What remains is build/test invalidation blast radius, code ownership, and the moment you publish anything externally — at which point REP re-enters in full.
Kitchen storage: one big 'baking' drawer (CCP/REP) means every borrowed item disturbs everyone; twenty labelled boxes (CRP) means fetching one recipe's ingredients opens twenty lids. Households reorganise as they grow — and so should component boundaries.
saying these in an interview costs you the question
- Claiming REP, CCP and CRP can all be fully satisfied with good design
- Treating the component partition as a permanent, up-front decision
- Assuming smaller components are always better architecture
- Applying a mature open-source library's fine-grained packaging to a young internal system
- Mixing in ADP/SDP/SAP as part of the cohesion triangle