Take full advantage of language features
It’s safe to say a substantial amount of business application software is in production today. It’s less safe to speculate about exactly how much code is in production. An entry on StackOverflow hints at how hard it is to determine how much code exists, and how much of it is written in this or that programming language.
In my line of work, I often work with organizations that have a substantial amount of existing code to support, maintain, and enhance. Anecdotally, I can report that nearly all the existing code I’ve seen in large corporations is written in Java or Cobol. Of course, there’s a lot of code in other languages, too; but Java and Cobol seem to have achieved very high market share, and held onto it for many years. So, if we’re interested in remediating technical debt in existing code bases, it seems reasonable to look at the design and coding practices applied to these two languages.
I want to consider Java design and coding practices in this piece. Cobol presents a different set of challenges, as it has no type system to speak of. With respect to existing Java code bases, refactoring monolithic code plays a large role in remediating technical debt; but here I want to home in on something a little more fundamental: The evidence (in the form of existing code bases) suggests most programmers do not take good advantage of built-in features of programming languages to keep their code simple, reliable, and maintainable.
tl;dr…
Take a look at this.
…and here’s the long version
Business application software manipulates information that represents concepts relevant to a business domain. When a programming language offers a way to define modules or objects that encapsulate key information about a domain concept, wouldn’t we want to use it? Object Oriented languages support custom classes, and Functional languages support custom type definitions. Why, then, does so much existing code in the wild define key domain concepts as simple strings or numbers?
When we define a domain concept such as currency amount as a double or a Social Security Number as a string, we’re certainly asking for trouble. A ripple effect of incidental complexity flows outward from the moment we decide to define a domain concept as a simple type. Now we have to write unit and functional checks for all the client code that uses the double monetary amount or the string Social Security Number. Now our application is vulnerable to errors when a piece of client code “forgets” to call a validation method at the right time.
And what about those validation methods? They’re usually dumped into a general-purpose utility class full of static methods that aren’t logically or functionally related to each other. Now we have tight coupling without good reason as well as static method calls peppered throughout the code base. Generally, I consider the very existence of a static utility class to be a code smell. Replacing the static class with a singleton isn’t really a “solution.” Often, there’s a deeper design problem lurking somewhere.
So, have a look at the sample code here and see what you think. No doubt you can criticize the “good” examples as well as the “bad.” That’s fine, as it will improve the quality and usefulness of the examples.
I’m not picking on Java developers. There happens to be quite a lot of legacy Java code out there, so it’s a logical language to examine. I’ve seen plenty of existing code written in other languages, such as C#, Python, and Ruby, that has exactly the same sort of issues. It’s really a software design question, and not a language-specific question.