Don't invent unnecessary requirements

When designing a system, write down the requirements and brutally inspect each one. Make sure each one is necessary. If you find yourself designing for requirements that aren’t on that list, add them to the list and verify with others that they’re really necessary. It’s too easy to implicitly and silently assume requirements that no one has asked for and no one needs, and every requirement boxes you in when devising solutions or pushes you to unnecessarily complex solutions.

Let’s say, as an extremely simple example, but one that I saw first hand, that you need to convert a double to a float. You might write down these obvious requirements:

You might then go write this code:

public float doubleToFloat(double d) {
    if (d < Float.MIN_VALUE || d > Float.MAX_VALUE) {
        throw new IllegalArgumentException(
            "Double " + d + " doesn't fit in a float");
    }

    return (float) d;
}

What just happened? You silently added another requirement:

But is that really necessary? Who asked for that? What are we storing in these doubles anyway? If it’s just time values on the order of seconds, then it’ll always be in range. And what happens when you cast something out of range, anyway? Turns out the float gets the value of infinity or zero, as appropriate. Maybe that’s actually better, we should ask around. And are we okay with the speed penalty of the check?

You might think that it’s easier to just write the check instead of going through the trouble of asking around about this extra requirement, but remember that every line of code is a potential bug (see Every Line Is a Potential Bug). And sure enough, your range check above is wrong. Float.MIN_VALUE is the smallest positive value a float can have, not the minimum value. So two months later someone passes in -1 and the service shuts down in production. (This happened.)

Examining your silent requirement would probably have led you to the simpler and more correct solution in the first place:

public float doubleToFloat(double d) {
    return (float) d;
}

(Or just inlining the cast.) Most of the time that I investigate a system that has melted down, I find the bug in code that never needed to exist in the first place (like the range check above). These stem from unexamined silent requirements.