We finally replaced the implicit coupling with explicit coupling in part 4 of this little series of articles. Now I want to take a moment to reflect on that journey and what, if anything, it achieved. (If you’re new here, maybe go back to part 1 to come up to speed.)
In broad-brush terms we took the following steps:
Notice the implicit coupling. In this case we saw duplication of two numbers and the algorithms in which they were used. In many ways this is the most difficult part of the process, and something I plan to write a lot more about in future articles. Knowledge of connascence can help here, up to a point.
Brainstorm some alternative designs. This felt like mostly mechanical list-making; maybe future worked examples will prove more challenging.
Filter those designs by testing them against our knowledge of the domain. Some of this felt quite arbitrary, so there’s a lot to explore in this space. I’m particularly interested in how — and how often — we mentally run test scenarios against potential designs as part of the filtering process.
Improve the encapsulation and naming around the magic numbers. At the time this didn’t feel like a step in its own right, but after it was done we had better things to hold onto when considering the final step. The names we chose were informed by our domain understanding; but how do we know when we've improved something’s encapsulation?
Use the new encapsulation units1 to create explicit dependencies. (We did this by creating the
DiscountRules
once and injecting them into every object that wanted to use them; theDiscountRules
object thus becomes an adjustment2 to theBasket
,Catalogue
andDisplayBasketCommand
objects.) This was relatively easy in the given example, because the codebase was quite small. I expect to have to work harder in future.
I am definitely not claiming these steps to be in any way a general process for improving code. They just happened to be the steps I took in this one case. And so in future articles I plan to tackle a wide variety of implicit coupling — in a wide variety of programming languages and programming paradigms — and explore the patterns we find along the way: How do we spot implicit coupling? How do we filter potential solution designs? Are there any patterns linking problems with solutions? And does replacing implicit with explicit coupling genuinely improve the habitability of a codebase, as I claim?
But in the meantime, some reflections.
What I think I mean by Implicit Coupling is that this encapsulation unit’s behaviour could be broken by a code change elsewhere and I can’t read that possibility in the code here. That is, any single instance of implicit coupling is a property of multiple encapsulation units simultaneously, and it can only be perceived by standing outside of all of them. Perspective is, I claim, highly important.
How did this implicit coupling come into existence at all? Presumably either by chance (ie. no developer ever thought about it) or, more likely I suspect, that the developer wrote the code while adopting the perspective of standing outside of all of the encapsulation units. I think we do this quite frequently: “I can put this here because I know that that thing over there will match it or compensate for it”. We write code with a god’s omniscient view of the whole landscape. (I suspect that test-driven development may sometimes help to reduce this effect somewhat, but not by much.) And every time we do it, that quick recollection of “that thing over there” leaves a sticky trail of implicit coupling in the code. My starting point in this series of articles is that creating code from this omniscient perspective is a big reason for code being uninhabitable.
So the implicit coupling gets into the code via the omniscient perspective. And later, it seems it can only be detected from that omniscient perspective too, by stepping out and above the individual encapsulation units into a larger one that encompasses them all. And in order to design a solution, we also need to step out and up into that encompassing unit, because we’ll likely need to change all of the inner units that are involved in the coupling.
(I suspect there’s a very clever parallel somewhere here with special relativity, quantum entanglement, and what Albert Einstein called “spooky action at a distance”. Luckily for you I don’t know enough physics to explore that idea here, so consider it an exercise for the interested reader. 🙂)
Expressed another way: we need to revisit the mistaken mindset in order to fix the problems it caused. And the end result is that we have encapsulation units that don’t require us to adopt the omniscient perspective in order to be understood. The coupling likely still exists, but now it is explicit — it is actively helping us to understand the local code we’re looking at.
So next time you’re test-driving a function or a class, try actively noticing every time you adopt that omniscient perspective; call it out and consider your options.
I hope this little worked example and the preceding articles have given you a good flavour of where the Explicit Coupling newsletter is likely to take us. Half the fun for me is that it has already taken turns that I wasn’t expecting, so we’re discovering the topic together. I therefore haven’t — and couldn’t — chart the upcoming direction in any detail, so please let me know what you would like me to cover in future articles.
If you like what you’ve read so far — and where it seems the journey might lead — please share these articles widely and ask all of your friends and colleagues to subscribe. That will in turn help others find it, and it will help keep me motivated too. Thanks for reading this far!
I’ve been using the term “encapsulation unit” in these articles without really explaining it. I think of an encapsulation unit as anything that has a name and can do work — so a function (but not an expression or a statement), or a module, or a class, or an object, or (maybe?) a category, or a process, or an application, or a container, or … you get the idea. I first saw the term in the book Fundamentals of Object-oriented Design in UML by Meilir Page-Jones — which is also where I first read about connascence.
An “adjustment” to an object is a dependency that modifies the object’s algorithm. See Growing Object-oriented Software, Guided by Tests (2009) by Steve Freeman and Nat Pryce.
I first came across a parallel with the concept of entanglement here: http://www.carlopescio.com/2010/11/notes-on-software-design-chapter-12.html where it is used as the term for all coupling, implicit or explicit. Physics has lots of useful metaphors (that do not work exactly in the same way) such as dampening to prevent a change rippling through, or centers of gravity for places that accrue more code over time.