As I was writing my book, Beyond Legacy Code, I started to see a pattern emerge. Practice 1 is “say what, why, and for whom before how.” One of the anti-patterns in software development, and therefore something that we want to avoid, is leaking implementation details in our code by saying how we do something in software. In software, especially object-oriented programming, we have this idea of an inside and outside. The ultimate outside of a computer program is its user interface but more often we like to subdivide within the program so that there are insides and outsides all over the place.
We conceive of our programs as a collection of objects and each object has public parts and private parts. This allows us to partition behaviors and hide implementation details. This is another way of seeing object-oriented programming that is quite valid, it’s all about hiding implementation details. Why? Because when we can hide something we can change it later without affecting other parts of the system.
A lack of encapsulation is one of the main things that causes technical debt in software. “Breaking up the monolith,” as one of my clients put it it is very often the task of working with legacy code. The monolith works as it was supposed to but we find it very difficult to repurpose it and that’s because we didn’t take the time to correctly model the behaviors. We focused on the implementation but not on the entities who are responsible for those implementations. That’s the additional step that object-oriented programming asks us to take.
It’s not enough to just create behavior in an object-oriented program, you must also assign those behaviors to entities who are responsible for performing them. We do this because it allows us to break up a complex program into its constituent parts, which allows us to more easily manage and extend a program in the future.
The notion of hiding implementation details by only saying what the service does and not how that service does what it does is a very important part of Agile software development. This is because it enables us to more easily refactor code.
For example, a critical practice in Agile software development is test-first development where we write a test for a behavior before implementing that behavior. Doing test-first development in this way helps us stay focused on building behavioral tests that are not dependent on the way we implement those behaviors and this is very important because these are the kinds of tests that support us when we go to refactor those behaviors and change the implementation.
If we change an implementation for a behavior without changing the behavior itself and a bunch of our tests break then they’re not helping us, they’re actually hindering us. When we do test-first development correctly and write tests for behaviors then the tests that we do write support us in regression and when we refactor our code, just like they should.
Most fundamentally, we have different ways of processing how something works versus what it is in our brains and when we speak we often semantically confuse these two concepts and implementation details to explain what we’re talking about. This can be a point of confusion and it’s better when communicating to separate out the what from the now when we’re talking and writing.
It’s a subtle thing but is valuable to focus on because by doing so we are also supporting good software development principles and practices.
When we hide how we do something by only showing what we’re doing, it encourages us to form tight contracts and strong interfaces for our methods and behaviors. It supports us in making autonomous objects that are straightforward to understand and work with.
I would even go so far as to say that the central quest of a software developer is to be able to get the job done while hiding as much of the implementation details as possible because the more we can hide the more of our system is changeable in the future without suffering a high cost.
Note: This blog post is based on a section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Understand Trade-Offs
Next Post: Name Things Well »