In 99.9% of cases, programmers spend most of their planning and initial coding creating the program that they want. They then spend twice as much time adding error-handling code to the original program, because they forgot that things can (and often do) go wrong.
This is “happy path” programming, or success oriented code.
We notice that, in happy path programming, there is exactly one path from start to finish. Because there are so few branches by default, most programmers write code in a way that makes error handling more complicated. The patterns they tend to employ include:
- Wedging all the functionality into one (main) function
- Assuming all functions terminate with perfect results
- Failing to create log outputs to track flow through the program
The problem with these patterns is that they’re unrealistic. Wedging all the functionality into the main function eliminates modularity, which reduces the efficacy of error checking. Assuming all functions are successful is a fool’s dream. And if you don’t create logs when things go right, you’ll never figure out how they went wrong.
The solution to all of this is to design with failures in mind.
Enforce modularity, because it limits the range of things that could go wrong at one time.
Check every input to every function, to make sure they aren’t nonsense (like writing 500 bytes to a single character pointer).
Use the if-else programming pattern to make functions internally modular as well as externally modular.
Create lots of logs. You want to keep records of everything that’s supposed to happen and everything that’s not supposed to happen. For every possible failure, attach a specific failure message that references the location of the failure in your code. Between these logs, you’ll know exactly what’s happening inside your program at all times.
If you create habits that revolve around all the things that can go wrong instead of what you expect to go right, you’ll vastly reduce your maintenance and debugging workload.