skip to content

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