Edges in Software

Using Edge Cases as Focal Points for Design Choices

One of my favorite sayings is “If you take care of the corners, the room takes care of itself.”

For the longest while I thought it was something that Frank Lloyd Wright had said but I haven’t been able to find an attribution. Regardless, I think that the idea is sound. The edge cases in our software define it. Ideally, we shouldn’t have many of them, but when we do they should be there by design. Software without edges is simpler and often more robust but there are tradeoffs. Let’s look at integer arithmetic as an example.

Most languages use 2’s complement encoding to align with hardware. As a programmer you get 231-1 or 263-1 as your max value and you just hope that you don’t overflow. For the most part this works. In some critical situations we can trap overflow and underflow, but in typical programming it’s unlikely that we’ll exceed the bounds of our 2’s complement integer - but, it is an edge. Why do we have it at all? The short answer is: history. Or rather, history and performance.

I don’t want to make a general case for unbounded arithmetic. I'm just offering it as an example of an edge.

An edge can be something as simple as the beginning or end of a list, the presence of an explicit if-statement, or an exception throw. Edges are points of discontinuity. You can even see them at the user interface where they can take the form of bothering the user with a request for more input instead of handling an odd situation in a standard way.

Whenever we encounter an edge we should ask what our systems would be like if we didn’t have it. We don’t do this enough. Edge case elimination is a sort of juiu-jitsu. Sometimes we can finesse our implementation to remove an edge, and sometimes we can finesse a problem so that we don’t even need to encounter one in an implementation.

Occasionally when I float (no pun intended) the idea of unbounded integers, someone mentions that they can be a vector for a denial of service attack. As an unbounded integer grows so do the computational costs associated with it. Maybe that’s a good reason to impose an edge. But we have to decide whether bounding (the edge we impose) is important everywhere.

There's a lot to say about edges - they really are a convenient frame for our design choices.