Cohesion: A Class Does One Thing Well
What This Concept Is
Cohesion is how strongly the parts of one module belong together. A cohesive class has a single, crisp answer to the question "what does this class do?" A non-cohesive class answers that question with a list, a paragraph, or an embarrassed pause.
Cohesion is not the same as size. A 300-line class can be cohesive if every line serves the same purpose; a 30-line class can be non-cohesive if it is half parsing and half formatting.
Practical signs of high cohesion:
- operations contribute to one coherent contract; sharing fields is only one clue
- the class name accurately predicts what every method does
- related policy changes have a clear home, while legitimate integration edits remain explicit
Why It Matters Here
Low cohesion is expensive because it spreads the blast radius of change. When a class does two unrelated jobs, every change to job A risks breaking the tests for job B. Review time goes up, merge conflicts multiply, and the class becomes a magnet for unrelated work.
Later clusters in this module (SOLID, code smells) are largely specialized diagnoses of low cohesion: Single Responsibility is cohesion at class scope; Feature Envy is cohesion violated across class boundaries.
Concrete Example
Low cohesion, one class doing three jobs:
class Order:
def __init__(self, items): self.items = items
def total(self): return sum(i.price for i in self.items)
def to_html(self): return f"<div>Total: {self.total()}</div>"
def save_to_db(self, conn): conn.execute("INSERT ...")
Order is simultaneously a domain object, an HTML renderer, and a persistence gateway. Three different reviewers (business rules, UI, infrastructure) will all touch this class for unrelated reasons.
Raise cohesion by separating jobs:
class Order:
def __init__(self, items): self.items = items
def total(self): return sum(i.price for i in self.items)
class OrderHtmlView:
def render(self, order): return f"<div>Total: {order.total()}</div>"
class OrderRepository:
def save(self, order, conn): conn.execute("INSERT ...")
Each class now has methods that share a purpose and operate on a shared focus.
Common Confusion / Misconception
"High cohesion means small." It does not. A Polynomial class with twenty methods for addition, multiplication, differentiation, and evaluation can be perfectly cohesive because every method is about polynomials.
A more dangerous misconception: "coupling is bad, so cohesion is good, so they're opposites." They are related, but not opposites. Splitting Order above separated unrelated responsibilities, but it also introduced new couplings (OrderHtmlView now depends on Order). Good design raises cohesion without paying more coupling than the problem deserves.
How To Use It
When reading or writing a class, apply this sequence:
- Write the class's purpose in one sentence, out loud, without "and."
- For each method, check whether its behavior is implied by that sentence.
- Map operations to the invariants and decisions they protect; field-access counts are clues, not thresholds.
- A stateless operation may still belong to a cohesive public capability. Check callers and ownership before moving it.
- A cluster of related fields suggests an extraction candidate. Compare the resulting collaboration cost and a concrete change scenario before deciding.
Check Yourself
- Can a cohesive class still have many methods? Why?
- Why is "this method does not use any fields" a cohesion warning?
- What is the relationship between cohesion and the Single Responsibility Principle?
Mini Drill or Application
Take a class from a project you know (yours, or one open-source file under 300 lines). Do all four:
- Write its purpose in one sentence without "and" or "also."
- List the methods and mark each as
coreordrift. - For every
driftmethod, name a better home for it. - State whether the class would become easier or harder to change for business reasons after that move.
Difficulty writing the purpose is a prompt to investigate; it is not by itself proof that the implementation is wrong.
Read This Only If Stuck
- Clean Code: Cohesion and many small classes
- OOAD: Cohesion and coupling
- Good Code, Bad Code: Classes (Part 1)
Engineering extension: Test the boundary with a change
For the Order example, compare a tax-rule change with an HTML-layout change. Write the expected touched responsibilities for both the combined and separated designs. The new repository and renderer introduce collaboration contracts; count that cost as well. A pure function that totals immutable line items may be as cohesive as a method. Try: justify keeping a five-line script intact. Check: preserving a readable local calculation can be better than three classes when no independently changing policy exists.
Use the design contracts and practice feedback after an independent attempt.