Skip to content

Chapter 3: Modularity

Introduction

As frontend applications scale, structural decay often appears long before visible bugs. Components become harder to change, teams slow down, and small changes ripple unexpectedly across the codebase. This chapter frames modularity as the primary structural defense against that decay—less as an abstract ideal, more as an ongoing architectural effort that requires attention, energy, and trade-off awareness.


Modularity as an Organizing Principle

“95% of the words written about software architecture are spent extolling the benefits of modularity, and little, if anything, is said about how to achieve it.”

-- Glenford J. Myers

Modularity is not a feature of a system—it is an organizing principle.

In physical systems, energy must be continuously applied to maintain order. Software systems behave similarly. Without deliberate effort, structural clarity degrades over time. Modularity does not emerge accidentally; it must be maintained.

From an architectural perspective, modularity refers to breaking a system into smaller, related groupings of responsibility.

In frontend systems, these groupings might be:

  • Component clusters
  • Feature folders
  • Hooks and utilities
  • State slices
  • Pages and layouts

Modularity vs Granularity

Modularity describes whether a system is broken into parts.
Granularity describes how large those parts are.

Moving from a monolithic architecture (for example, a traditional layered system) toward a more distributed style (such as microservices or micro-frontends) increases modularity. However, the size of each module—their granularity—introduces its own trade-offs.

“Embrace modularity, but beware of granularity.”

-- Mark Richards

In frontend terms:

  • Too coarse: large feature folders that become mini-monoliths
  • Too fine: excessive component fragmentation that increases coordination cost

The tension is structural, not stylistic.


What a Module Means in Practice

A module is a logical grouping of related code.

Depending on language and paradigm, this may look different:

  • Classes in object-oriented systems
  • Functions in functional systems
  • Files and folders in frontend codebases

Throughout architectural discussions, module is used as a general term for related units of behavior and data—not a specific language construct.

In React-based systems, modules often emerge implicitly through:

  • Folder structure
  • Import boundaries
  • State ownership
  • Rendering responsibilities

Measuring Modularity

Modularity can be discussed qualitatively, but architecture also offers analytical lenses for evaluation.

Cohesion

Cohesion measures how strongly related the elements inside a module are.

“A cohesive module should not be divided; doing so increases coupling and reduces readability.”

-- Larry Constantine

High cohesion implies:

  • Clear responsibility
  • Minimal unrelated logic
  • Predictable change impact

Low cohesion often signals:

  • Mixed concerns
  • Accidental growth
  • Future refactoring pressure

Common Types of Cohesion (from strongest to weakest)

  • Functional cohesion
    All parts contribute to a single responsibility.
  • Sequential cohesion
    Output of one part becomes input to another.
  • Communicational cohesion
    Parts operate on shared data.
  • Procedural cohesion
    Parts must execute in a specific order.
  • Temporal cohesion
    Parts are related by timing (e.g., initialization).
  • Logical cohesion
    Grouped by category, not behavior.
  • Coincidental cohesion
    Unrelated elements grouped together.

Frontend systems often drift toward temporal or logical cohesion as they grow.


Coupling and Stability

Coupling measures how dependent modules are on one another.

Two commonly discussed forms:

  • Afferent coupling – who depends on this module
  • Efferent coupling – who this module depends on

High coupling increases fragility. Changes propagate farther and break more easily.

This leads to metrics such as:

  • Instability – likelihood a module will change
  • Abstractness – proportion of abstract vs concrete elements

These values form the well-known Zones of Pain and Uselessness diagram.


Connascence: A Deeper View of Coupling

Two components are connascent if a change in one requires a change in another to maintain correctness.

Connascence shifts the conversation from dependency count to dependency strength.

Static Connascence (compile-time)

  • Name – agreement on identifiers
  • Type – agreement on data types
  • Meaning (Convention) – agreement on value semantics
  • Position – agreement on parameter order
  • Algorithm – agreement on implementation logic

Dynamic Connascence (runtime)

  • Execution – order of execution matters
  • Timing – time sensitivity between components
  • Values – values must change together
  • Identity – shared reference to the same entity

From an architectural perspective, weaker forms are preferable—especially across module boundaries.


Locality, Distance, and Refactoring

Two guiding observations help improve modularity over time:

  • Rule of Degree
    Strong forms of connascence can often be refactored into weaker forms.
  • Rule of Locality
    As distance between components increases, coupling should become weaker.

In modern terms, this aligns closely with bounded contexts from Domain-Driven Design: keep high coupling local, and reduce assumptions across boundaries.

Frontend systems naturally grow. Without attention, small issues scale disproportionately.


Modularity as an Ongoing Effort

Improving modularity generally involves:

  • Minimizing overall connascence
  • Containing strong coupling within narrow boundaries
  • Maximizing internal cohesion

These are not one-time activities. They are continuous structural adjustments as systems evolve.

Modularity is not about perfection—it is about maintaining changeability.


Closing Perspective

Modularity is less about decomposition and more about containment.

It reflects how well a system:

  • Absorbs change
  • Limits ripple effects
  • Preserves clarity over time

In frontend architecture, modularity determines whether a codebase remains a product—or slowly becomes an obstacle.