Extremely Extensible Software Design (Part 3)
I’ve explained the way I think of extensible code like this:
Code is extensible if a developer need not edit any existing code other than configuration code in order to add a new feature.
In my previous two articles, I gave examples of code that is extensible in this sense.
In this part, I will explain some of the reasons I think this way.
The Little Things
Anxiety and frustration
When a developer has to edit code in a number of different files, there are some worrying thoughts that come up.
Am I missing something?
Have I edited all the code I need to edit?
How do I know if I have?
This is not a good feeling.
Am I breaking something?
Holy cow! What a mess!
We’ve probably all had some of these thoughts when maintaining or extending code before, even our own code!
If we design our code in such a way that it can be extended without editing existing code, then these concerns are significantly reduced.
Requiring recompilation and changes in dependent classes
This is especially important if we are writing a shared library. We don’t want our library to be designed so that changes (especially changes that you expect) will require our clients to recompile and rework their code when they upgrade.
One of the example warning signs I mentioned in Part 1 was the need to add an element to an enumeration in order to extend the code. One of the reasons this design hurts the extensibility (and reusability) of our code is that when an enumeration changes, other code that used to compile no longer compiles. (In a language like Kotlin, the compiler checks to ensure that when
statements are exhaustive.) Not only that, but our clients will need to write code to deal with the new enumeration element.
Some of the other issues I mentioned in Part 1 fall into this category as well.
Well-Known Smells
A “smell” in software is a feature of the code that suggests to an experienced developer that the code will be difficult to maintain or extend. Much has been written about recognizing smells and dealing with them by “refactoring”.
Refactoring software means to reorganize the code without changing its functionality. The intention is to make the code more maintainable and extensible.
There are not hard rules about smells. They are just flags that should trigger us to challenge the code. Smelly code may be fine and folks can disagree about whether a smell needs to be removed and which smells are worse that others.
The code problems I described in Part 1 are well-understood and published code smells.
Switch and if chains
In Part 1, I mentioned that if a developer has to add cases to a switch
or if
statement, that is a warning sign that your code may have problems.
This is a well-known smell that should make us think about using a polymorphic design. The recommended solution to this is, naturally, to replace conditional with polymorphism. And, yes, that’s even the name of the refactoring!
Shotgun Surgery and Solution Sprawl
If a developer needs to make changes in many existing classes in order to add a feature, this is a well-known smell called “shotgun surgery”. In his Refactoring book (highly recommended), Martin Fowler explains that this smell occurs when code in different places must be edited to extend the software.
Another highly-recommended book is Refactoring to Patterns by Joshua Kerievsky. In it, he gives a very similar definition to the phrase “solution sprawl.” His treatment of this issue is excellent.
Conclusion
Here I’ve given some of the reasons I think of extensible code the way I do.
In Part 1 and Part 2 of this series, I give a couple of examples of patterns for extensible code.