Why tight coupling is better than loose coupling most of the time

Ben Lu
3 min readSep 21, 2020

--

Something that we’re taught from blogs and the almighty overlords of software development is that loose coupling is better that tight coupling. For me and probably for most people, most of the time, that is probably not true.

Our examples will assume that you are using a typed language and systems where schema changes are propagated from a source of truth for tight coupling examples.

The Benefits

The Ripple effect

The ripple effect means that if you make a change in one place, it’s effect transfers elsewhere. There are two benefits to this: you know if you broke something very quickly as the type checker will throw an error, and it means you have faster iteration speed for changes as everyone gets the same upgrade at once.

In the distributed case, you will still be limited to non breaking changes for 100% availability.

Less overhead

The more assumptions you can make, the simpler life is. You can see this with Blockchain versus Credit Card companies, microservices versus monoliths, asynchronous versus synchronous, monorepos versus… multi repos? Anyways, you get the point, if you can assume that something happens instantly rather than worrying about the Byzantine Generals Problem, or going from having many possible states, which are compounded for each action, to only one possible state, you have to do less work and less complexity to worry about.

Knowing who depends on you

Most of the time, within your own company you want to know who uses your services, for example, if you want to deprecate a service, update it, or fix security issues.

Fallacies

Bad design is not tight coupling

Generally speaking, best practice is to design modules. Modules expose a “public” contract and modules should communicate through this contract. So if you have code calling each other everywhere, this is just bad design.

You want to be coupled to as few things as possible, but the things that are coupled where reasonable should be tightly coupled.

More effort to test

Mocking libraries are great, it’s the same effort to mock an API as it is to write a Mock class and inject it.

Microservices are and should be loosely coupled

The design idea is typically that services should publish (produce) to a Kafka log and other services consume from this Kafka log. Here you have a huge schema evolution problem, how do you make a migration, do you throw away old events, how many consumers do you need to upgrade anyways?

If you tightly couple your microservices, then they can communicate with a strict contract, with single generational backwards compatibility for async deploy availability. You can also see the dependency graph, which means you can avoid the pinball architecture, and design better for your needs.

When loose coupling is better

Third parties

Are you writing a library for open source? Probably needs to be loosely coupled, versioned etc. Obviously you can’t make downstream changes automatically.

The same goes for the reverse direction, if you depend on a third party dependency, you don’t want your code to automatically update whenever someone pushes it, you probably want to upgrade infrequently and test thoroughly for all upgrades.

Lots of dependencies

Sometimes you need to make breaking changes. And sometimes that’s a huge change across a lot of places. It’s not really feasible to make meaningful code changes across a lot of code at once. Consider migrating programming languages, if you can do this gradually, for example feature by feature, it’s much easier.

Speed

Sometimes it’s easier to write some scripts that aren’t tightly coupled. Don’t let a blog slow you down.

Thoughts

Software as a science is something that has united people in an objective truth. If your code doesn’t run then it doesn’t run, and if it does run then it’s great. People might say it’s ugly or bad code, but if it runs, then it’s doing its primary function. This is not true of design patterns, everyone codes in a different style, uses different languages and preaches their own beliefs.

This blog article is just more preaching, if what you are doing works, then great! If you worry about doing changes due to downstream effects, and across teams, perhaps reevaluating dogmatic assumptions in your process may improve your outcomes.

--

--