Coding in denial

To write an application that works, you must handle unexpected conditions.

Once I worked on a Java system that had been in development for several years. After my new code had no effect, I was horrified to find

try {

  // do lots of stuff, including run my code
  
} catch (Throwable th) {
}

In Java, catching Throwable intercepts all possible application exceptions and all possible JVM exceptions, including OutOfMemoryError and internal JVM errors. This program caught a Throwable, ignored the exception and continued as if nothing was wrong. If we were at a 12-step meeting, we would call that denial.

The rationale behind this particular case was that they didn’t want the user to see exceptions. I like to imagine that technical support call.

Customer: When I press the button, nothing happens.
Tech Support: Do you see an error message?
Customer: No.
Tech Support: Look in the log.
Customer: Nothing there.
Tech Support: Try rebooting and call us back.

Denial is a way to cope with stress, but in software it is particularly unhealthy. Masking the exception slowed down my development. Instead of getting the error immediately, I had to step through the debugger, which is much slower. When this software went to testing, the QA engineers could miss the fact that anything unexpected happened. By the time the customer finds it, he has no information to help you debug.

Recommendations

To write software that works, I try not to live in denial. I make all unhandled cases explode spectacularly so they get fixed right away.

For most conditions I don’t expect, I throw some kind of RuntimeException. This includes situations in the code that are not yet implemented. For example:

void solveGlobalWarming() {
  throw new UnsupportedOperationException("not yet implemented");
}

If anyone calls that method, he will know immediately that it doesn’t do anything. If an exception becomes bothersome, then I write code to handle that situation. The key is to make situations I do not expect to blow up spectacularly.

I try to capture all exceptions and write them to our error log with as much information as possible. For example:

  • stack trace
  • timestamp
  • username of the user
  • browser version
  • request URL and parameters
  • SQL query being executed

I find it helpful to log the errors and warnings twice, once in the regular log and again in a log of just the errors and warnings. Regular logs can grow quite large, especially when a large failure is underway. Writing the errors and warnings out separately gives me a much smaller log to check for problems.

If the exception happened to an end-user, I like to display an error message to the user. This helps the errors be found during QA and beta testing. It also prompts customers to report there is a problem so it gets addressed. I understand not wanting to show a stack trace to a customer. That’s fine. On the error page, I often put additional information in HTML comments. Then the user can see the information by viewing the HTML source.

Finally, I like to have the system automatically email the development team for every error. I have been in situations where I received that email and had the problem diagnosed before the customer called a few minutes later. I also have had the pleasure of calling customers proactively and saying, “You got an error yesterday when you did X. I wanted to let you know that problem is now fixed.”

Conclusion

Don’t live in denial. Admit you make mistakes. Writing software that works is hard.

The sooner bugs are uncovered, the sooner they get fixed.