The Rule of Thrice: Know When to Build Generic Solutions in Code

Once is a fluke. Twice is a coincidence. Thrice is a pattern.

The Rule of Thrice: Know When to Build Generic Solutions in Code

Knowing when to build generic solutions is a long-standing problem in software, as this XKCD comic illustrates:

Code Lifespan: Surely (no one/everyone) will (recognize how flexible and useful this architecture is/spend a huge amount of effort painstakingly preserving and updating this garbage I wrote in 20 minutes)

Striking the right balance between "ya-ain't-gonna-need-it" premature generalization and "copy-paste" code re-use comes with experience.  It's part art and part science.  Over time, I've developed a rule of thumb that helps me make these decisions.  

I call it, "The Rule of Thrice."

The Rule of Thrice

The Rule of Thrice goes like this,

Once is a fluke. Twice is a coincidence. Thrice is a pattern.

To follow the Rule of Thrice, you don't spend any time generalizing a solution until the problem has appeared at least three times.

Benefits

  • Avoids premature generalization
  • Better defines the pattern
  • Reduces technical debt

Avoids Premature Generalization

The left side of the comic above makes this point better than I can:

Developer: It took some extra time to build, but now we can use it for all our future projects.
Narrator: How to ensure your code is never reused.

I've been that developer more times than I care to admit...

Better Defines the Pattern

When you write a procedure based on a single instance of a problem, you end up making a lot of assumptions about:

  • Which values will remain constant
  • Which values will change
  • What behavior needs to be dynamic
  • What conditions dictate branching in your code's logic

Waiting for multiple instances of the problem allows you to make fewer assumptions.  This results in cleaner code than if you were to slap on branching logic over time as the true nature of the problem revealed itself to you.

Reduces Technical Debt

I'm a backward-compatibility zealot.  

One of the big benefits of maintaining a code library is being able to import updates of the library into existing projects.  If you are constantly introducing breaking changes into your code library, then you will learn to avoid importing updates into your projects.  That's why backward compatibility is so important within your code library.

The big downside to maintaining backward compatibility is that early design decisions are very difficult to undo.  By waiting until you have at least three examples of a pattern, you can avoid making obvious design blunders that you will come to regret later.


Additional reading

Copying and Pasting Code with Purpose
The first commandment of software development is, “Thou shalt not copy and paste code.” Sometimes that’s wrong.

All original code samples by Mike Wolfe are licensed under CC BY 4.0