Writing Maintainable Code: High Cohesion
June 10, 2025
You're fixing a serious problem at 2 AM. The payment system is broken, customers are angry, and you need to fix it fast. You open the PaymentProcessor
class and find 847 lines of code doing everything from checking payments to sending emails to calculating taxes to tracking user data. Your heart sinks as you realize this one class handles dozens of different tasks.
This happens more often than we want to admit. In our previous article on dependency injection and inversion of control, we looked at how to reduce connections between code parts. Today, we'll focus on the other half: high cohesion.
Most developers have heard "high cohesion, low coupling," but understanding what high cohesion really means—and how to do it—can change how you write code.
Understanding Cohesion Through a Kitchen Story
Before diving into code, let's understand cohesion through a familiar setting: the kitchen.
Picture a new cook setting up their first kitchen. They buy all the basic items but have no system for organizing them. Pots sit next to breakfast cereal. Cooking oil hides behind the coffee maker. Salt and pepper are in different cabinets. When it's time to cook even a simple meal, they spend more time looking for ingredients and tools than actually cooking.
Now compare this with a professional restaurant kitchen. Every item has a purpose and a place. The salad station has everything needed for salads: fresh greens, cutting boards, special knives, dressings, and toppings. The grill station has all grilling tools within arm's reach. The baking section has baking tools, ingredients, and equipment.
But here's where it gets interesting. Some expensive steakhouses take this further. They prepare special setups for their best dishes. For their famous ribeye, they create a special prep station with the exact cut of meat, specific seasonings, the right cooking oil, toppings, and even the proper plate—everything needed to make that dish perfectly.
This ribeye station shows high cohesion. It serves one clear purpose: creating the perfect ribeye. Everything in that station relates directly to that goal. Nothing unrelated gets in the way.
This focused organization lets the chef work efficiently. They don't waste time searching for ingredients across the kitchen. They don't accidentally grab the wrong seasoning. The chance of mistakes drops a lot because everything they need is logically grouped and easy to reach.
High Cohesion in Software Engineering
High cohesion in software works the same way. It means grouping related functionality together within a module, class, or function so that the module has a single, well-defined purpose.
Think back to our 2 AM debugging scenario. That massive PaymentProcessor
class violates high cohesion. It handles payment processing, but also email notifications, tax calculations, user analytics, and probably a dozen other unrelated tasks. This makes it incredibly difficult to understand, modify, or debug.
A high-cohesion approach would split this into focused modules:
PaymentValidator
handles only payment validation logicPaymentProcessor
handles only the core payment processingNotificationService
handles only notification sendingTaxCalculator
handles only tax-related calculationsAnalyticsTracker
handles only analytics events
Each module has a clear, single responsibility. When you need to fix a payment validation bug, you know exactly where to look. When you need to modify how notifications are sent, you only touch the notification service.
This change is like what happens when a messy kitchen becomes a professional one. Just as the chef doesn't need to search through the entire kitchen to find ribeye seasonings, you don't need to look through hundreds of lines of unrelated code to find the payment checking logic.
The Apparent Contradiction: To Group or Separate?
You might wonder: "High cohesion says group related things together, but low coupling says separate things. Don't these conflict?"
Vlad Khononov, in his book "Balancing Coupling in Software Design," offers a great insight. He describes cohesion as "how much the parts within a module belong together." In other words, cohesion is actually a type of coupling—but it's "good coupling."
This view solves the seeming conflict. Some coupling is necessary and helpful. When you group related functionality together, you're creating coupling—but it's intentional, logical coupling that makes your code easier to understand and maintain.
The key is asking: "What belongs together to make future maintenance easier?" If two pieces of functionality are so related that they often change together, they probably belong in the same module. If they serve different purposes and change on their own, they should be separated.
Two Different Perspectives
High cohesion and low coupling examine your code from different angles:
- High cohesion looks inside a module, ensuring everything within it is highly related and serves a single purpose
- Low coupling looks between modules, ensuring they don't become entangled in ways that make changes difficult
These views work together. When you achieve high cohesion within a module, you naturally create clear boundaries around that module. These clear boundaries make it easier to achieve low coupling between modules.
Consider a well-designed UserAuthenticator
class. High cohesion ensures it only contains login-related methods: login()
, logout()
, validateCredentials()
, and refreshToken()
. It doesn't contain user profile management or email sending logic. This focused design creates clear boundaries, making it easier to use this class in other parts of your system without unwanted connections.
The Maintenance Advantage
High cohesion pays off when you're maintaining and extending code. Instead of searching through scattered, unrelated functionality, you can quickly find exactly what you need to change.
When a bug report comes in about password reset emails, you know to look in the PasswordResetService
. When you need to modify payment processing logic, you go straight to the PaymentProcessor
. This predictability reduces debugging time and lowers the risk of creating new bugs.
High cohesion also makes code reviews more effective. Reviewers can focus on a single, well-defined area of functionality rather than trying to understand a huge class that touches multiple concerns.
Putting High Cohesion into Practice
Start using high cohesion in your next code review or refactoring session. Look for these warning signs of low cohesion:
- Classes or functions that are difficult to name clearly
- Modules that have "and" in their description (handles payments and notifications and analytics)
- Code that requires you to understand multiple unrelated concepts to make a simple change
When you spot these patterns, ask yourself: "What's the single purpose here?" Then slowly separate unrelated responsibilities into their own focused modules. Your future debugging sessions will become much more pleasant—and far less likely to happen at 2 AM.
Support ExplainThis
If you found this content valuable, please consider supporting our work with a one-time donation of whatever amount feels right to you through this Buy Me a Coffee page.
Creating in-depth technical content takes significant time. Your support helps us continue producing high-quality educational content accessible to everyone.