Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • Define errors out of existence. Error cases are one of the primary sources of complexity in software; they create complexity both for the code that generates the error and for the code that must handle it, and they often result in subtle bugs where programmers forget to check for errors, resulting in runtime crashes when the errors occur. Instead, whenever possible, design code so that there is no error condition. For example, in the Tcl scripting language there is a command unset, which removes a variable. Unfortunately I made the mistake of implementing this command to throw an error if the variable doesn't exist ("why would anyone delete a variable that doesn't exist?"). However, it's fairly common for people to want to get rid of a variable that may or may not already exist; as a result, Tcl applications are littered with code that invokes unset inside a catch clause that ignores errors. In retrospect I should have defined unset to ignore nonexistent variables suddenly. This results gives the command behavior "make sure this variable no longer exists," which is simple and reasonable.

    Programmers often think that it's better to define as many error conditions as possible ("my code is really careful"), but this just creates a lot of complexity. I've even seen cases where methods require additional arguments that serve no purpose in the method except to allow for additional error checking; once the error checking was completed, the arguments are ignored! I would argue the opposite: design code with as few error conditions as possible. Wherever possible, design abstractions so that every possible combination of inputs is meaningful and reasonable: make your abstractions just "do the right thing". Or, said another way, handle as many errors as possible locally, but export as few errors as possible (this reminds me of the classic license plate sticker "think globally, act locally").
  • Think of classes as martyrs. The best classes are those that bring suffering on themselves to make the world a better place for everyone else. It's okay if a class is complicated internally, as long as it presents a really simple and powerful interface to the rest of the world. Of course, it's even better if you can figure out how to decompose the class internally so it's not so complex. But if complexity is inevitable, better to encapsulate it in one class rather than spread the complexity across a large number of callers. Or, said another way, push complexity down, not up.

...