High Cohesion and Low Coupling
Good modular design groups closely related responsibilities while minimizing dependencies between modules. In Java backend systems, this makes code easier to understand, test, change, and reuse.
Cohesion describes how strongly the responsibilities inside one module belong together. A highly cohesive class or package has a focused purpose rather than collecting unrelated operations simply because they are convenient to place together.
Coupling describes how much one module depends on other modules. Low coupling means changes inside one component require few or no changes elsewhere, which reduces the ripple effect of modifications.
In a monolith, high cohesion and low coupling are still valuable. A monolith can be divided into well-defined modules even though everything runs in one deployable application. Good boundaries make later maintenance and possible extraction into separate services easier.
Java interfaces, dependency injection, and clear domain boundaries can help reduce coupling. For example, an `OrderService` can depend on a `PaymentGateway` interface instead of directly constructing a particular payment implementation.
The goal is not to eliminate all dependencies. Some dependencies are necessary; the design objective is to keep them intentional, narrow, and stable while keeping each module focused.
Key points
- High cohesion keeps related responsibilities together.
- Low coupling limits dependencies between modules.
- A monolith can still have strong modular boundaries.
- Interfaces and dependency injection can reduce concrete dependencies.
- Avoid both giant classes with unrelated duties and excessive cross-module knowledge.
Interview questions
What do cohesion and coupling mean?
Cohesion measures how closely related the responsibilities within a module are. Coupling measures how strongly one module depends on other modules. Good design generally aims for high cohesion and low coupling. This helps isolate changes and makes code easier to understand and test.
How would you reduce coupling in a Java backend?
I would first identify which dependencies are necessary and which expose implementation details. Interfaces can hide concrete implementations, while dependency injection prevents classes from constructing their own collaborators. I would also keep APIs between modules narrow and avoid reaching directly into another module's internals. The objective is controlled dependencies, not zero dependencies.
Can a monolith have high cohesion and low coupling?
Yes. Deployment structure and internal modularity are different concerns. A monolithic application can contain separate modules with clear responsibilities and limited dependencies. This is often called a modular monolith.
Why is high cohesion useful when maintaining a large codebase?
High cohesion makes it easier to locate behavior because related operations are grouped together. A change to one responsibility is less likely to require searching through unrelated classes. It also makes tests more focused and reduces the cognitive load required to understand a component.
What is a practical sign that two modules are too tightly coupled?
A strong warning sign is that a small internal change in one module repeatedly forces changes in several others. Another sign is when one module knows many implementation details about another module. I would examine the dependency direction and identify whether a narrower API or abstraction can establish a cleaner boundary. I would also consider whether the responsibilities themselves are split incorrectly.