Coding tip #2: fail fast, don’t fail, don’t worry

Facebook recently pulled the plug on one of their data centers…
On purpose.

The idea was to investigate how well they could recover from live failures.

We developers, and beginning developers especially, sometimes have this weird notion that code should be perfect and withstand any storm. Truth is, something can and will always go wrong at some point in time, and we should stop fearing it.

The first, most noticeable form of something going wrong, is an exception that’s being thrown. Beginning developers will often shy exceptions. They’re cryptic and are more likely to happen in the middle of a demo than while developing…

Fail fast

Hence, out of fear of introducing new exceptions by actually throwing one, beginning developers start writing code as:

public object getValue(string key){
  if(key == "CurrentUser")
    return SomeContext.User.Name;
  if(key == "CurrentTeam")
    return SomeContext.Team.Name;
  return "Not found";
}

Peaceful, right? No matter what value you ask for, no exception shall ever leave this method.

The unfortunate thing here if the calling logic is flawed somewhere, you might only find out much, much later in the process.
The above piece of code is called by some EmailTaskPreparer, which retrieves “current_user” to create an instance of a task. That task is put on a queue, one hour later a worker process picks it up and processes it by getting the current user’s email address, then sending an email.
One day later, you get a bug report there are undeliverable emails hanging around the system and you get to embark on the pleasant adventure of backtracking every possible piece of code that is sending emails, putting email-tasks on queues, and how they build those tasks.

The key lesson is: fail fast. If something is wrong, throw an exception on the spot instead of returning a peaceful default value.
The calling logic will still be just as flawed, but at least now you end up with a bug report stating that an InvalidKeyArgument was thrown when the EmailTaskPreparer called ‘getValue’, which will be easy to find, fix and will give you more time to actually get some real work done.

Don’t fail

Obviously, learning to code is all about understanding to take everything in moderation. The next rule of fist is to understand that exceptions are for ‘exceptional situations’ only.
When you have an abundance of exceptions being thrown all over your code, you’ll soon end up with a lot of try-catch blocks, and eventually you’ll end up with a code base that has two new problems:
– the code becomes less readable (at the bottom of your try block are a bunch of alternative code paths that make your logic harder to follow)
– the code becomes slower (the compiler can do less optimizations because it needs to make sure it can handle your expected exceptional code pathing)

To address the first, consider adding logic to your classes that can pre-approve an operation. This is why an iterator has a ‘hasNext()’ function, a command has a ‘canExecute()’ function: you can ask if you should expect something to go wrong, and decide on how to handle that on the spot, instead of 100’s of lines lower in a catch=block. It’ll make your code much more readable. Don’t fail if you could have avoided it.

Don’t worry

Finally, there are very little use cases to actually catch an exception. If you take the two previous rules in mind, exceptions will only occur when something really unexpected happens. In literature, exceptions are considered ‘final’ (the code below where you throw an exception will not execute) because they signify the system has entered a state from which recovery is not expected to be possible and execution should not continue.
Hence, if an exception occurs that you could not possibly have avoided and there’s no way you can recover from it, why bother catching it?
Don’t worry. Really, you should only really catch exceptions in a very limited couple of cases:
– you could not avoid it (no ‘hasNext’, ‘canExecute’, etc) but you still know how to recover from it For example: reschedule the task for later execution
– you want to hide exception details: a general catch-all block that catches any exception, logs it, and throws a new exception that hides any internals specific to the current layer of your application. For example you can should the SQL exception (“Connection failed for user “Bob” with password “Bob123″), only to throw a new generic DatabaseOperationFailedException.*
Beyond the above use cases and a couple of others, catching and handling exceptions should not be a part of the majority of your code base.

Don’t worry, all systems will fail at one point or the other, just try to make sure that when it fails, you’ll have a precise stacktrace and clean codebase to help you trace their cause (or, that you know how to plug a data center back in).

Advertisements

Coding tips #1: tell, don’t ask

As part of a new assignment, I have the privilege of working with a new team. Hey teamies! One of the things the team and I really want, is to make sure our code is clean and manageable. Hence, I’ll be putting up a coding tip once in a while on how you can improve your code for readability and maintainability.

Tell, don’t ask, is a reminder that you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do (source).

In the LightSwitch world, entities are our models. All too often we are tempted to write code that asks the models about their state, then we take decisions about that state. For example:

if(currentCustomer.Orders.Any(c => c.ClosingCode.Code == "1209" || c.ClosingCode == "1304")
{
  //Preferred customer, add discount
  //More code here...
}

This works beautifully. It’s fast to write, any other developer can clearly read what’s going on, and it executes in 0ms flat (give or take a few ticks).

There’s a couple of issues with this type of coding though… First of all, how about readability? Can any developer clearly read what’s going on? Well, read: yes. Understand: no. If he/she does not know what these closing codes mean, he/she’s shit out of luck.  Secondly, there’s a maintenance issue. What if a closing code changes? What if a third closing code is added that makes a customer preferred, or one is removed? What if we want to support multiple tenants, and for one tenant closing code “1209” makes the customer preferred, but for another one it doesn’t?

In all of these likely cases, we’d have to loop over our entire code base and find all the spots where we ‘ask’ the object about its state, and then take actions accordingly, and update that code…

We can get a few quick wins in maintainability and readability by adding a new field to the ‘ClosingCode’ entity:

if(currentCustomer.Orders.Any(c => c.MakesCustomerPreferred)
{
  //Preferred customer, add discount
  //More code here...
}

It seems like just a small change, but adding this field to the ClosingCodes really improves readability (as a new developer, I don’t have to know about the actual codes), we can add/edit/delete closing codes that make a customer preferred, and we can support different closing codes for multiple tenants.

Almost there though: our code still has the hard-coded idea that a customer can only be preferred based on closing codes. What if one day we also want to make a customer preferred based on other parameters, like ‘simply being a nice person’?  This additional refactoring requires you to take a decision though: refactoring takes time, and time costs money. Are these scenarios likely to happen, or are they more of a ‘one day it’d be nice to have this’ (YAGNI – you ain’t gonna need it).

If there is a very real chance that these scenarios might happen, you can further reduce the complexity of the code by moving the ‘IsCustomerPreferred’ state into the Customer object, instead of keeping it hidden inside the OrderClosingCode table:

if(currentCustomer.IsPreferred)
{
  //Preferred customer, add discount
  //More code here...
}

Obviously, you’ll need additional code, for example when an order is closed, to tell the customer it is preferred or take that status away.

 

Keep rocking LS!

 

Jan