Modularity, Coupling, and Cohesion

Expected level: Working

Modularity means building a system from parts (modules) with clear boundaries. Coupling describes how much the parts depend on each other, and cohesion describes how closely the contents of one part belong together.

Modules and interfaces

A module can be a function, class, package, library or service. Each has a public interface (what others may use) and hidden internals (what it may change freely). Callers depend on the interface only. Keep the interface small and the internals private.

Cohesion: what is inside

A module is cohesive when everything in it serves one purpose. An InvoiceCalculator that calculates invoices is cohesive. A Helpers class with date formatting, file upload and email sending is not. Low cohesion makes a module hard to name, hard to find things in and likely to change for many reasons.

Coupling: what is between

Coupling is the degree to which one module needs to know about another. Tight coupling shows up as:

  • Reaching into another module’s internals or database tables.
  • Sharing global or mutable state (see State Management).
  • Assuming another module’s exact behaviour or call order.
  • Two parts that must always be changed and deployed together.

Looser coupling comes from depending on small interfaces or abstractions, passing in what a module needs instead of creating it inside (dependency injection), and communicating by messages or events instead of direct calls.

Aiming for high cohesion and low coupling

That is a direction, and it has limits:

  • Some coupling is unavoidable, since modules must cooperate.
  • Extra abstraction costs complexity. Do not add interfaces and layers for changes that will never come.
  • Splitting into separately deployed services makes coupling look lower, but it moves the problem to network calls and versioned contracts (see Distributed Systems Basics). Many teams start with a well-structured single application and split later.
  • Duplicating a little code can be better than coupling two modules that merely look alike.

Signs of trouble

A single change touching many files, a class that nobody dares edit, circular dependencies between packages, and tests that need half the system set up all point to high coupling or low cohesion. Fixing them is regular refactoring, and neglecting them builds technical debt.

Try it

Choose a module or class in your codebase and say in one sentence what it is responsible for. If you need “and” more than once, its cohesion is probably low. Then list the other modules it imports and decide which dependencies could be replaced by a narrower interface or a parameter.

Common mistakes

  • Reaching into another module's internals or database tables.
  • Adding interfaces and layers for changes that will never come.
  • Dumping unrelated code into a Helpers or utils class.
  • Treating a split into services as lower coupling when it only moves it to network calls.

Check yourself

  • How can you tell a module has low cohesion?
  • Name two signs of tight coupling.
  • Why does splitting into services not remove coupling?

Related topics

Terms

Further reading