Software Design

[Modularization #1] What Is Modularization? Why Split Code? (Cohesion & Coupling)

As projects grow, changing one file becomes increasingly intimidating. It is hard to tell how far the impact will spread.

4 min read
Cover image for [Modularization #1] What Is Modularization? Why Split Code? (Cohesion & Coupling)

As projects grow, changing one file becomes increasingly intimidating. It is hard to tell how far the impact will spread.

Builds slow down and code reviews grow wider. New team members struggle to know where to start reading.

Modularization is the oldest remedy for this problem. In short, it means dividing a large block of code into units that can be understood and replaced independently.

This article explains what modularization actually divides, how to judge modules by cohesion and coupling, and when to split them.

Here is the key summary first.

  1. Modularization groups things that change together and establishes boundaries.
  2. Good modules have high cohesion and low coupling.
  3. The first goal is to reduce change costs, not improve build speed.
  4. Splitting before boundaries are clear only increases complexity.

What Exactly Does Modularization Divide?

Organizing folders is different from splitting modules.

Folders only organize files. Code in any folder can freely use code from another folder.

Modules add enforceable boundaries. Outside a module, you can use only the interfaces it makes public.

Modularization is not file organization; it makes the compiler enforce what outsiders need to know and what they do not.

These boundaries enable two things.

  • Independent understanding: use a module by looking only at its interface, without knowing its internals.
  • Independent replacement: if the interface stays the same, the implementation can be replaced without affecting the outside.

This is David Parnas’s information hiding concept from a 1972 paper. Modules should be split by the design decisions you want to hide, not by features.


Criteria for Good Modules: Cohesion and Coupling

If splitting modules made things harder, these two metrics are usually misaligned.

Cohesion describes how closely related the code inside a module is. Higher is better.

Coupling describes how tightly modules depend on one another. Lower is better.

Here is a simple way to assess them.

Question Signal
Do you modify several modules at once to change one feature? High coupling
Does a module mix unrelated code? Low cohesion
Does deleting one module remove exactly one feature? Well split

The key criterion is change. Put code that changes together in one module, and code that changes for different reasons in separate modules.

This principle sounds familiar. SOLID’s Single Responsibility Principle (SRP) extends its “reason for change” to the module level.

A structure that exposes only the public interface outside the module
A structure that exposes only the public interface outside the module

What Are the Benefits of Modularization?

Change costs drop first.

With boundaries, a change’s impact stays inside the module. Reviewers can also conclude that the outside is safe if the module’s public interface is unchanged.

Team-based division of work becomes easier. Assigning ownership by module reduces conflicts caused by overlapping work areas.

Testing becomes lighter too. You can test one module in isolation without launching the entire app.

Builds also get faster because only changed modules need recompilation. But this is a result, not the goal. Poor boundaries can still force a full rebuild every time.

In Swift, boundaries appear through access control.

// Expose only protocols outside the module
public protocol PriceFormatter {
    func format(_ amount: Int) -> String
}

// Hide implementations inside the module
final class KoreanPriceFormatter: PriceFormatter {
    func format(_ amount: Int) -> String { "\(amount)cir" }
}

Outside, you only need to know PriceFormatter. Ideally, changing the implementation should not even require recompiling code outside the module.

Boundaries are drawn on a whiteboard before they are drawn in code
Boundaries are drawn on a whiteboard before they are drawn in code

When Should You Split—and When Should You Wait?

Modularization is not free. Boundaries bring interface design, version management, and project-configuration costs.

Situation Decision
One feature change affects code owned by several teams or areas Time to split
Slow builds frequently interrupt development Split, but design change boundaries first
An early product whose domain boundaries still change often Wait until the boundaries stabilize
A small app built alone Folders and access control are enough

Modularization with bad boundaries is worse than none. If two modules always change together, the boundary is wrong. Merge them.

This Is How It Comes Up in Interviews

Q. Explain cohesion and coupling, and describe their relationship.

Cohesion is the relatedness of elements inside a module; coupling is the degree of dependency between modules. Good design aims for high cohesion and low coupling. Keeping related code together reduces what crosses the module boundary, so the two are complementary.

Q. What criterion would you use to split modules?

Use reasons for change. Keep code that changes together for the same reason in one module, and separate code that changes for different reasons. A proper boundary answers what design decision the module hides, not merely which features it lists.


The next article covers dependency direction and circular dependencies between modules, problems you inevitably face after splitting them.

Designing relationships after the split is the truly difficult part. With a feel for cohesion and coupling, the next article will be much easier to follow.