Package Principles are six design principles formulated by Robert C. Martin (Uncle Bob) that guide how to organize classes into packages (components) and manage dependencies between them. Three principles address package cohesion (what goes inside packages), and three address package coupling (relationships between packages).
Overview
| Cohesion Principles | Coupling Principles |
|---|---|
| REP: Reuse-Release Equivalence | ADP: Acyclic Dependencies |
| CRP: Common Reuse | SDP: Stable Dependencies |
| CCP: Common Closure | SAP: Stable Abstractions |
In this context, a “package” is a binary deliverable like a .jar, .dll, or npm package—a deployable unit of code, not just a namespace or folder.
Package Cohesion Principles
These principles determine what classes belong together in a package.
REP: Reuse-Release Equivalence Principle
“The granule of reuse is the granule of release.”
Meaning: Anything that is reused must be released and tracked with version numbers.
Key Points:
- Reusable components require formal release tracking and versioning
- Users must be able to depend on specific versions
- The package author must provide notification of changes, support, and documentation
- Classes grouped together should make sense from a reuser’s perspective
Implications:
- If classes are reused together, they should be released together
- Random collections of unrelated classes don’t make good packages
- Packages should have clear, cohesive purposes that users can understand
CRP: Common Reuse Principle
“The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.”
Meaning: Group classes that tend to be reused together; don’t force users to depend on things they don’t use.
Key Points:
- Classes with tight interdependencies belong in the same package
- Container classes and their iterators should be together
- Don’t include classes that aren’t typically used with the others
Example:
// GOOD: Related classes together
package collections {
class ArrayList
class ArrayListIterator
class LinkedList
class LinkedListIterator
}
// BAD: Unrelated classes forced together
package utils {
class EmailValidator
class ArrayList
class DateFormatter
}
Implication: CRP tells us which classes should NOT be together—those that aren’t reused together.
CCP: Common Closure Principle
“The classes in a component should be closed together against the same kinds of changes. A change that affects a component affects all the classes in that component and no other components.”
Meaning: Group classes that change for the same reasons and at the same times.
Key Points:
- This is the Single Responsibility Principle applied to packages
- If a change is needed, it should ideally affect only one package
- Reduces release overhead—fewer packages need redeployment
- Classes that change together belong together
Example:
// GOOD: Payment-related changes affect one package
package payment {
class PaymentProcessor
class PaymentValidator
class PaymentReceipt
}
// BAD: Payment logic scattered across packages
package validation { PaymentValidator }
package processing { PaymentProcessor }
package receipts { PaymentReceipt }
The Tension Diagram
These three principles create competing forces:
REP
/ \
/ \
/ \
CCP ───── CRP
- REP and CCP are inclusive: They make packages larger by grouping classes
- CRP is exclusive: It makes packages smaller by removing unrelated classes
The Tension:
- Maximizing CRP and CCP leads to many small packages
- REP adds overhead for managing many releases
- Early in development, favor CCP (developability over reuse)
- As a system matures and is reused, shift toward REP and CRP
Uncle Bob advises focusing on CCP in early development because developability is more important than reuse at that stage.
Package Coupling Principles
These principles govern dependencies between packages.
ADP: Acyclic Dependencies Principle
“Allow no cycles in the component dependency graph.”
Meaning: The dependency structure must be a Directed Acyclic Graph (DAG)—no circular dependencies.
The Problem with Cycles:
// BAD: Circular dependency
Package A → Package B → Package C → Package A
When packages form cycles:
- Changes in one package ripple through the entire cycle
- Testing requires all packages in the cycle
- Independent deployment becomes impossible
- Build order becomes undefined
Breaking Cycles:
- Introduce an abstraction: Create a new package with an interface that both depend on
// Instead of A ↔ B
// Create: A → IService ← B
-
Use Dependency Inversion: Make the lower-level module depend on an abstraction owned by the higher-level module
-
Event-driven communication: Replace direct calls with events or messages
SDP: Stable Dependencies Principle
“Depend in the direction of stability.”
Meaning: A package should only depend on packages that are more stable than itself.
Measuring Stability:
- Fan-in (Ca - Afferent Coupling): Incoming dependencies—how many packages depend on this one
- Fan-out (Ce - Efferent Coupling): Outgoing dependencies—how many packages this one depends on
Instability Metric:
I = Ce / (Ca + Ce)
I = 0: Maximally stable (many depend on it, hard to change)
I = 1: Maximally unstable (nothing depends on it, easy to change)
Key Insight: Stable packages should never depend on unstable ones. Dependencies should point toward stability.
Example:
// GOOD: Unstable depends on stable
UI Layer (I=1) → Business Logic (I=0.3) → Core Domain (I=0)
// BAD: Stable depends on unstable
Core Domain (I=0) → UI Utilities (I=1)
Practical Application:
- UI frameworks (volatile) should depend on core business logic (stable)
- Core domain should have few or no outgoing dependencies
- Adapters and plugins should be unstable; what they adapt to should be stable
SAP: Stable Abstractions Principle
“A component should be as abstract as it is stable.”
Meaning: Stable packages should be abstract so they can be extended. Unstable packages should be concrete so they can be easily modified.
The Problem:
- A stable, concrete package becomes rigid—hard to extend without modification
- It violates the Closed Principle
Abstractness Metric:
A = Na / Nc
Na = Number of abstract classes/interfaces in package
Nc = Total number of classes in package
A = 0: Completely concrete
A = 1: Completely abstract
The Main Sequence:
Plot packages on an I vs A graph:
A (Abstractness)
1 | Zone of
| Uselessness *
| *
0.5 | * <- Main Sequence (ideal)
| *
| *
0 +--*-----------------
0 0.5 1 I (Instability)
- Zone of Pain (I=0, A=0): Stable and concrete—rigid, hard to change
- Zone of Uselessness (I=1, A=1): Unstable and abstract—no one implements it
- Main Sequence: The ideal diagonal line where I + A ≈ 1
Distance from Main Sequence:
D = |A + I - 1|
D = 0: On the main sequence (ideal)
D = 1: As far from ideal as possible
Example:
// GOOD: Stable package is abstract
namespace Core.Abstractions // I≈0, A≈1
{
public interface INotificationService { }
public interface IPaymentGateway { }
}
// Unstable packages are concrete
namespace Infrastructure.Email // I≈1, A≈0
{
public class SmtpNotificationService : INotificationService { }
}How the Principles Work Together
Dependency Direction
The healthiest architecture flows from unstable → stable → abstract:
┌─────────────────┐
│ UI, Adapters │ Unstable, Concrete (I≈1, A≈0)
│ (outer layers) │
└────────┬────────┘
│ depends on
▼
┌─────────────────┐
│ Application │ Middle stability
│ Services │
└────────┬────────┘
│ depends on
▼
┌─────────────────┐
│ Core Domain │ Stable, Abstract (I≈0, A≈1)
│ (abstractions) │
└─────────────────┘
Combined Effect
| Principle | Purpose | Action |
|---|---|---|
| ADP | Eliminate cycles | Introduce abstractions or event-driven communication |
| SDP | Direct dependency flow | Depend on stable (high fan-in, low fan-out) packages |
| SAP | Maintain flexibility | Stable packages must be abstract, not concrete |
SAP + SDP = Dependency Inversion Principle (DIP) at the package level:
- Stable packages are abstract (SAP)
- Unstable packages depend on stable ones (SDP)
- Result: Concrete details depend on abstractions
Practical Application
When Designing Packages
- Identify what changes together (CCP)
- Consider what’s reused together (CRP)
- Version packages that are reused (REP)
- Draw the dependency graph—eliminate cycles (ADP)
- Ensure dependencies point toward stability (SDP)
- Make stable packages abstract (SAP)
Warning Signs
- Cyclic dependencies: Packages that depend on each other
- Stable concrete packages: Core packages with no abstractions
- Unstable packages with many dependents: Volatile code that many packages rely on
- Releases that cascade: Changing one package forces many others to update
Resources
- Principles of OOD - Uncle Bob
- Package principles - Wikipedia
- Agile Software Development: Principles, Patterns, and Practices - Robert C. Martin
See Also
- Coupling - Class-level coupling concepts
- Cohesion - Class-level cohesion concepts
- SOLID Principles - Class-level design principles
- Dependency Inversion Principle - The class-level equivalent
- Object-Oriented Design
- Design Concepts
- Good Software Practices