Original PDF Flash format fail-fast  


Fail Fast

design
E d i t o r : M a r t i n F o w l e r ■ T h o u g h t W o r k s ■ f o w l e r @ a c m . o r g
Fail Fast
Jim Shore
The most annoying aspect of software de- sent? A common approach is to return null
velopment, for me, is debugging. I don’t
or a default value:
mind the kinds of bugs that yield to a
few minutes’ inspection. The bugs I
public int maxConnections() {
hate are the ones that show up only af-
string property =
ter hours of successful operation, under
getProperty(“maxConnections”);
unusual circumstances, or whose stack traces
if (property == null) {
lead to dead ends.
return 10;
Fortunately, there’s a simple technique that
}
will dramatically reduce the number of these
else {
bugs in your software. It won’t re-
return property.toInt();
duce the overall number of bugs, at
}
least not at first, but it’ll make
}
most defects much easier to find.
The technique is to build your
In contrast, a program that fails fast will
software to “fail fast.”
throw an exception:
Immediate and
public int maxConnections() {
visible failure
string property =
Some people recommend mak-
getProperty(“maxConnections”);
ing your software robust by work-
if (property == null) {
ing around problems automatically.
throw new NullReferenceException
This results in the software “failing slowly.”
(“maxConnections property not
The program continues working right after an
found in “ +
error but fails in strange ways later on.
this.configFilePath);
A system that fails fast does exactly the op-
}
posite: when a problem occurs, it fails imme-
else {
diately and visibly. Failing fast is a nonintuitive
return property.toInt();
technique: “failing immediately and visibly”
}
sounds like it would make your software more
}
fragile, but it actually makes it more robust.
Bugs are easier to find and fix, so fewer go into
Imagine this method is part of a Web-based
production.
system that’s undergoing a minor upgrade. In this
For example, consider a method that reads
release, let’s say the developer accidentally intro-
a property from a configuration file. What
duces a typo in the configuration file, triggering
should happen when the property isn’t pre-
the error-handling code. For the code that returns
0 7 4 0 - 7 4 5 9 / 0 4 / $ 2 0 . 0 0 © 2 0 0 4 I E E E
P u b l i s h e d b y t h e I E E E C o m p u t e r S o c i e t y
I E E E S O F T W A R E
2 1

DESIGN
public class Assert {
public static void true(bool condition, string message) {
if (!condition) throw new AssertionException(message);
}
public static void notNull(object o) {
if (o == null) throw new NullReferenceException();
}
public static void cantReach(string message) {
throw new UnreachableCodeException(message);
}
public static void impossibleException(Throwable e, string message) {
throw new UnreachableCodeException(e, message);
}
}
Figure 1. An assert class in Java.
Most languages have built-in assertions,
but they don’t always throw exceptions.
They’re also usually pretty generic, limit-
1 public static void Main()
ing expressiveness and causing duplica-
2 {
tion. For these reasons, I usually prefer to
3 WriteCenteredLine(null);
implement my own assertion class, as Fig-
4 }
ure 1 shows.
5
However, it’s tough to know when to
6 public void WriteCenteredLine(string text)
add assertions. One way to tell is to look
7 {
for comments. Comments often docu-
8 int screenWidth = 80;
ment assumptions about how a piece of
9 int paddingSize = (screenWidth – text.Length) / 2;
code works or how it should be called.
10 string padding = new string(‘ ‘, paddingSize);
When you see those comments, or feel
11 Console.WriteLine(padding + text);
like writing one, think about how you
12 }
can turn it into an assertion instead.
When you’re writing a method, avoid
writing assertions for problems in the
Figure 2. A stack trace
a default value, everything will seem fine. But
method itself. Tests, particularly test-driven de-
that leads to a null
when customers start using the software, they’ll
velopment, are a better way of ensuring the cor-
reference (C#).
encounter mysterious slowdowns. Figuring it out
rectness of individual methods. Assertions shine
could take days of hair pulling.
in their ability to flush out problems in the
The outcome is much different when we
seams of the system. Use them to show mis-
write the software to fail fast. The instant the
takes in how the rest of the system interacts
developer introduces the typo, the software
with your method.
stops functioning, saying maxConnections
property not found in c:\projects\
Writing assertions
SuperSoftware\config.properties. The
A good example of the finesse needed to
developer slaps his or her forehead and spends
use assertions well is Assert.notNull().
30 seconds fixing the problem.
Null reference exceptions are a common
symptom of defects in my programs, so I’d
Fail-fast fundamentals
like my software to tell me when a null refer-
Assertions are the key to failing fast. An as-
ence is created inappropriately.
sertion is a tiny piece of code that checks a con-
On the other hand, if I used Assert.
dition and then fails if the condition isn’t met. So,
notNull() after every single variable assign-
when something starts to go wrong, an assertion
ment, my code would drown in a sea of useless
detects the problem and makes it visible.
assertions. So I put myself in the shoes of the
2 2
I E E E S O F T W A R E
w w w . c o m p u t e r. o r g / s o f t w a r e

DESIGN
luckless developer debugging the system. When a
1
public class Example
null reference exception occurs, how can I make
2
{
it easy for the developer to find the problem?
3
public static void Main()
Sometimes, the language will automatically
4
{
tell us where the problem is. For example, Java
5
FancyConsole out = new FancyConsole();
and C# throw a null reference exception when
6
out.WriteTitle(“text”);
a method is called on a null reference. In simple
7
}
cases, the exception’s stack trace will lead us to
8
}
the source of the problem, as in this example:
9
10 public class FancyConsole()
System.NullReferenceException
11 {
at Example.WriteCenteredLine()
12
private const screenWidth = 80;
in example.cs:line 9
13
private string _titleBorder;
at Example.Main() in
14
example.cs:line 3
15
public FancyConsole()
16
{
Here, tracing backwards through the stack
17
_titleBorder = getProperty(“titleBorder”);
trace leads us to line 3, where we see a null ref-
18
}
erence being passed into a method (see Figure
19
2). The answer’s not always obvious, but a
20
public void WriteTitle(string text)
few minutes of digging will find it.
21
{
Now consider a more complicated case,
22
int borderSize = (screenWidth – text.Length)
such as
/(_titleBorder.Length * 2);
23 string border = “”;
System.NullReferenceException
24
for (int i = 0; i < borderSize; i++)
at Example.Main() in
25
{
example.cs:line 22
26
border += _titleBorder;
at FancyConsole.Main() in
27
}
example.cs:line 6
28
Console.WriteLine(border + text + border);
29
}
Here, the stack trace leads to lines 6 and 22,
30 }
which doesn’t help at all (see Figure 3). The real
error is at line 17: getProperty() returns null,
causing an exception when _titleBorder is
Figure 3. A stack trace that leads to a dead end (C#).
dereferenced at line 22. The stack trace leads to
a dead end in this case—typical of code designed
to fail slowly—requiring tedious debugging.
public string toString(Object parameter) {
It’s this latter case that I want to prevent. I
return parameter.toString();
need the program to give me enough informa-
}
tion to find bugs easily. So for my code, I’ve in-
stituted the following rule of thumb: in most
(a)
cases, the program will fail fast by default, so I
don’t do anything special about null references
public class Foo {
(see Figure 4a). However, when I assign a pa-
private Object _instanceVariable;
rameter to an instance variable, the program
won’t fail fast without my help, so I assert that
public Foo(Object instanceVariable) {
the parameter is not null (see Figure 4b).
Assert.notNull(instanceVariable);
This rule of thumb could be helpful for
_instanceVariable = instanceVariable;
your programs, too, but the main point here is
}
the thought process I went through. When
}
adding assertions to your code, follow a line
of reasoning like the one I used for null refer-
(b)
ence exceptions. Think about what kinds of
defects are possible and how they occur. Place
Figure 4. An Assert.notNull() rule of thumb: (a) no assertion
your assertions so that the software fails ear-
necessary and (b) assertion needed.
S e p t e m b e r / O c t o b e r 2 0 0 4
I E E E S O F T W A R E
2 3

DESIGN
lier—close to the original problem—making
string result =
the problem easy to find. What kinds of prob-
file.readProperty(key);
lems are common in your code and how can
// assertion goes here
you use assertions to make them easy to fix?
return result;
}
Eliminate the debugger
In some cases, a stack trace is all you need to
You could write the assertion message in sev-
find an error’s cause. In other cases, you must
eral different ways. One possibility is
know the contents of some variables. Although
you can find the variable data by reproducing
Assert.notNull(result, “result was
the error in a debugger, some errors are hard to
null”);
reproduce. Wouldn’t it be better if your pro-
gram told you exactly what went wrong?
but that merely repeats the assertion condition.
When writing an assertion, think about what
Another possibility is
kind of information you’ll need to fix the prob-
lem if the assertion fails. Include that informa-
Assert.notNull(result, “can’t find
tion in the assertion message. Don’t just repeat
property”);
the assertion’s condition; the stack trace will
lead to that. Instead, put the error in context.
which gives some context but not enough to
For an example, we return to our configu-
eliminate the debugger.
ration file reader:
A better assertion is
Figure 5. Global error
public string readProperty
Assert.notNull(result, “can’t find
handler for a C# batch-
(PropertyFile file,
[“ + key + “] property in config
processing system.
string key) {
file [“ + file + “]”);
which gives just the right amount of
public static void Main() {
information.
try
You don’t need to go overboard
{
when writing assertion messages.
foreach (BatchCommand command in Batch())
Assertions are for programmers, so
{
they don’t need to be user friendly,
try
just informative.
{
command.Process();
Robust failure
}
Failing fast seems like it could re-
catch (Exception e)
sult in pretty fragile software. Sure, it
{
makes defects easier to find, but what
ReportError(“Exception in “ + command, e);
about when you deploy the software
// continue with next command
to customers? We don’t want the ap-
}
plication to crash just because there’s
}
a typo in a configuration file.
}
One reaction to this fear is to dis-
catch (Exception e)
able assertions in the field. Don’t do
{
that! Remember, an error that oc-
ReportError(“Exception in batch loader”, e);
curs at the customer’s site made it
// unrecoverable; must exit
through your testing process. You’ll
}
probably have trouble reproducing
}
it. These errors are the hardest to
find, and a well-placed assertion ex-
private static void ReportError(string message, Exception e)
plaining the problem could save you
{
days of effort.
LogError(message, e);
On the other hand, a crash is
PageSysAdmin(message);
never appropriate. Fortunately, there’s
}
a middle ground. You can create a
2 4
I E E E S O F T W A R E
w w w . c o m p u t e r. o r g / s o f t w a r e

DESIGN
global exception handler to gracefully handle
unexpected exceptions, such as assertions, and
Bugs add a lot of expense and risk to our
projects—not to mention, they’re a pain in
bring them to the developers’ attention. For ex-
the neck to figure out. Since the hardest
ample, a GUI-based program might display
part of debugging is often reproducing and
pinpointing errors, failing fast can reduce de-
an unexpected problem has occured
bugging’s cost, and pain, significantly.
Furthermore, it’s a technique you can start
in an error dialog and provide an option to
using right away. Be sure to implement a
email tech support. A batch-processing system
global error handler so your overall stability
might page a system administrator and con-
doesn’t suffer. Search your existing code for
tinue with the next transaction (see Figure 5 for
catch-all exception handlers and either re-
an example).
move or refactor them. Then you’re ready to
If you use a global exception handler, avoid
gradually introduce assertions. Over time,
catch-all exception handlers in the rest of your
more and more errors will fail fast, and
application. They’ll prevent exceptions from
you’ll see the cost of debugging decrease and
reaching your global handler. Also, when you
the quality of your system improve.
use resources that have to be closed (such as
files), be sure to use finally blocks or using
statements (in C#) to clean them up. This way,
Jim Shore is the founder of Titanium I.T., a Portland, Ore., consultancy
if an exception occurs, the application will be
specializing in helping software teams work more effectively. Contact him at
returned to a fresh, working state.
jshore@titanium-it.com.
SET
wireless networks
INDUSTRY
gigabit Ethernet
enhanced parallel ports
STANDARDS 802.11 FireWire
token rings
IEEE Computer Society members work together to define
standards like IEEE 802, 1003, 1394, 1284, and many more.
HELP SHAPE FUTURE TECHNOLOGIES

JOIN AN IEEE COMPUTER SOCIETY STANDARDS WORKING GROUP AT
www.computer.org/standards/
S e p t e m b e r / O c t o b e r 2 0 0 4
I E E E S O F T W A R E
2 5