Note: This blog post is based on my personal notes but rewritten and restructured by AI.
Chapter 3: Modularity
Chapter 3 explores the concept of modularity — the art and science of organizing code into logical, maintainable, and scalable units. It dives into definitions, metrics, and guidelines for designing modular software, using both classic and modern analysis techniques.
What Is Modularity?
Modularity is the logical grouping of related code. While platforms offer packaging mechanisms (e.g., packages in Java, namespaces in .NET), modularity refers to how we conceptually organize code to improve clarity and maintainability.
Example: A Java package like com.mycompany.customer would logically contain all customer-related code.
Why It’s Important
- It’s a foundational architectural trait — even if it’s not explicitly mentioned in requirements
- Without it, systems become chaotic and hard to maintain
Key Takeaways
- Modularity is logical, not always physical
- It’s critical for reusability, clarity, and scalability
How to Use It
- Use packages, folders, and namespaces to group related code logically
- Design modules around domain functionality or bounded contexts
Measuring Modularity
To understand and improve modularity, we use three main concepts:
1. Cohesion
Cohesion = How closely related the internal parts of a module are.
- High cohesion → Everything in the module works together
- Low cohesion → Unrelated things are grouped together (bad)
Why It’s Important
High cohesion means modules are easier to understand, maintain, and reuse.
Key Takeaways
- Aim for functional cohesion (the best form)
- Avoid coincidental cohesion (the worst)
How to Use It
- Use tools that calculate LCOM (Lack of Cohesion in Methods) to spot problem areas
- Refactor modules/classes with unrelated responsibilities
2. Coupling
Coupling = The degree to which a module depends on others.
- Afferent coupling (Ca) = Incoming connections (how many other modules use this one)
- Efferent coupling (Ce) = Outgoing connections (how many modules this one uses)
Why It’s Important
- High coupling increases fragility and change risk
- Low coupling improves independence and testability
Key Takeaways
- Lower is better for most coupling
- Be cautious with highly coupled modules
How to Use It
- Use tools to identify modules with high Ce or Ca
- Refactor to reduce unnecessary dependencies
3. Abstractness, Instability & Distance from Main Sequence
These metrics help evaluate modular balance in object-oriented systems.
Abstractness (A)
Ratio of abstract elements (interfaces, abstract classes) to total.
Formula: A = #abstract / (#abstract + #concrete)
Instability (I)
Formula: I = Ce / (Ce + Ca)
Indicates volatility — how easily a module may break due to changes.
Distance from Main Sequence (D)
Measures deviation from the ideal balance.
Formula: D = |A + I - 1|
- Near 0 = balanced
- High = problematic (too abstract or too concrete)
Why It’s Important
- Helps spot fragile or useless modules
- Guides toward structural balance
Key Takeaways
- Aim for low D (close to the “main sequence”)
- Use visual diagrams to identify modules in the zone of pain or zone of uselessness
How to Use It
- Use static analysis tools to compute A, I, D
- Refactor modules to move closer to ideal balance
Connascence: A Deeper Look at Coupling
Connascence = If changing one component forces another to change to maintain correctness.
Static Connascence (Code-Level, Compile-Time)
| Type | Description | Example |
|---|---|---|
| CoN (Name) | Shared method/variable names | getCustomer() |
| CoT (Type) | Shared data types | int age vs String age |
| CoM (Meaning) | Shared conventions or magic values | int TRUE = 1 |
| CoP (Position) | Parameter order dependencies | update(String name, ID) |
| CoA (Algorithm) | Shared logic/algorithms | Shared hashing logic |
| CoI (Identity) | Shared references or resources | Shared distributed queue |
Dynamic Connascence (Runtime-Level)
| Type | Description | Example |
|---|---|---|
| CoE (Execution) | Order of operations matters | send() before setSubject() |
| CoT (Timing) | Concurrent operations cause race conditions | Multithreading errors |
| CoV (Values) | Multiple values must be changed together | DB transactions |
| CoI (Identity) | Shared runtime identity | Shared cache or singleton |
Connascence Properties
| Property | Meaning |
|---|---|
| Strength | How hard is it to refactor? Prefer weaker types. |
| Locality | Is the connascence local (same module) or remote? |
| Degree | How many components are affected by the dependency? |
Why It’s Important
- Connascence is actionable — it tells you how coupling happens
- Helps guide refactoring efforts with precision
Key Takeaways
- Prefer static over dynamic connascence
- Convert strong connascence to weaker forms
- Use locality: Strong dependencies are okay when close, not when far apart
How to Use It
- During refactoring, reduce CoM to CoN (e.g., use constants)
- Apply Weirich’s rules:
- Rule of Degree: Convert strong to weak
- Rule of Locality: Use weaker forms as distance increases
Unifying Coupling and Connascence
This section shows how connascence refines traditional coupling.
- Traditional coupling = “What is connected”
- Connascence = “How it’s connected”
Why It’s Important
- Offers a richer vocabulary and tools for evaluating dependencies
- Bridges classic structured programming with modern design practices
How to Use It
- Use coupling for high-level analysis
- Use connascence for precise refactoring strategies
From Modules to Components
- Module = Logical unit of related code
- Component = Physical deployment unit
- A component implements one or more modules
- Components are the primary architectural building blocks
Why It’s Important
Understanding this distinction is crucial for deployment, scalability, and integration.
How to Use It
- Package modules into components thoughtfully (e.g., JARs, DLLs, microservices)
- Design components to be independently deployable and loosely coupled
Final Summary
Modularity is a critical architectural principle that ensures code remains manageable as systems grow. Chapter 3 outlines how to measure and improve modularity using cohesion, coupling, and connascence. These tools help architects identify structural issues and refactor toward healthier systems. Understanding the distinction between modules (logical) and components (physical) is key for deploying scalable software.
Most Useful Insights
✅ Modularity = logical grouping, not necessarily physical separation
✅ Cohesion: Higher = better
✅ Coupling: Lower = better
✅ Use LCOM to measure internal cohesion
✅ Use Ca / Ce to analyze dependency structure
✅ Use Abstractness, Instability, D for balance metrics
✅ Connascence helps understand and refactor coupling
✅ Prefer static, weak, local connascence
✅ Use components as the main deployable units
✅ Trade-offs are inevitable — use metrics to choose wisely
