What's The Difference Between Tightly-, Loosely-, and De-Coupled ?
In a tweetstorm that spun up late last week, Taylor Otwell produced the following commentary:
look guys I’m “decoupled” because this package doesn’t have composer dependencies!!! HAHAHAHA LOL
how many composer packages a given package has does NOT affect YOUR code’s decoupling.
that is a matter of programming to an interface, etc.
you people seriously do not understand decoupling. at all.
if you type hint AuraAnything that is a HARD, CONCRETE dependency. THAT is coupling.
IlluminateContracts are all interfaces. abstractions. not concretions. THAT’s decoupling.
IlluminateContractsViewFactory could be a Laravel view factory, could be an Aura one. That’s decoupling.
how many composer pkgs the IMPLEMENTOR needs is an implementation detail my consuming code need not care about
consuming code ONLY cares about programming to an interface for decoupling.
you [@philsturgeon] and brandon [savage] and paul [jones] don’t understand basic programming concepts like coupling
and think somehow coupling is tied to composer
Aura ships hard concretions = you are tightly coupled to Aura.
which should my consuming code give a shit if Aura is decoupled AMONGST ITSELF. Nobody gives a shit.
i only care if MY code is coupled to Aura.
and since Aura makes you depends on hard concretions, it promotes hard couplings.
I’m saying if you type-hint a class dependency, you are coupled to that implementation (cont)
regardless of that package’s internal dependencies
While some of Taylor’s rant is correct for as far as it goes, much of it glosses over important distinctions in subtle misdirection, and the remainder displays some misunderstandings. He is also flat wrong in his assertions of other peoples’ understanding of “basic programming terminology.” As such, I think his words demand a careful response for future reference.
First, I’m glad to see Taylor paying attention to the proper use of terminology in a software context. This is something he’s not always been great at in the past, and I encourage him here.
But I can’t quite tell if Taylor thinks the developers who use Aura believe their code is decoupled by virtue of using Aura. Or maybe it’s that the Aura marketing phrase “fully decoupled libraries” is the target of his ire. I infer allusions to both from his tweets, so I’ll attempt to address both possibilities. (Perhaps there is some other interpretation I have missed.)
It should be so obvious as to not require stating, but for the sake of explicitness: If your code has a dependency on classes in a particular thrid-party package, your code is tightly coupled to the code in that package. This is true for any classes in any library, framework, or other package code. So, if you believe that depending on an Aura library in your code makes your code “decoupled” then you are mistaken. As far as I know, I have never attempted to state or imply otherwise. I don’t think any Aura users have this misperception, but if so, consider this a corrective.
The fact that your code could be tightly coupled to another package does not mean that the other package is coupled to anything else. That is to say, the other package might have no couplings of any sort to any other code outside itself. The other package in that case is de-coupled.
The Aura library packages are designed with that kind of decoupling in mind. That is, no Aura library package depends on anything at all in any other Aura package. Each of the Aura libraries is thus fully decoupled from the others, and incidentally from any framework that is composed of them. (Note that the *_Kernel and *_Project packages are coupled to other packages; the decoupling principle applies only to the Aura library packages.)
But why would you care if a particular library package is itself decoupled from other packages? I assert that one reason (of many) you want libraries that are themselves decoupled is so that, if you have to swap one out in your own code, you only have to worry about the one library, not about all the dependent libraries that it is coupled to (and all the dependent libraries they are coupled to). Swapping out is still tedious: you will need to work through your code, change all the typehints from that library’s classes to those in another, and change all the injections that specify classes from the library. But at least it’s only the one library; the fact that the library is itself decoupled reduces the swapping-out work.
Taylor points out another level of coupling called “loose” coupling. This means that, instead of your code depending on a particular class, you instead depend on an interface. This couples you to the interface, but not to any particular implementation. If your code depends on interfaces, your code is loosely coupled to the implementations of those interfaces (although I don’t think this means you are de-coupled – there’s still some knowledge necessary for interactions).
Being loosely coupled is a good situation to be in compared to being tightly coupled. If you need to swap out an implementation of an interface, you won’t need to change your typehints (unless you swap to another set of interfaces). However, you will still need to change all your injections to the new implementation. Overall, being loosely coupled makes for less work when swapping out libraries.
How can you tell if a package is coupled to another package? Provided that composer.json is not lying, it’s easy enough to examine the “require” element to see if there are other packages listed there. If there are, then it seems likely that the package is coupled to whatever is required. You need to exercise a little judgment, though. If the required package contains only interfaces, then the coupling is “loose”. Otherwise, it is “tight”. If there are no other required packages at all, then the package has no coupling of any sort; it is fully decoupled from anything outside of it.
However, that’s only if you assume composer.json is not lying. To really discover the coupling of a particular package, you would need to examine its code. Any uses of interfaces defined outside the package indicates loose coupling, uses of classes defined outside the package indicates tight coupling, and no uses of interfaces or classes outside the package indicates full decoupling.
(Note that this discussion is of inter-package coupling. Even if the classes inside a package may still be coupled to each other, the package as a whole may still be decoupled from any other package.)
Having said all this, Taylor is trying out a “contracts” package that exposes the Laravel interfaces independently of the implementations. I think this is a neat idea. It’s the only truly new thing I’ve seen introduced to the PHP community by the Laravel codebase, and I think it is worthy of emulation.
Even so, if the “contracts” include anything besides interfaces, I think coupling to them might be defined as “tight”. I am thinking specifically of the Exception classes included in the “contracts” package. Although it may be fair to think that Exceptions are exempt from coupling rules, perhaps they would be better provided as interfaces to Exceptions, instead of classes proper. I will reserve my judgment on that for a later time.
Read the Reddit discussion about this post here.