The Elements of Code

Conclusion

In most professional domains, ... it takes a relatively long time for students to acquire the relevant knowledge and skills required for the profession.

K Anders Ericcson, Deliberate Practice

New violin students often develop bad technique that limits their potential progress; this effect is exacerbated if they lack formal training. Perhaps they can learn some of the simpler pieces, but as soon as they need to play very quickly their bow orientation or positioning makes it impossible. Perhaps they can play some slower pieces, but holding the violin improperly leaves them unable to use vibrato to create a rich and vibrant tone.

This is not exclusive to violin students, or even musicians. Anyone who has practiced a sport knows the same lesson: bad habits put a limit on performance.

Golf, an individual sport with a single goal and one metric, is one of the best examples of this. Every golfer knows that beginners can get lucky, and anyone with enough persistence will eventually put the ball in the hole. However, eventually putting the ball into the hole does not make one good at golf. Rather, practice and technique reduce the number of strokes it takes to accomplish the goal, and perhaps more importantly, help forestall injury while playing the game.

Improving technique involves undoing bad habits, and that process is difficult, tedious, and requires dedication. It requires the learner to commit to the endeavor and to think critically about not only their goal, but more importantly how they accomplish the goal. This often involves “relearning” the activity to undo bad habits. During the process of relearning, the learner will temporarily become worse at the activity.

The violinist training their bow technique will find they must play much slower. The golfer focused on their swing and stance will find the ball goes to unexpected places, at first. And the programmer learning to apply the rules in this book will initially take longer to write the same piece of code.

It is vital to recognize this is a transition phase. Output will decrease while new technique is learned, and coding may feel more difficult or cumbersome. But it is temporary; as technique improves, so do instincts, and eventually problems that once may have taken days or even weeks become trivial. Of course, it is not a panacea: there will always be difficult problems that plague programmers— no amount of technique can make Paganini’s 24th Caprice easy, and certainly there are at least as difficult problems that face us in software. However, eliminating problems of our own making allows us to focus on the challenge inherent in the specification.

Deliberate Practice

In 2004, Dr. Ericcson published a paper titled “Deliberate Practice and the Acquisition and Maintenance of Expert Performance in Medicine and Related Domains”. Specificity was clearly a virtue he admired.

In it, he covered the body of evidence that shows that continued, deliberate practice is the necessary ingredient for becoming an expert in a given field. Most people who learn a skill will stop as soon as they are merely proficient; they are able to accomplish the goal, so there is no incentive to get better. However, if one wishes to truly master the skill, they must have the drive and desire to continuously improve. The evidence suggests this drive is more important than any innate talent. The process of improvement is often a slow one. It requires “execution, monitoring, planning, and analyses of performance”.

The paper covers many disciplines, including that of computer science. In all cases, unless there is a drive to become better, not only will practitioners quickly hit the limit of “proficient amateur,” they will in fact become worse at the skill over time.

I have worked as a programmer for almost two decades. To my chagrin, even after I learned the rules outlined in this book, I continued to break them with scant justification. As a result, I have written bugs, inscrutable code, and overly complex logic. I have made working in projects more difficult not only for myself, but other programmers who must maintain the bad code I wrote.

The unfortunate truth is that we are constantly fighting problems of our own creation.

Software is similar to a building undergoing constant maintenance and additions, and yet much of it is treated as though the initial plan is all that will ever be needed. What happens when another story is requested? What about another three stories? Can the foundation support the load? Can additional structural support be added, or was the original construction such that it precludes any addition?

Following the rules we’ve covered in this book does not guarantee that the codebase will be correct for all possible future modifications, without changing existing elements. Instead, it provides a path to ensure that most of the time, most elements will not need to be modified, and if they must be, the process to do so is relatively painless. Critically, it creates software focused on understandability.

Of course, understanding how to write code properly is only one part of being a capable programmer. There is much more to learn about designing good software: from high-level system architecture to low-level project layout, from user interface design to language interface design; it all matters. Writing code that will stand the test of time, while critical, is only the beginning. As always, the most important element is to nurture the desire to learn and improve.

Whereas proficiency in everyday skill is attained rapidly, professional development ... is completed only after years or even decades of experience.

K Anders Ericcson, Deliberate Practice

And this is a wonderful thing, because surely we would quickly become bored otherwise.

The Road to Mastery

Unless he is certain of doing as well [as the masters], he will probably do best to follow the rules.

Strunk and White, The Elements of Style

Over the course of this book, we have covered ten rules that we can carry with us into future projects:

  1. Make your code read like a recipe.
  2. Avoid state mutations, and create variables close to where they are used.
  3. Avoid constructing new objects outside of bootstrapping and factories.
  4. Use polymorphism to make the program dynamic.
  5. When possible, use maps, registries, and polymorphism rather than conditionals.
  6. Names should reflect semantic mappings.
  7. Document why something is done, not what is being done.
  8. Write unit tests focused on contracts and complexity, not merely coverage.
  9. Learn and improve through refactoring.
  10. Practice.

The rules outlined in this book are foundational. They are the fundamental code construction skills necessary to build reliable, understandable, extensible software.

Eventually, following the rules will become instinctual. That is an important step, because it leaves you free to focus on the inherent complexity of problems, without introducing accidental complexity as you compose the solution.