Component principles skill

Components are the units of deployment -- the smallest entities that can be independently deployed.

by wondelai·MIT license·★ 2,235 Stars on the repo·GitHub ↗

Use now

Files of Component principles

wondelai/main1 file
component-principles.md
Show the full text248 lines

Component Principles

Components are the units of deployment -- the smallest entities that can be independently deployed. In Java they are jar files, in Ruby they are gems, in .NET they are DLLs, in JavaScript they are npm packages or bundled modules. Robert C. Martin defines six principles that govern how classes should be grouped into components (cohesion) and how components should relate to each other (coupling).

This reference covers the three cohesion principles (REP, CCP, CRP), the three coupling principles (ADP, SDP, SAP), practical application of stability and abstractness metrics, and strategies for managing component dependencies.

Component Cohesion: What Goes Inside a Component

REP: The Reuse/Release Equivalence Principle

"The granule of reuse is the granule of release."

Classes and modules that are grouped into a component should be releasable together. If you version and release a component, every class in it should make sense as part of that release. A component should have a cohesive theme -- a reason for being grouped.

Why it matters:

  • Users of a component expect that when they upgrade to a new version, all classes in the component have been updated coherently
  • If a component contains unrelated classes, users are forced to upgrade for changes they don't care about
  • A component without a coherent theme is difficult to document, understand, and maintain

Practical implications:

  • A component named order-domain should contain Order, OrderItem, OrderStatus, OrderPolicy -- all cohesively related to order business rules
  • It should NOT also contain UserPreferences or EmailTemplate just because they happen to be used nearby
  • When you can't write a one-sentence description of what the component does, it probably violates REP
CCP: The Common Closure Principle

"Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons."

This is the Single Responsibility Principle applied at the component level. A component should not have multiple reasons to change.

Why it matters:

  • When a change in business requirements affects multiple classes, ideally all those classes are in the same component
  • This means only one component needs to be redeployed rather than many
  • Minimizes the ripple effect of changes across the deployment landscape

Practical application:

Change Reason Group Together Separate From
Order pricing rules change OrderCalculator, DiscountPolicy, TaxCalculator OrderController, OrderRepository
Database schema changes OrderMapper, OrderRepository, OrderMigration Order, OrderCalculator
API response format changes OrderPresenter, OrderSerializer, OrderViewModel Order, OrderService
Authentication rules change AuthPolicy, TokenValidator, SessionManager OrderService, PaymentService

The key question: "When this business rule changes, which classes will I need to modify?" Group those classes together.

CRP: The Common Reuse Principle

"Don't force users of a component to depend on things they don't need."

Classes in a component should be tightly related. If you depend on one class in a component, you should depend on most (ideally all) classes in that component. If you only use one class out of twenty, the component is too broad.

Why it matters:

  • When a component changes, all components that depend on it must be revalidated and potentially redeployed
  • If Component A depends on Component B but only uses one class, changes to unrelated classes in B still force A to be revalidated
  • Fat components create unnecessary coupling

Practical test: For each class in a component, ask: "If I remove this class, would users of this component notice?" If they wouldn't, the class may belong elsewhere.

The Tension Triangle

REP, CCP, and CRP are in tension with each other:

         REP
        /    \
      /        \
    CCP ------- CRP
  • REP + CCP push toward larger components (group things that are released and changed together)
  • CRP pushes toward smaller components (don't include things that aren't used together)
  • Early in development: Favor CCP (minimize redeployment cost as code churns)
  • As the system matures: Shift toward CRP (minimize unnecessary coupling as the system stabilizes)

A component's composition typically evolves over time, starting broad (CCP-oriented) and narrowing (CRP-oriented) as the system matures.

Component Coupling: Relationships Between Components

ADP: The Acyclic Dependencies Principle

"Allow no cycles in the component dependency graph."

The dependency graph of components must be a Directed Acyclic Graph (DAG). If Component A depends on B, and B depends on C, and C depends on A, you have a cycle -- and the three components are effectively one undivisible monolith.

Why cycles are destructive:

  • Cycles make independent release impossible -- you can't release A without releasing B and C
  • Changes in any component in the cycle potentially affect all others
  • Build order becomes ambiguous or impossible
  • Testing requires all components in the cycle to be present

Detecting cycles:

A --> B --> C --> A    (CYCLE: A, B, C are effectively one component)

A --> B --> C          (NO CYCLE: DAG)
      |         ^
      v         |
      D --------/

Breaking cycles -- two strategies:

Strategy 1: Apply the Dependency Inversion Principle

If B depends on A and A depends on B (cycle), extract an interface:

Before (cycle):
  A <--> B

After (no cycle):
  A --> InterfaceX <-- B

A defines InterfaceX. B implements it. Now A depends on nothing new; B depends on InterfaceX (which lives with A). The cycle is broken.

Strategy 2: Extract a new component

Move the shared dependency into a new component that both A and B depend on:

Before (cycle):
  A <--> B

After (no cycle):
  A --> C <-- B

C contains the shared classes. Both A and B depend on C. Neither depends on the other.

SDP: The Stable Dependencies Principle

"Depend in the direction of stability."

A component should only depend on components that are more stable than it is. Stability here means "difficulty of change" -- a component is stable if many other components depend on it (making it hard to change without breaking things).

Measuring stability:

  • Fan-in (Ca): Number of classes outside the component that depend on classes inside the component (incoming dependencies)
  • Fan-out (Ce): Number of classes inside the component that depend on classes outside the component (outgoing dependencies)
  • Instability (I): I = Ce / (Ca + Ce), where I ranges from 0 (maximally stable) to 1 (maximally unstable)
Metric Value Meaning Implication
I = 0 Maximally stable (many dependents, no dependencies) Hard to change; should be abstract
I = 1 Maximally unstable (no dependents, many dependencies) Easy to change; should be concrete
I = 0.5 Balanced Moderate change risk

The SDP rule: If Component A depends on Component B, then I(B) should be less than or equal to I(A). You should depend on things that are harder to change than you are.

Violation example: If a highly stable component (I=0.1, many dependents) depends on a highly unstable component (I=0.9, few dependents), the unstable component's frequent changes will destabilize all the stable component's dependents.

SAP: The Stable Abstractions Principle

"A component should be as abstract as it is stable."

Stable components (hard to change) should be abstract (contain mostly interfaces and abstract classes). This way, their stability does not prevent them from being extended. Unstable components (easy to change) should be concrete, containing the implementations that change frequently.

Measuring abstractness:

  • Nc: Number of classes in the component
  • Na: Number of abstract classes and interfaces in the component
  • Abstractness (A): A = Na / Nc, where A ranges from 0 (fully concrete) to 1 (fully abstract)
The Main Sequence

Plot each component on a graph with Instability (I) on the x-axis and Abstractness (A) on the y-axis. The ideal line runs from (0,1) to (1,0) -- the "Main Sequence."

A (Abstractness)
1 |  * Zone of Uselessness
  |    \
  |      \  <-- Main Sequence
  |        \
  |          \
0 |____________* Zone of Pain
  0          1
    I (Instability)

Zone of Pain (I=0, A=0): Maximally stable AND maximally concrete. Very hard to change but contains no abstractions for extension. Examples: database schemas, concrete utility libraries that everyone depends on. Painful to modify.

Zone of Uselessness (I=1, A=1): Maximally unstable AND maximally abstract. No one depends on these abstract interfaces. They serve no purpose. Dead code.

The Main Sequence: Components should fall near the line from (0,1) to (1,0). Stable components should be abstract. Unstable components should be concrete.

Distance from the Main Sequence: D = |A + I - 1|, where D ranges from 0 (on the line) to ~0.7 (in a zone).

Components with high D values warrant investigation.

Practical Component Design

Component Mapping to Clean Architecture
Clean Architecture Circle Stability Abstractness Character
Entities Very stable (I near 0) Abstract (interfaces, domain types) Core business rules; many dependents
Use Cases Stable (I = 0.2-0.4) Moderately abstract (ports, interactors) Application rules; depend on entities
Adapters Unstable (I = 0.5-0.7) Concrete (controllers, gateways) Translation layer; depend on use cases
Frameworks Very unstable (I near 1) Concrete (configuration, wiring) Glue code; depend on everything
Versioning Components Independently

When components are properly decoupled:

  • Each can have its own version number
  • Each can be released on its own schedule
  • Teams can own components independently
  • Breaking changes in one component can be managed through interface versioning
Component Design Workflow
  1. Start with CCP: Group classes by reason for change. Don't worry about component size.
  2. Apply CRP: Remove classes that aren't used together. Split components that force unnecessary dependencies.
  3. Check REP: Ensure each component has a coherent theme and can be meaningfully versioned.
  4. Graph dependencies: Draw the component dependency graph. Look for cycles.
  5. Break cycles (ADP): Use DIP or extraction to eliminate every cycle.
  6. Check stability direction (SDP): Ensure dependencies flow toward stability.
  7. Balance abstraction (SAP): Make stable components abstract; make unstable components concrete.
  8. Measure D: Plot components on the I/A graph. Investigate those far from the Main Sequence.
Common Component Anti-Patterns
Anti-Pattern Symptom Fix
God component One component contains everything Split by CCP: group by reason for change
Circular dependencies Can't release or build independently Apply ADP: DIP or extraction
Concrete stable component Many dependents, no abstractions, painful to change Apply SAP: extract interfaces, move implementations to unstable components
Unstable abstractions Abstract component with no dependents Remove dead abstractions or rethink dependency structure
Shotgun releases Changing one feature requires releasing five components Apply CCP: group co-changing classes together
Dependency magnet One utility component everyone depends on Split into focused components; apply CRP
Dependency Analysis Tools
Language Tool Purpose
Java JDepend, ArchUnit Measure component metrics, enforce dependency rules
JavaScript/TypeScript Dependency Cruiser, Madge Visualize and validate module dependencies
Python import-linter, pydeps Enforce import rules, visualize package dependencies
.NET NDepend Component metrics, dependency analysis
Go go vet, custom linters Package dependency validation
General SonarQube Cross-language dependency and quality analysis

These tools automate the detection of cycles, stability violations, and components in the Zone of Pain or Zone of Uselessness. Integrate them into CI/CD pipelines to prevent architectural drift.

1# Component Principles
2 
3Components are the units of deployment -- the smallest entities that can be independently deployed. In Java they are jar files, in Ruby they are gems, in .NET they are DLLs, in JavaScript they are npm packages or bundled modules. Robert C. Martin defines six principles that govern how classes should be grouped into components (cohesion) and how components should relate to each other (coupling).
4 
5This reference covers the three cohesion principles (REP, CCP, CRP), the three coupling principles (ADP, SDP, SAP), practical application of stability and abstractness metrics, and strategies for managing component dependencies.
6 
7## Component Cohesion: What Goes Inside a Component
8 
9### REP: The Reuse/Release Equivalence Principle
10 
11**"The granule of reuse is the granule of release."**
12 
13Classes and modules that are grouped into a component should be releasable together. If you version and release a component, every class in it should make sense as part of that release. A component should have a cohesive theme -- a reason for being grouped.
14 
15**Why it matters:**
16- Users of a component expect that when they upgrade to a new version, all classes in the component have been updated coherently
17- If a component contains unrelated classes, users are forced to upgrade for changes they don't care about
18- A component without a coherent theme is difficult to document, understand, and maintain
19 
20**Practical implications:**
21- A component named `order-domain` should contain `Order`, `OrderItem`, `OrderStatus`, `OrderPolicy` -- all cohesively related to order business rules
22- It should NOT also contain `UserPreferences` or `EmailTemplate` just because they happen to be used nearby
23- When you can't write a one-sentence description of what the component does, it probably violates REP
24 
25### CCP: The Common Closure Principle
26 
27**"Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons."**
28 
29This is the Single Responsibility Principle applied at the component level. A component should not have multiple reasons to change.
30 
31**Why it matters:**
32- When a change in business requirements affects multiple classes, ideally all those classes are in the same component
33- This means only one component needs to be redeployed rather than many
34- Minimizes the ripple effect of changes across the deployment landscape
35 
36**Practical application:**
37 
38| Change Reason | Group Together | Separate From |
39|---------------|---------------|---------------|
40| Order pricing rules change | `OrderCalculator`, `DiscountPolicy`, `TaxCalculator` | `OrderController`, `OrderRepository` |
41| Database schema changes | `OrderMapper`, `OrderRepository`, `OrderMigration` | `Order`, `OrderCalculator` |
42| API response format changes | `OrderPresenter`, `OrderSerializer`, `OrderViewModel` | `Order`, `OrderService` |
43| Authentication rules change | `AuthPolicy`, `TokenValidator`, `SessionManager` | `OrderService`, `PaymentService` |
44 
45**The key question:** "When this business rule changes, which classes will I need to modify?" Group those classes together.
46 
47### CRP: The Common Reuse Principle
48 
49**"Don't force users of a component to depend on things they don't need."**
50 
51Classes in a component should be tightly related. If you depend on one class in a component, you should depend on most (ideally all) classes in that component. If you only use one class out of twenty, the component is too broad.
52 
53**Why it matters:**
54- When a component changes, all components that depend on it must be revalidated and potentially redeployed
55- If Component A depends on Component B but only uses one class, changes to unrelated classes in B still force A to be revalidated
56- Fat components create unnecessary coupling
57 
58**Practical test:** For each class in a component, ask: "If I remove this class, would users of this component notice?" If they wouldn't, the class may belong elsewhere.
59 
60### The Tension Triangle
61 
62REP, CCP, and CRP are in tension with each other:
63 
64```
65 REP
66 / \
67 / \
68 CCP ------- CRP
69```
70 
71- **REP + CCP** push toward larger components (group things that are released and changed together)
72- **CRP** pushes toward smaller components (don't include things that aren't used together)
73- **Early in development:** Favor CCP (minimize redeployment cost as code churns)
74- **As the system matures:** Shift toward CRP (minimize unnecessary coupling as the system stabilizes)
75 
76A component's composition typically evolves over time, starting broad (CCP-oriented) and narrowing (CRP-oriented) as the system matures.
77 
78## Component Coupling: Relationships Between Components
79 
80### ADP: The Acyclic Dependencies Principle
81 
82**"Allow no cycles in the component dependency graph."**
83 
84The dependency graph of components must be a Directed Acyclic Graph (DAG). If Component A depends on B, and B depends on C, and C depends on A, you have a cycle -- and the three components are effectively one undivisible monolith.
85 
86**Why cycles are destructive:**
87- Cycles make independent release impossible -- you can't release A without releasing B and C
88- Changes in any component in the cycle potentially affect all others
89- Build order becomes ambiguous or impossible
90- Testing requires all components in the cycle to be present
91 
92**Detecting cycles:**
93 
94```
95A --> B --> C --> A (CYCLE: A, B, C are effectively one component)
96 
97A --> B --> C (NO CYCLE: DAG)
98 | ^
99 v |
100 D --------/
101```
102 
103**Breaking cycles -- two strategies:**
104 
105**Strategy 1: Apply the Dependency Inversion Principle**
106 
107If B depends on A and A depends on B (cycle), extract an interface:
108 
109```
110Before (cycle):
111 A <--> B
112 
113After (no cycle):
114 A --> InterfaceX <-- B
115```
116 
117A defines `InterfaceX`. B implements it. Now A depends on nothing new; B depends on `InterfaceX` (which lives with A). The cycle is broken.
118 
119**Strategy 2: Extract a new component**
120 
121Move the shared dependency into a new component that both A and B depend on:
122 
123```
124Before (cycle):
125 A <--> B
126 
127After (no cycle):
128 A --> C <-- B
129```
130 
131C contains the shared classes. Both A and B depend on C. Neither depends on the other.
132 
133### SDP: The Stable Dependencies Principle
134 
135**"Depend in the direction of stability."**
136 
137A component should only depend on components that are more stable than it is. Stability here means "difficulty of change" -- a component is stable if many other components depend on it (making it hard to change without breaking things).
138 
139**Measuring stability:**
140 
141- **Fan-in (Ca):** Number of classes outside the component that depend on classes inside the component (incoming dependencies)
142- **Fan-out (Ce):** Number of classes inside the component that depend on classes outside the component (outgoing dependencies)
143- **Instability (I):** I = Ce / (Ca + Ce), where I ranges from 0 (maximally stable) to 1 (maximally unstable)
144 
145| Metric Value | Meaning | Implication |
146|-------------|---------|-------------|
147| I = 0 | Maximally stable (many dependents, no dependencies) | Hard to change; should be abstract |
148| I = 1 | Maximally unstable (no dependents, many dependencies) | Easy to change; should be concrete |
149| I = 0.5 | Balanced | Moderate change risk |
150 
151**The SDP rule:** If Component A depends on Component B, then I(B) should be less than or equal to I(A). You should depend on things that are harder to change than you are.
152 
153**Violation example:**
154If a highly stable component (I=0.1, many dependents) depends on a highly unstable component (I=0.9, few dependents), the unstable component's frequent changes will destabilize all the stable component's dependents.
155 
156### SAP: The Stable Abstractions Principle
157 
158**"A component should be as abstract as it is stable."**
159 
160Stable components (hard to change) should be abstract (contain mostly interfaces and abstract classes). This way, their stability does not prevent them from being extended. Unstable components (easy to change) should be concrete, containing the implementations that change frequently.
161 
162**Measuring abstractness:**
163 
164- **Nc:** Number of classes in the component
165- **Na:** Number of abstract classes and interfaces in the component
166- **Abstractness (A):** A = Na / Nc, where A ranges from 0 (fully concrete) to 1 (fully abstract)
167 
168### The Main Sequence
169 
170Plot each component on a graph with Instability (I) on the x-axis and Abstractness (A) on the y-axis. The ideal line runs from (0,1) to (1,0) -- the "Main Sequence."
171 
172```
173A (Abstractness)
1741 | * Zone of Uselessness
175 | \
176 | \ <-- Main Sequence
177 | \
178 | \
1790 |____________* Zone of Pain
180 0 1
181 I (Instability)
182```
183 
184**Zone of Pain (I=0, A=0):** Maximally stable AND maximally concrete. Very hard to change but contains no abstractions for extension. Examples: database schemas, concrete utility libraries that everyone depends on. Painful to modify.
185 
186**Zone of Uselessness (I=1, A=1):** Maximally unstable AND maximally abstract. No one depends on these abstract interfaces. They serve no purpose. Dead code.
187 
188**The Main Sequence:** Components should fall near the line from (0,1) to (1,0). Stable components should be abstract. Unstable components should be concrete.
189 
190**Distance from the Main Sequence:**
191D = |A + I - 1|, where D ranges from 0 (on the line) to ~0.7 (in a zone).
192 
193Components with high D values warrant investigation.
194 
195## Practical Component Design
196 
197### Component Mapping to Clean Architecture
198 
199| Clean Architecture Circle | Stability | Abstractness | Character |
200|--------------------------|-----------|-------------|-----------|
201| **Entities** | Very stable (I near 0) | Abstract (interfaces, domain types) | Core business rules; many dependents |
202| **Use Cases** | Stable (I = 0.2-0.4) | Moderately abstract (ports, interactors) | Application rules; depend on entities |
203| **Adapters** | Unstable (I = 0.5-0.7) | Concrete (controllers, gateways) | Translation layer; depend on use cases |
204| **Frameworks** | Very unstable (I near 1) | Concrete (configuration, wiring) | Glue code; depend on everything |
205 
206### Versioning Components Independently
207 
208When components are properly decoupled:
209- Each can have its own version number
210- Each can be released on its own schedule
211- Teams can own components independently
212- Breaking changes in one component can be managed through interface versioning
213 
214### Component Design Workflow
215 
2161. **Start with CCP:** Group classes by reason for change. Don't worry about component size.
2172. **Apply CRP:** Remove classes that aren't used together. Split components that force unnecessary dependencies.
2183. **Check REP:** Ensure each component has a coherent theme and can be meaningfully versioned.
2194. **Graph dependencies:** Draw the component dependency graph. Look for cycles.
2205. **Break cycles (ADP):** Use DIP or extraction to eliminate every cycle.
2216. **Check stability direction (SDP):** Ensure dependencies flow toward stability.
2227. **Balance abstraction (SAP):** Make stable components abstract; make unstable components concrete.
2238. **Measure D:** Plot components on the I/A graph. Investigate those far from the Main Sequence.
224 
225### Common Component Anti-Patterns
226 
227| Anti-Pattern | Symptom | Fix |
228|-------------|---------|-----|
229| **God component** | One component contains everything | Split by CCP: group by reason for change |
230| **Circular dependencies** | Can't release or build independently | Apply ADP: DIP or extraction |
231| **Concrete stable component** | Many dependents, no abstractions, painful to change | Apply SAP: extract interfaces, move implementations to unstable components |
232| **Unstable abstractions** | Abstract component with no dependents | Remove dead abstractions or rethink dependency structure |
233| **Shotgun releases** | Changing one feature requires releasing five components | Apply CCP: group co-changing classes together |
234| **Dependency magnet** | One utility component everyone depends on | Split into focused components; apply CRP |
235 
236### Dependency Analysis Tools
237 
238| Language | Tool | Purpose |
239|----------|------|---------|
240| Java | JDepend, ArchUnit | Measure component metrics, enforce dependency rules |
241| JavaScript/TypeScript | Dependency Cruiser, Madge | Visualize and validate module dependencies |
242| Python | import-linter, pydeps | Enforce import rules, visualize package dependencies |
243| .NET | NDepend | Component metrics, dependency analysis |
244| Go | `go vet`, custom linters | Package dependency validation |
245| General | SonarQube | Cross-language dependency and quality analysis |
246 
247These tools automate the detection of cycles, stability violations, and components in the Zone of Pain or Zone of Uselessness. Integrate them into CI/CD pipelines to prevent architectural drift.
248 

Discussion

Alternatives

shadcn/uiManages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI, including chat interfaces. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".Coding · MITWeb artifacts builderSuite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.Coding · Apache-2.0Create STYLE_GUIDE.mdThis prompt assists users in creating a comprehensive STYLE_GUIDE.md for their projects. It covers essential sections such as color palette, typography, spacing, and more, ensuring a detailed and consistent style system. Users can also include example component design references.Coding · CC0-1.0Frontend developer skillThis prompt is designed for an elite frontend development specialist. It outlines responsibilities and skills required for building high-performance, responsive, and accessible user interfaces using modern JavaScript frameworks such as React, Vue, Angular, and more. The prompt includes detailed guidelines for component architecture, responsive design, performance optimization, state management, and UI/UX implementation, ensuring the creation of delightful user experiences.Coding · CC0-1.0