May 28th, 2020
Decoupled. But from what?
When I joined a new team a while ago, I received an explanation from the lead developer about the existing architecture. A discussion started because of a comment they made "oh, this is fine because it's totally decoupled". I had the complete opposite view - in my eyes the coupling was very tight. I don't remember the exact arguments used, but I do know that we did not reach an agreement that day.
What troubled me though, is that I experienced that coupling to be a fact. A property of the software artifact, not something up for debate. And certainly not a "matter of opinion". Then at DDDEU this year one of the keynote speakers was Kent Beck, who spoke about coupling and cohesion. And during the talk he made a point that finally made it click for me:
When you say things are coupled, they are coupled with respect to something.
The example he gave was interesting:
Given two completely independent services, when one misbehaved in a certain way causing massive network traffic, the other also went down. It turned out these two seemingly independent services were coupled after all, with respect to the network switch they shared.
Coupling is a matter of perspective
Having gained the wisdom that things can be coupled and decoupled at the same time, I tried to think of a few more examples.
What I came up with was a train. In a train, you could say all the sections are coupled to each other. You can't simply remove a car in the middle without touching the cars on either side. So they are coupled with regards to the physical composition of the train.
The concept of a train is also coupled, with regards to the mechanism used to connect the sections together. All sections have to be compatible. Same for the wheel spacing: each section needs to fit the same track.
At the same time, the cars might be completely decoupled.
One section might be a passenger car and another used for transporting goods. Or one might have US electrical outlets and 110 volt while another might use the UK standard and 220 volt (I'm not sure if this is ever the case but it should be possible).
Coupling is not isolated
It also occurs to me that changing one aspect of coupling/decoupling might influence another. If a train's sections were coupled even more rigidly - let's say welded together - then the fact that the use of a section is independent from other sections (i.e. a section can be used as a passenger or goods transport) is less relevant.
Individual sections are still decoupled with respect to function, but to change the train is so costly that you can't take advantage of it.
Decoupling is abstracting
From my own examples, I gather that standardization plays a big part in decoupling. That is to say, we use standards to push the coupling into a higher level of abstraction:
- Not using any standard? Be prepared to hand-weld each section of your train when you add or remove sections.
- Using your own standard? Be prepared to manually alter all sections of trains you buy from a supplier.
- Using a standard for connecting parts? Be prepared to alter all the sections when that standard changes.
Hopefully, each of those steps would happen in an ever lower frequency - reducing the amount of work needed to accomodate the inevitable change.
Coupling with respect to something
So let me take this all the way back to the original story. In my discussion I failed to ask the question:
which aspect of the architecture are you referring to as decoupled?
We were developing an API, using another upstream API as a data source. Those two API's were closely related, but not completely the same. They each had their own API specification. From that perspective, those two applications are completely decoupled. They were even written in different programming languages and one could change without affecting the other as long as the specifications were met.
Except, in the implementation of our API, we made a request to the upstream API, added/altered some data, and then sent the result as a response. Seen from this angle, there is no way to guarantee that we meet our specification. Our output is completely dependent on the output of the upstream API. So there is very tight coupling there, any change in the specification in the upstream API needs to be changed in our API at exactly the same time in order not to affect our downstream clients.
Output A* is A, plus any modifications done by the backend processing. So while specification A* happens to match specification B at the moment, there are many non-breaking changes (like adding a key) that will immediately be part of the output of the backend - so we either change spec B to match or remove the key during processing. Either way, being forced to change one part of the code due to a change somewhere else is tight coupling.
Coupled, or not coupled...
And so with two different perspectives, were we both right to argue that these API's were decoupled AND coupled? I'm not sure. But I still think that the implementation was the bigger problem 😎
Pointy haired boss