I develop by some general principles. One of the strongest is “generous on input, strict on output”. This means that the code should be tolerant of variations in it’s input but should be strictly correct when outputting data. This ranges from the small to the large.

As an example, when you accept a boolean as a string, ignore the case and allow it to be expressed as “1, 0, T, F, Y, N, TRUE, FALSE, YES, NO, etc.”. If it’s not anything you’re prepared to see, assume it’s false. But when outputting a boolean as a string, stick to one single convention and one single case religiously.

When you parse a custom format file, assume fields might be left out, or that they may be malformed. Attempt to “do the right thing” with the values you do get and set sane defaults.

Do you do this in every single routine throughout your code? No, you do it at the boundaries. But those boundaries can be hard to see. If you’re writing a Java library, you do it at the published interface points. If you’re writing PL/SQL packages, you do it on the non-local procedures and functions. If you’re writing anything that reads from the network or a file, that’s a boundary point. Anywhere your code will reasonably connect to code you didn’t write and data you didn’t create, is a boundary.

There’s another name for this: code defensively.

Assume the other programmers are wild anarchists who revel in destroying your code. Assume they don’t read your documentation and will call your routines simply by guessing what the parameters mean. Because that’s exactly what will happen.

This approach will save you a ridiculous amount of time dealing with defects both subtle and obvious.

But there’s a flip side to this. It’s called failing fast. You should attempt to be lenient with what your code accepts, but when basic requirements are validated, you should fail fast and clearly. Is that parameter absolutely required and can never be null? Test it right at the start and throw an exception that explains exactly what was wrong with the input. Don’t let the null percolate through the code until it causes a problem.

With these two techniques you’ll find that consumers of your code will have an easier time. You’ll find fewer defects raised. And you’ll find that your own maintenance of your code will be easier.