Enforce your invariants damnit

We have a problem at work. We’ve done the live release, and now all the bugs are rolling in, merrily slaying kittens as they go.

You know what I inevitably find they’re caused by? Well, other than multithreading, improperly tested clustering, those goddamn EJBs, Spring MVC… err. what was I saying?

Oh yes. They’re caused by improperly enforced invariants. Things which should be true and which you rely on being true but aren’t, because you never bothered to enforce them and someone fucked up.

Sure, we could just not fuck up. After all [insert meaningless excuse here]. Doesn’t work. Know why? It’s because you’re an idiot.

Yes. I mean you, personally.

Of course, so am I. Everyone is an idiot when at their worst, and do you really want to claim we all have perfect days? You’ve never had a hungover Monday or an exhausted Friday? People make mistakes. Any pretense that you can avoid this is asking for your project to fail. The correct response is to assume that everyone is going to make mistakes and include steps to deal with this.

So, how do I enforce my invariants?

The most common source of having too many invariants is redundant information. Choose properties which are orthogonal. For example, we have a boolean property on one of our objects, say ‘foo’, and a String bar. We failed to enforce the invariant foo == !(bar == null). But ‘foo’ is clearly a redundant property and the implementation of isFoo should simply have been { return !(bar == null); }. When you can do it this way, this is by far the best approach. It’s easy, it’s straightforward, and can potentially eliminate most of the invariants you need to preserve. Unfortunately the really hard ones to enforce don’t work this way.

So, how do we enforce the more complicated ones? For example, I have two properties foo and bar. Either of these may be null, but not both simultaneously (and both may be non-null). How do I enforce this?

We’re going to have to do it by inserting run time constraints. Compile time checking of invariants would be fantastic, but Java (and most similar languages) have piss poor support for this. You can probably manage some support with the annotation processing tool, but I don’t know how and I doubt it’s pretty.

So, runtime constraints. This means that any code which might cause them to break should check. Note that this applies only to constructors and methods on the object which mutate the state of the object. If these are all invariant safe then so will be any method which invokes them.

Key point in the above? If you don’t have any methods which mutate state, all you need to bombproof is the constructor. There really is no excuse for making mutability the default. It has so many disadvantages. In particular, JavaBeans loaded with a set method for every private field are the work of the devil. This also combines very well with the thread safety advantages of immutability. Imagine the following code:

if (myObject.foo == null)
{
// Some other nasty thread comes in and chnages foo to some non-null value and nulls bar.

doSomethingWhichCountsOnBarBeingNotNull(myObject.bar);
}

Boom. Nullpointer exception. Sure, you should be synchronising on myObject. This would remove the problem. But why introduce an extra step when you can avoid the problem altogether?

Anyway, suppose we really do want mutable state. How do we manage it? We still need to ditch those bloody set methods. In particular you cannot have a set method for both foo and bar on the above bean. Doesn’t work. Why? Because we need to be able to do “myBean.setFoo(null); myBean.setBar(someObject);”, which enforces the invariant perfectly well. Enforcing the order in which you do things would be a real nightmare, and for more complicated invariants would be flat out impossible. Methods which affect an invariant have to behave atomically with respect to the object, so need to be bundled together on the object. For example you could do “myBean.setFooAndBar(null, someObject)”. Not the best of examples, but you get the idea. The point is that you can guarantee that any combination of methods invoked on the object will enforce the invariant iff the invariant held at the end of each method invocation.

In fact, this is an example of why the anemic domain model antipattern is such a bad idea. In making your domain object nothing more than a mutable data structure which gets managed by other bigger objects you break encapsulation and scatter your manipulation code to the four winds, decentralising the code that needs to enforce the desired behaviour. Of course, taking it too far the other way can be dangerous too – if you have thousands of methods on your object which mutate state, you need to make sure every one of them is invariant safe.

Another good way: use the database. If your object model is backed by a database (a practice I consider questionable, but very common), insert every constraint you can at the database level. Databasi suck in many ways but they also have a lot of good points, enforcing constraints is something they’re very good at. Take advantage of this. In particular they’re very good for enforcing uniqueness constraints, which are much harder to do at the object level. The constraints that are easy to enforce at the object and database levels are fairly complementary, so using both will ensure a very high coverage.

Finally, sprinkle assertions and comments elsewhere in the code wherever an invariant is really important. In fact, I’ve just thought of a good way to do this – make your invariants public methods with a descriptive name. This means that your assertions can be very nicely structured like:

assert(myObject.fooAndBarNotBothNull());

These should of course not be necessary, but add a level of self documentation to the code, and will help catch any places you’ve failed to enforce the invariants as early as possible (and add no runtime overhead in the live environment, if you worry about such things).

If you follow all of the above advice then you will have eliminated a major source of bugs, and the kittens will thank you for it. Even if you only follow some of it, every bug caught early is a bug you don’t have to fret about later.

This entry was posted in programming and tagged , on by .

One thought on “Enforce your invariants damnit

  1. Pingback: A parable about problem solving in software development | David R. MacIver

Comments are closed.