Coupling Isn't Bad, Bad Coupling Is
"Reduce coupling" is one of those phrases everyone repeats and nobody questions. But coupling isn't the villain. A function that calls another is coupled to it. A component that reads from a store is coupled to it. Software is coupling, parts leaning on parts to get anything done.
The real villain is careless coupling: things wired together for convenience, not because they belong together, so every change ripples further than it should. It never looks dangerous at first. That's how it sinks a codebase.
So the useful question isn't "is this coupled?" Everything is. It's "do these two things have a real reason to change together?"
If two things genuinely change for the same reason, coupling them is fine, even good. If they don't, you've created accidental coupling, and that's where the pain lives.
Three examples make this concrete.
The Giant Multi-Step Form
Almost every codebase grows one. A form starts simple, then every new field and rule gets piled into the same file until one component knows about everything.
No single line is wrong. But the payment logic now shares a file with the name field. The tax-ID rule knows about the plan, which knows about the step number. To fix step three's validation, you scroll past steps one and two to find it.
These steps never had a reason to share a file. They got glued together because adding one more if was the easy path every time.
The fix: let each step own its fields, validation, and UI. The parent just knows the order.
Now the payment step changes without anyone touching the personal step, and each piece is testable alone. The coupling that's left, the parent knowing the order, is the real kind. It has to exist.
Search That Knows Too Much
A search box should do one thing: take a query, return results. But it tends to sprout tentacles into filtering, sorting, URL state, analytics, and rendering.
This one function is now tied to the database shape, the filter, the sort state, the analytics SDK, the router, and the UI. Restyle a result card? Edit search. Add a filter? Edit search. Everything funnels through one overloaded bottleneck.
Split those jobs apart, querying, filtering, tracking, rendering, and each can change without disturbing the rest. Search goes back to doing one thing, and the callers compose the rest.
The Sprawling Config Object
The sneakiest one: a giant config blob with thirty-plus fields, each shaped a little differently because each was added by a different person on a different day.
Spot the chaos: required, isRequired, req, and optional are the same idea in four costumes. type, inputType, kind, and widget all describe the input. label, title, and displayName all name the field. Every reader has to memorize the dialect of each entry.
This is coupling at its most corrosive. There's no single shape to learn, so the cost doesn't grow with the number of fields, it grows with every inconsistency. You can't write one renderer or one type to cover them, because no two fields agree on what "required" looks like.
An inconsistent shape turns every entry into a special case. The config stops being data and becomes a pile of exceptions, each one a thing you have to remember.
The fix isn't fewer fields, it's one shape every field obeys:
Still thirty fields, but only one shape to learn. One renderer handles all of them. One type catches mistakes. The thirty-first field costs nothing extra, because it speaks the same language as the rest. The coupling didn't vanish, it became consistent, and consistent coupling is cheap.
The Takeaway
Coupling isn't the enemy, accidental coupling is. The form, the search box, and the config all went wrong the same way: things got wired together for convenience, not because they belonged together.
So before you wire two things up, ask one question: do these really belong together, or am I just taking the easy path today? If they belong together, couple them, and keep it consistent. If they don't, give each piece its own corner. Your future self, reading this file next month, will thank you.