Why this matters
The Reuse/Release Equivalence Principle (REP) states that the granule of reuse is the granule of release. You cannot reuse code that isn't tracked and released — someone must own it, version it, and provide a release note when it changes. Any class you want to reuse must belong to a component that is properly packaged and released.
The Common Closure Principle (CCP) is the component-level SRP: gather into a single component all the classes that change for the same reasons at the same times, and separate classes that change for different reasons into different components. The practical benefit is enormous — if a requirement change touches only one component, only that component needs to be recompiled, retested, and redeployed. A "utility" package that mixes unrelated concerns violates CCP: changing any one concern forces all consumers of the package to upgrade, even those that use none of the changed code.
The problem
A catch-all utilities package violates CCP — a change in PDF generation forces a new release that all dependents must adopt, even those that never touch PDF code.
Bad
# utils/ package — mixes HTTP helpers, date formatters, PDF and email
# http_helpers.py — HTTP request wrappers
# date_formatters.py — date parsing and display
# pdf_generator.py — PDF creation (heavy deps: reportlab, weasyprint)
# email_templates.py — HTML email builder
# PDF team updates pdf_generator.py → new "utils" version cut.
# Every service using only date_formatters must now upgrade, retest,
# and redeploy — even though nothing relevant to them changed.
// @company/utils — one package with unrelated concerns
// src/
// http.ts — axios wrappers
// dates.ts — date-fns helpers
// pdf.ts — puppeteer PDF export (heavy!)
// email.ts — Handlebars email builder
// A one-line change in pdf.ts bumps utils from 3.1.0 to 3.2.0.
// Every microservice importing only dates.ts must update their
// package.json, re-run CI, and redeploy. Unnecessary pain.
The solution
Each concern gets its own package with an independent release cadence. A change in pdf_utils is invisible to date_utils consumers.
Good
# http_utils/ v1.3.0 — HTTP helpers only
# wrappers.py
# date_utils/ v2.1.0 — date parsing and display only
# formatters.py
# pdf_utils/ v0.9.0 — PDF generation only
# generator.py
# email_utils/ v1.0.2 — email templates only
# templates.py
# A change in pdf_utils triggers only a pdf_utils release.
# date_utils users are unaffected — no forced upgrade, no forced redeploy.
// @company/http-utils v1.3.0 — axios wrappers
// @company/date-utils v2.1.0 — date-fns helpers
// @company/pdf-utils v0.9.0 — puppeteer PDF export
// @company/email-utils v1.0.2 — email templates
// A change in @company/pdf-utils only touches pdf consumers.
// Services using @company/date-utils see zero churn — no forced upgrades.
Key takeaway
If the classes in a component don't change for the same reason, they don't belong in the same component. CCP at the component level is SRP at the class level — separate what changes separately.