skip to content

Reuse/Release Equivalence Principle

The granule of reuse is the granule of release: whatever you expect people to reuse must be something you can version and ship as a unit. You will learn why a component needs a coherent theme and a real release process before anyone can depend on it.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

5

What does the Reuse/Release Equivalence Principle (REP) state, and what does it require of a reusable component?

level: juniorimportance: must knowfreq 38%

answer

  1. granule of reuse = granule of release
  2. can't reuse what you can't release
  3. version + changelog + published artifact
  4. coherent theme, shipped together
  5. copy-paste / source-path reuse = violation

basics

~20 s

REP says the granule of reuse is the granule of release: the unit you reuse must be the unit you release. A component has to be published as a versioned whole, with a version number and release notes, so consumers depend on a specific version instead of copying files.

solid answer

~50 s

REP (Reuse/Release Equivalence Principle) is one of the three component-cohesion principles: "the granule of reuse is the granule of release." It answers "which classes belong in one component?" from the consumer's side — you cannot reuse anything you cannot release, so a component must be a releasable unit: it has a single name and boundary, a version number, a published artifact, and release notes describing what changed. Everything inside it is versioned and released together, which implies the contents share a coherent theme and purpose — a consumer who wants one class should sensibly want the rest. Practically, REP is what lets a consumer say "I depend on payments-client 2.4.1", upgrade deliberately, read the changelog, and stay on an old version while others move. Copy-pasted source, a shared folder on a build path, or an unversioned grab-bag component all violate it.

code

yaml · 8 lines
yaml
# REP-compliant: consumer names a bounded, versioned, published unit
dependencies:
  - payments-client: "2.4.1"   # immutable version, changelog exists

# REP violations:
#   sourcePaths: ["../../teamB/src/payments"]  # reuse without release
#   - payments-client: "latest"                # no reproducible granule
#   - common-utils: "9.3.0"                    # no theme: everyone bumps for everything

go deeper

for a junior

State the slogan and unpack it: the thing you reuse must be the thing you release — a named, versioned, published artifact with release notes, not copied files.

for a middle

Add the consumer mechanics: pinning versions, reproducible builds, changelog-driven upgrade decisions, semver signalling, and the fact that shared version = shared churn, so contents need a coherent theme.

for a senior

Frame REP as one of three cohesion principles and note its size pressure in both directions; give concrete violations (copy-paste, source-path builds, latest, unversioned common-utils) and how you'd remediate incrementally.

for a principal

Talk about it as an organisational contract: release process, ownership, deprecation and support windows, registry/tooling investment, and the deliberate exceptions (monorepo single-version policy, code not meant for cross-boundary reuse).

## The vocabulary first - **Component**: the smallest unit of *deployment/distribution* in your ecosystem — a jar, a DLL, an npm package, a Python wheel, a Go module, a shared library. Not a class, not a folder: a thing that can be shipped independently of the program that uses it. - **Release**: publishing that component under a name and an immutable version (`payments-client 2.4.1`) into somewhere consumers can fetch it from (a package registry, an artifact repository). - **Granule (granularity) of reuse**: the size of the chunk another team actually takes and depends on. - **REP**: *the granule of reuse is the granule of release.* If a group of classes is to be reused together, it must be released together — and conversely, whatever you release together is what you're telling people to reuse together. REP comes from Robert C. Martin's component principles: three **cohesion** principles (REP, CCP — Common Closure, CRP — Common Reuse) that answer "which classes go in which component?", and three **coupling** principles (ADP, SDP, SAP) that answer "how may components depend on each other?". ## Why the principle exists Reuse is not a technical act, it's a *relationship*. A consumer reusing your code needs to know: 1. **What exactly am I depending on?** — a named, bounded artifact, not "those five files Dave wrote". 2. **Which snapshot of it?** — a version, so my build is reproducible and my colleague's machine resolves the same bytes. 3. **What changed, and does it break me?** — release notes / a changelog. 4. **Can I stay put?** — old versions remain available, so I upgrade on *my* schedule, not the author's. None of that is possible unless the reusable chunk is also the *released* chunk. If you reuse code by copying source files, or by pointing a build at another team's source folder, you get: no version, no changelog, no way to pin, silent breakage the moment the author edits a file, and N drifting copies of the "same" code. REP formalises the fix: **package it, version it, publish it, announce it.** ## What a REP-compliant component looks like - One published artifact with a stable coordinate/name. - An **immutable** version per release (re-publishing different bytes under the same version destroys the guarantee). - A version *scheme* consumers can reason about — commonly **semantic versioning**: MAJOR for breaking changes, MINOR for backward-compatible additions, PATCH for backward-compatible fixes. - Release notes / changelog for each version. - A **coherent theme**: the classes inside should share a reason to exist, because they now share a release cadence and a version number. If half the component is HTTP retry helpers and half is date formatting, every date-format fix forces an HTTP-consumer version bump for no benefit. - A defined release *process* (who can cut a release, how it's tested, where it's published, how it's deprecated). ## The consequence people miss: everything shares one version number A version number is a *promise about the whole artifact*. Two facts follow: - **Churn is shared.** A busy class in the component drags every quiet class along: consumers who only use the quiet part still see a stream of new versions and must decide whether to upgrade. - **Breakage is shared.** A MAJOR bump anywhere is a MAJOR bump everywhere; consumers of untouched classes still pay migration attention. So REP has a size pressure in *both* directions. Too small (one class per component) and you get dependency-management overhead, huge dependency graphs, and coordinated multi-release changes for one logical feature. Too large (a company-wide `common-utils`) and unrelated consumers are yoked to each other's release noise — the classic REP smell. ## Common violations | Violation | Why it breaks REP | |---|---| | Copy-pasting source between projects | No version, no changelog, forks drift silently | | Build reads another team's source directory directly | You reuse a non-released granule; any edit is instantly live | | Unversioned "latest"/snapshot dependencies | No reproducible build, no deliberate upgrade | | A `utils`/`common` bag with no theme | Released together but not reused together; pure release noise | | Publishing without release notes | Consumers can't judge risk of upgrading | | Mutating an already-published version | The version stops identifying a specific state | ## Edge cases and nuance - **Internal-only code still counts.** "We're all one company" is not a substitute for versioning; it just means your registry is private. - **Monorepos can satisfy REP** — but only if the shared code is still built as a bounded, named, versioned artifact (or the repo enforces atomic single-version consumption across all consumers, which is a different, deliberate strategy). A monorepo does not exempt you from having a release *boundary*. - **Not all code should be a component.** REP applies to code intended for reuse *across* release boundaries. Code used only inside one deployable doesn't need its own version. - **REP is about the release relationship, not the packaging tool.** A jar with no changelog and mutable versions is no more REP-compliant than a copied folder.

  • If REP pushes toward publishing everything reusable, why not make every class its own versioned component?
    Because release has real cost: each component needs a build, a version, a changelog, a release decision, and a node in every consumer's dependency graph. Splitting too far means one logical change requires several coordinated releases in dependency order, and consumers face huge graphs and version-conflict (diamond) problems. REP sets a floor on the release relationship, not a mandate for maximum fragmentation — the other cohesion principles (CCP, CRP) supply the counter-pressures that pick the actual size.
  • A team says "we do reuse properly — we publish a jar", but the jar is republished under the same version whenever they fix something. Is that REP-compliant?
    No. REP needs the released granule to *identify* a specific state of the code. A mutable version means two consumers building on different days get different bytes under the same coordinate, builds aren't reproducible, and a changelog can't be attached meaningfully. Immutable versions plus release notes are part of the release process REP presumes.
  • Does REP say anything about what goes *inside* a component?
    Yes, indirectly but strongly. Because everything inside shares one version and one release cadence, the contents must share a coherent theme and audience — otherwise consumers are forced through version churn and major-version migrations caused by code they never call. That pressure is what makes catch-all `utils` components a REP smell, and it's the bridge to the Common Reuse Principle.

A reusable component is like a published edition of a book. You cite "3rd edition, 2021" — bounded, dated, unchangeable, with a preface listing what changed. Reusing code without a release is like citing "the manuscript on the author's desk": it changes under you, two readers never see the same text, and you can't say which version you read.

saying these in an interview costs you the question

  • "REP just means put related classes in the same package/folder" — it's about a released, versioned artifact, not directory layout.
  • "It's internal code, so we don't need versions" — internal consumers need pinning and changelogs just as much.
  • "We reuse it, so we satisfy REP" — reuse by copying source or pointing at a source path is exactly what REP forbids.
  • "REP means make components as small as possible" — REP constrains the release relationship; size is settled against CCP and CRP.
  • "Publishing a jar is enough" — without an immutable version, a changelog, and a release process, it's a file drop.
  • "Semantic versioning is REP" — semver is one common way to communicate a release; REP is the underlying requirement that a release exists at all.

context

open as a page

Your team publishes a shared library called `common-utils` that contains HTTP retry helpers, date formatting, CSV parsing, and a feature-flag client, all versioned together. Judged against the Reuse/Release Equivalence Principle (REP), what is wrong and what would you change?

level: middleimportance: must knowfreq 34%

basics

~20 s

The component has no coherent theme: nobody reuses all four things together, yet everyone shares one version. Any change forces unrelated consumers to see new versions and possibly breaking bumps. Split it into themed, separately versioned components with their own release notes.

open as a page

Does a monorepo where every service is built and deployed from HEAD violate the Reuse/Release Equivalence Principle (REP)? How should REP be applied when producers and consumers share one repository?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Not necessarily. REP demands that reuse happen through a defined, identifiable release. In a monorepo built at HEAD, the commit itself is the release: one version for everything, atomic changes, no pinning. It satisfies REP's intent only if boundaries, ownership and change communication still exist.

open as a page

How does the Reuse/Release Equivalence Principle (REP) interact with the Common Closure Principle (CCP) and the Common Reuse Principle (CRP), and what goes wrong when a team over-applies REP?

level: seniorimportance: should knowfreq 26%

basics

~20 s

REP, CCP and CRP pull in different directions. REP and CCP push components larger (releasable, coherent, co-changing); CRP pushes smaller (don't force consumers to depend on things they don't use). You pick a spot on that triangle that suits the project's current stage, then revisit it.

open as a page

You are defining the release strategy for an organisation's internal shared components. What policies and mechanisms make the Reuse/Release Equivalence Principle (REP) actually hold at scale, and when would you deliberately not release a reusable piece of code?

level: principalimportance: nice to knowfreq 14%

basics

~20 s

Give each component an owner, a charter, an immutable versioned artifact in a registry, a semver policy over a declared public API, per-release notes, and a deprecation/support window. Skip releasing code that has no cross-boundary consumer or is still churning heavily.

open as a page