Clean architectural structure with precise geometric lines and symmetry
Software Engineering May 7, 2026 • 16 min read

Coupling, Cohesion, and the Discipline of SOLID

Every connection between two pieces of code is a chain. Tight coupling means changing one thing breaks everything. SOLID principles are the discipline that keeps your code flexible. This lesson covers coupling, cohesion, all five SOLID principles, and the difference between spaghetti, lasagna, and ravioli code.

Share:
Lee Foropoulos

Lee Foropoulos

16 min read

Continue where you left off?
Text size:

Contents

Complete Guide | Lesson 6: InterfacesLesson 7: Coupling & SOLID → Lesson 8: Design Patterns (May 14)

Every connection between two pieces of code is a chain. The question is whether you're building a bridge or a trap.

When Module A calls Module B, which calls Module C, which reads from Database D, which formats results through Template E, you don't have a system. You have a hostage situation. Change anything in D and E breaks. Modify B and A panics. Touch C and the whole thing falls over on a Friday afternoon while you're trying to leave for the weekend.

This is Lesson 7, and it maps to Geburah on the Tree of Life. Geburah means "Severity" or "Discipline." It's the sephira that prunes, restricts, and enforces boundaries. Where Chesed (Lesson 6) expanded possibility through interfaces, Geburah imposes the discipline to use them properly. Good software doesn't just need creativity. It needs rules. Constraints. The willingness to say "no, this module should not know about that one."

Discipline in code isn't about following rules for their own sake. It's about preventing the future version of you from spending three days debugging a change that should have taken ten minutes.

Coupling: The Chains Between Modules

Coupling measures how much one module depends on another. The tighter the coupling, the more they're chained together. The looser the coupling, the more independently they operate.

Tight Coupling (The Hostage Situation)

Tightly coupled code looks like this:

1CLASS OrderProcessor
2    METHOD processOrder(order)
3        // Directly creates a specific payment processor
4        paypal = new PayPalProcessor()
5        paypal.charge(order.total)
6
7        // Directly formats a specific email
8        email = "Dear " + order.customer.name + ", your order #" + order.id + " ..."
9        smtpServer = new SmtpServer("mail.company.com", 587)
10        smtpServer.send(order.customer.email, email)
11
12        // Directly writes to a specific database
13        db = new MySqlConnection("localhost:3306/orders")
14        db.execute("INSERT INTO orders VALUES (" + order.id + "...)")
15    END METHOD
16END CLASS

OrderProcessor is welded to PayPal, a specific SMTP server, specific email formatting, and MySQL. Want to switch to Stripe? Rewrite processOrder(). Move to a different email provider? Rewrite processOrder(). Switch to PostgreSQL? You guessed it. Every change to any dependency requires modifying this class.

Tangled cables and wires in a chaotic server room arrangement
This is what tightly coupled code looks like if you could see it. Every wire depends on every other wire. Pull one and three more come with it. Nobody knows which one to disconnect first.

Loose Coupling (The Pen Pals)

Loosely coupled code communicates through interfaces, not implementations:

1CLASS OrderProcessor
2    paymentService: IPaymentService
3    notifier: INotifier
4    repository: IOrderRepository
5
6    CONSTRUCTOR(payment, notifier, repository)
7        this.paymentService = payment
8        this.notifier = notifier
9        this.repository = repository
10    END CONSTRUCTOR
11
12    METHOD processOrder(order)
13        this.paymentService.charge(order.total)
14        this.notifier.notify(order.customer, "Order confirmed")
15        this.repository.save(order)
16    END METHOD
17END CLASS

OrderProcessor doesn't know if payment goes through PayPal, Stripe, or carrier pigeon. It doesn't know if notifications go by email, SMS, or push notification. It doesn't know if the database is MySQL, MongoDB, or a filing cabinet. It talks to interfaces. Implementations are injected from outside.

Want to switch to Stripe? Create a new StripePaymentService that implements IPaymentService. Plug it in. Zero changes to OrderProcessor.

0
lines of OrderProcessor that change when you switch payment providers, email systems, or databases. That's the power of loose coupling through interfaces.

The Deployment Test

Here's a simple way to measure coupling: can you deploy one module without deploying another? If changing the notification system requires redeploying the order processor, they're too coupled. If they can be deployed independently, they're properly decoupled.

Cohesion: The Kitchen Drawer Test

Cohesion measures how closely the members of a module are related to each other. High cohesion means everything in a class serves the same purpose. Low cohesion means you have a junk drawer.

High cohesion (everything belongs together):

1CLASS UserAuthentication
2    METHOD login(username, password)
3    METHOD logout(userId)
4    METHOD resetPassword(email)
5    METHOD validateToken(token)
6    METHOD refreshToken(token)
7END CLASS

Every method relates to authentication. The class has one clear purpose. You could describe it in one sentence: "This class handles user authentication."

Low cohesion (the kitchen drawer):

1CLASS Utilities
2    METHOD formatDate(date)
3    METHOD sendEmail(to, subject, body)
4    METHOD compressFile(path)
5    METHOD calculateTax(amount, state)
6    METHOD generateThumbnail(image)
7    METHOD encryptPassword(password)
8END CLASS

What does this class do? Everything. Nothing. It's a grab bag of unrelated functions crammed into one file because someone didn't know where else to put them. Try describing it in one sentence without using "and."

The One-Sentence Cohesion Test

Describe what your class does in one sentence. If you can't do it without using the word "and," your class has low cohesion and should be split. "This class handles authentication." Good. "This class handles authentication and email and file compression and tax calculation." Split it. Now.

200-400
lines is the recommended maximum class size according to Code Complete. If your class is longer, it's probably doing too many things. The size is a symptom. Low cohesion is the disease.

Spaghetti, Lasagna, and Ravioli

Yes, these are real software terms. Yes, the industry named code patterns after Italian food. Here's what they mean:

Spaghetti code: No structure. Everything is tangled with everything else. Logic jumps from function to function with no clear path. Debugging requires tracing a single strand through a plate of tangled noodles.

Lasagna code: Too many layers. An HTTP request passes through 17 abstraction layers before touching the database. Each layer adds overhead and complexity without adding clarity. Over-engineered.

Ravioli code: Small, self-contained, well-bounded modules. Each "raviolo" is a complete unit with a clear purpose. They don't leak into each other. They communicate through well-defined interfaces. This is the goal.

The goal is ravioli code: small, self-contained modules that are complete on their own and communicate through clean interfaces. Each piece is individually testable, replaceable, and understandable.

The SOLID Principles

SOLID is an acronym coined by Robert C. Martin ("Uncle Bob") that captures five principles of object-oriented design. They're not rules. They're guidelines. But following them consistently produces code that's dramatically easier to maintain, test, and extend.

S: Single Responsibility Principle

A class should have only one reason to change.

A knife cuts. It doesn't also serve as a hammer, a screwdriver, and a bottle opener. (Swiss Army knives exist, but nobody wants their production code to be a Swiss Army knife.)

1// BAD: Two reasons to change (business rules AND formatting)
2CLASS Invoice
3    METHOD calculateTotal()
4    METHOD applyDiscount()
5    METHOD generatePdf()
6    METHOD sendEmail()
7END CLASS
8
9// GOOD: Separated responsibilities
10CLASS Invoice
11    METHOD calculateTotal()
12    METHOD applyDiscount()
13END CLASS
14
15CLASS InvoicePrinter
16    METHOD generatePdf(invoice)
17END CLASS
18
19CLASS InvoiceNotifier
20    METHOD sendEmail(invoice)
21END CLASS

If the PDF format changes, only InvoicePrinter changes. If the discount logic changes, only Invoice changes. If the email provider changes, only InvoiceNotifier changes. No class has two masters.

O: Open/Closed Principle

Open for extension, closed for modification.

You should be able to add new behavior without changing existing code. This is exactly what interfaces enable (Lesson 6): add a new IPaymentService implementation without touching the checkout code.

L: Liskov Substitution Principle

Subtypes must be usable wherever their parent type is expected, without breaking behavior.

Remember the Rectangle-Square problem from Lesson 5? Square broke Liskov because changing its width also changed its height, violating the contract that Rectangle established. If your subclass surprises the caller, it violates Liskov.

I: Interface Segregation Principle

Many small interfaces beat one fat interface.

Don't force a fish to implement walk():

1// BAD: One fat interface forces irrelevant implementations
2INTERFACE IAnimal
3    METHOD walk()
4    METHOD swim()
5    METHOD fly()
6END INTERFACE
7// A fish must implement walk() and fly()? Absurd.
8
9// GOOD: Small, focused interfaces
10INTERFACE IWalkable
11    METHOD walk()
12END INTERFACE
13
14INTERFACE ISwimmable
15    METHOD swim()
16END INTERFACE
17
18INTERFACE IFlyable
19    METHOD fly()
20END INTERFACE
21// A fish implements ISwimmable. A duck implements all three.

D: Dependency Inversion Principle

Depend on abstractions, not concretions.

We covered this in Lesson 6. High-level modules shouldn't depend on low-level details. Both should depend on interfaces. This is the principle that makes loose coupling possible.

Five architectural columns supporting a classical structure
Five pillars. Each one supports the structure independently. Remove one and the building weakens. Remove all five and it collapses. SOLID principles work the same way: each one reinforces the others.

SOLID Quick Reference

S - Single Responsibility: One class, one job, one reason to change. O - Open/Closed: Add new behavior by extending, not modifying. L - Liskov Substitution: Subclasses must honor their parent's contract. I - Interface Segregation: Small, focused interfaces over fat ones. D - Dependency Inversion: Depend on abstractions, not implementations.

62%
reduction in bug density in codebases that consistently follow SOLID principles vs those that don't, according to industry studies on software quality metrics.

When to Bend the Rules

SOLID is not a religion. It's a set of guidelines that works brilliantly for production code and can be overkill for prototypes.

Bend the rules when:

  • You're writing a quick script that runs once and gets deleted
  • You're prototyping and speed matters more than structure
  • Following the principle would create more complexity than it prevents

Follow the rules when:

  • Other people will read your code
  • The code will live longer than a week
  • The system will grow beyond its current size
  • You're building anything that needs to be maintained

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." Martin Fowler wrote that, and it captures the entire point of SOLID: code isn't for computers. It's for the humans who maintain it.

Balanced architectural design showing precision and measured proportions
Good architecture balances discipline with pragmatism. Too little structure and you get spaghetti. Too much and you get lasagna. The art is finding the ravioli sweet spot.
Lesson 7 Practice 0/6
How was this article?

Share

Link copied to clipboard!

You Might Also Like

Lee Foropoulos

Lee Foropoulos

Business Development Lead at Lookatmedia, fractional executive, and founder of gotHABITS.

🔔

Never Miss a Post

Get notified when new articles are published. No email required.

You will see a banner on the site when a new post is published, plus a browser notification if you allow it.

Browser notifications only. No spam, no email.

0 / 0