Small details matter because you deal with them often. Any enhancement you make thus yields a benefit often, hence a bigger overall benefit. In other words: invest small care, get big return. This is an irresistible proposal!
Examples of small design-level details that I care about because I have experienced great payback from them:
All these details emphasize that code is written once then used many times. The extra care at time of writing pays back at time of using, each time, again and again. Each enhancement that minimises brain effort at time of use is welcome, because software design is a matter of economy.
Other kinds of “details” that I care about involve the human aspects of crafting software: being on site, face-to-face communication rather than electronic media, respect and consideration at all times, always celebrate achievements, etc. Because ultimately, it also boils down to people that feel like building something together.
The concept of degrees of freedom looks so relevant to software development that I am wondering why it is not considered more often. Fortunately Michael L. Perry dedicates a full section of his blog to that concept. In this post I will quote a lot, please consider that as a sign of enthusiasm.
A mathematical model of a problem is written with equations and unknowns. You can think of the number of unknowns as representing the number of dimensions in the problem.
Depending on the number m of equations compared to the number n of unknowns (variables), there are several cases:
m > n: there is no solution, the problem is over-constrained
m = n: there is only one solution
m < n: the system is under-determined system, and the dimension of the solution set is usually equal to n ? m, where n is the number of variables and m is the number of equations.
A common concept in mechanics
The concept of DOF is prevalent in mechanics. In particular, a system with more internal constraints than the total possible number of DOFs has no solution. However in practice it can still work, provided some of the bodies are not absolutely rigid.
The table is often given as an example, because it only needs three legs to be stable on the ground, but usually has 4 legs. This only works because the table is not fully rigid, and can accommodate the small imperfection of the ground.
Not yet common in software
In his post, Michael L. Perry explains in practice how to analyse software using DOF. First find the unknowns:
To identify the degrees of freedom in software, start by defining the unknowns. These are usually pretty simple to spot. These are the things that can change. In a checkbook program, for example, each transaction amount is an unknown, as are the the account balance and the color used to display it (black or red).
Then find out the constraints between the DOFs.
Next, define the equations. These are the relationships between the unknowns that the software has to enforce. In the checkbook, the balance is the sum of all transaction amounts. And the color is red if the balance is negative or black otherwise.
Subtract to find your degrees of freedom. One amount per transaction (n), one balance, and one color gives n+2 unknowns. The balance sum and the color rule give us two equations. n+2-2 = n degrees of freedom, one per transaction.
Quoting again Michael L. Perry (across various posts in the DOF category):
Understanding the degrees of freedom in the software helps to create a maintainable design.
Adding independent data to a system increases its degrees of freedom. Adding dependent data does not. Adding an immutable field does not.
You want no more degrees of freedom in the system than the problem calls for.
The concept of degree of freedom is remarkably useful to help distilling the domain down to the essential variable parts and the constraints between them. Any extra independent data can only create opportunities for bugs.
Many concepts look obvious because they are used often, not because they are really simple.
Small quantities that we encounter all the time in most projects, such as date, time, price, quantity of items, a min and a max… hardly are a subject of interest from developers “as it is obvious that we can represent them with primitives”.
Yes you can, but you should not.
I have experienced through different projects that every decision to use a new value value object or to improve an existing value object, even very small and trivial, was probably the design decision that yields the most benefit in every further development, maintenance or evolution. This comes from the simple fact that using something simpler “all the time” just makes our life simpler all the time.
As a reminder, value objects are objects with an identity solely defined by their data. Using value objects brings many typical benefits of Object Orientation, such as:
One value object often gathers several primitives together, and one thing is always simpler that a few things
The value object can be fully unit-tested: this gives a tremendous return, because once it is trusted there will simply be no bug about it any more
Methods that encapsulate even the simplest expression with a good name tells exactly what it does, instead of having to interpret the expression every time (think of isEmpty() instead of “size() == 0”, it does save some time each time)
The value object is a place to put documentation such as the corresponding unit and other conventions, and have them enforced. Static creators makes creation easy, self-explanatory, can enforce the validations and help select the right unit (percent, meter, currency)
The method toString() can just tell in a pretty-formatted fashion what it is, and this is precious!
The value object can also define various abilities, such as being Comparable, being immutable, be reused in a Flyweight fashion etc.
And of course, when needed it can be changed to evolve, without breaking the code using it. I could not imagine a team claiming to be really agile without the ability at least to evolve its most basic concepts in an easy way.
The first time it takes some extra time to build a value object, as opposed to jumping to a few primitives; but being lazy actually means doing less in the long run, not just right now.
Patterns represent a couple (intent, solution), where the intent matters most. Based on that intents, that can be generic or specialized, I propose to consider patterns like types in languages with strong typing, for the compiler to enforce their constraints.
If we had an explicit way to mark the use of the pattern in the code, and if the compiler was able to know about this pattern, then it could enforce all the above. Moreover, it could also generate corresponding unit tests to assert the dynamic and runtime aspects constrained by the pattern.
This approach can be considered similar to the typing used in programming languages, where the compiler can enforce many constraints according to the type declarations given by the developer.
In current languages there is no explicit way to declare the use of patterns in the source code. The reader of the source must pay attention to various hints and compare to his/her prior knowledge to decide whether or not a pattern is used. Typical hints include naming conventions, where the class or interface names ends with the name of the pattern participant: an interface TradeFactory is probably a Factory that creates Trade instances.
Since many years I have been using a custom Javadoc tag @pattern to explicitly mark the patterns I use in my source code, hoping that a tool could use it in the future. Over time I have noticed several benefits from doing that, the biggest one being that I am forced to be 100% clear about what I want. Many times I noticed that asking myself to explicitly declare in patterns what I was doing required me to think deeper. Though I know what I am doing, when trying to express that intent using patterns from the books I am forced to clarify my intent to decide which exact pattern I am applying. This is fully in the spirit of the Pragmatic Programmer‘s “Programming by Coincidence“, and also in the spirit of Test Driven Development where the unit test is forcing you to clarify what you really want.
In other words, we could call that Pattern Driven Development. PDD, yet another acronym!
It may not sound intuitive that there exists a documented pattern for each and every possible design decision, but I can confirm that the panel of existing patterns is very wide and already covers a lot of common design decisions. Such patterns are far from sophisticated solutions or tricks, they usually just name intents and known practices. But this is exactly what we need. Dirk Riehle, in a recent paper entitled Design Pattern Density Defined, found that in the case of open-source frameworks, and considering only classic design patterns, the density of such patterns was between 40 and 70%, where density is defined by “The design pattern density of an object-oriented framework is the percentage of its collaborations that are design pattern instances.”
In addition, it becomes increasingly common for software teams to use custom patterns as a convenient way of documenting their common design practices. Actually this is how the pattern story began when Kent Beck and Ralph Johnson decided to document the design of HotDraw in the now classic paper: Patterns generate architectures. To illustrate that, the Drupal team is using custom patterns in addition to standard patterns to document their design in an efficient way, as described in the program of the DrupalCon Paris 2009 conference:
This session will cover the how and why of design patterns, and review the most common patterns used in Drupal as well as a number of common patterns we could be using. Some are Drupal-specific patterns and some more more general software design patterns.
Another major example of patterns dedicated for a particular project is the the Eclipse IDE. Erich Gamma, the main Eclipse designer -one of the Gang of Four- wrote the book Contributing to Eclipse: principles, patterns, and plug-ins(together with Kent Beck) where Eclipse-specific and standard patterns are used together as the primary mean of documenting the design.
Patterns are signs to denote intents, and I propose to promote the use of patterns (patterns already existing or to be defined in the context of a particular project) to trace these intents explicitly, and to enable tools to enforce pattern-specific constraints.