This article is about the second group of concepts I wanted to talk about after re-reading Clean Architecture.
I want to try something different this time: Instead of elaborating each idea in long, continuous prose, I'll just list them as separate chunks.
So, here it goes:
- We already know that tight coupling is a bad thing to have. It binds together software in a way that makes it hard to change and adapt to future changes. If two pieces of software are tightly coupled, they can't change independently and maintenance becomes harder as a result.
- In an ideal scenario, most of your code should depend on abstractions instead of depending on concrete classes/software constructs. In statically typed OOP languages, this means that most references should be to either interfaces or abstract classes and that DI is used to control the dependency flow. For dynamically typed languages you can accomplish pretty much the same with duck typing.
- Dependency cycles happen when a class depends on a class that depends on ... that depends on the first class of the chain. These are some of the most annoying things to deal with in a project but can be solved (in most cases) by using the Dependency Inversion Principle.
- A common form of depending on concrete things is when you tie your project to specific technologies/libraries. Like directly calling DB wrappers across all your code (or worse, smearing SQL everywhere). Instead, you should hide those details behind a clearly defined interface tailored to the problem you are trying to solve, it will make things easier if you need to change tech in the future.
- The real reason for depending on abstractions is that high-level concepts tend to change less frequently than implementation details. If you define a coherent set of behaviors in an interface, it's unlikely that it will change as often as the classes that implement it. It's about them being far more stable software constructs.
- It's impossible to have a system with only stable components. Stability is tied to requirements, and if they change your system will need to change. The only thing you can do is to ensure that the components that embody that instability are not directly referenced and are hidden behind clean interfaces/abstractions.
- Talking about "concretions", you can measure the dirtyness of a component by checking how dependent it is on implementation details. The main method will very often be the dirtiest part of your system. This is ok, if any part of your system is allowed to binge on dependencies and tight coupling, that's the main method.
- Another benefit of not tying your system to concrete implementations/technologies is that it becomes easier to totally remove or change them down the road. Hiding them behind an interface lets you delegate those choices to future you. The counterpart of you that lives in the future has much more knowledge about the system needs and is better equipped to make those choices, so make it easy for your buddy to implement those decisions.
Those are the ones I can remember, they all embody the exact same idea: Build your system in a way that protects your business logic from knowing implementation details.
Nowadays we have a huge array of amazing languages that make this extremely easy, so go ahead and use those features to build great software!
Also, if you are interested in design principles for achieving this, you can take a look at a series of articles I wrote on the S.O.L.I.D principles, I hope you'll find them useful.
Thanks for reading.