2024 Public Training Schedule
November 18 – 21, 2024 – Agile Analysis and Design Patterns
Half-Day Sessions
December 9 – 12, 2024 – Agile Analysis and Design Patterns
Half-Day Sessions
(c) 2024 To Be Agile
I’ve been looking forward to writing this blog post for a while because I’ve been misinterpreted in the past on what I mean by implementation independence. And this is entirely understandable because the concept is slippery and difficult for people to easily grasp. Yet I feel implementation independence is one of the most important concepts in software development, not only in writing our tests but also in writing code, and even just to help us think more clearly.
In English and many other spoken languages, we conjugate verbs depending upon when they happened. For example, we say “walk,” “walking,” or “walked” depending upon when it happened. This lets the listener instantly know the timeframe that actions occur in.
But there’s no semantic distinction that we make in our language when we talk about WHAT, WHY and HOW. These are fundamentally different perspectives that require different knowledge. Because of the way spoken language works, we tend to slip in one of these different perspectives without any warning to our listener. This can significantly lower the fidelity of communication between individuals and so I try to keep a “cohesion of perspectives” when I write or speak by only dealing with a single perspective at one time and making it clear when I shift perspectives.
There are many ways we can break down perspectives and here we’ll just focus on one, which are the levels of perspectives that the UML calls out for object-oriented programs. These are the conceptual, specification and implementation perspectives.
The conceptual perspective talks about what we want but not about how we get it. Ideally, we would like the clients of services to have the conceptual perspective. This means that the services we provide should include APIs that only specify what they give and not specify how they give it. Again, this is not so easy to do because the way we speak and the way we think doesn’t distinguish these different levels of perspectives and so it’s really easy to pollute the conceptual perspective with implementation details.
This is not a just the coding thing, it has a lot to do with how we conceptualize problems. Naming things can also leak implementation details if we’re not careful. Our names for APIs should focus on the results they provide and not how they get those results. Why hide implementation details? The answer is simple. If we can hide implementation details then we’re free to change those details later without breaking a bunch of code.
Dependencies can happen in many subtle ways, including the way we name things. If I’m using 256-bit Blowfish encryption then the method I call to invoke encryption would be encrypt() and not encryptWith256BlowfishEncryption() because the latter name would have to be changed if I decided to replace that encryptor with a different type of encryptor in the future. This also makes it easier for me to introduce polymorphism in the future and I can select from a wide variety of encryptors but all of the encryptors that I use polymorphically are called encrypt().
There are really two worlds in software, the world of intentions and the world of implementation. We software developers get to straddle both worlds and everyone else should live in the world of intentions. This helps make code more flexible when it needs to change in the future.
Previous Post: « Continuous Integration Requires the Right Tests
Next Post: Why Write the Test First »