A beautiful thing to say
A few people have told me I find odd things beautiful. Well, consistent with that character trait, here’s a colossal generalization about software development that I find beautiful.
“Programming is about establishing invariants, perturbing them, and then re-establishing them. It is a game of stepping-stone from one island of consistency to another.” – The Old New Thing
First off, this is a beautiful way of expressing what’s going on code. I like the echo of musical harmony, where you start at rest, then build tension, then release. I like the stepping-stone metaphor; you’re “in the air” and “unsupported” while you’re jumping from one consistent state to another. I like that it hints towards the difference between the code humans are capable of writing (and reading), and imaginable but currently not-feasible automatically generated systems, in which the invariants (stepping stones) are many and tiny, and of complicated shape; I have this intuition that there’s no reason other than maintainability-by-humans to hold design ideals of “define few invariants” and “define invariants that can be simply expressed”.
The more I think about it, though, the first sentence doesn’t match reality. Is “perturbing” an invariant similar to “violating” it? I guess not, since an invariant is a truth that holds universally over your model.
Letting go of your ideals
Imagine you have a model consisting of two variables, a and b, and an invariant that says “a + b = 0 at all times”.
If a starts out as –1, and b starts as +1; our invariant is satisfied. Assuming we can’t atomically modify both a and b in a single operation, we can’t change either a or b without violating the invariant. Even if the target state for the overall model satisfies the invariant (say a = –2, b = +2), there’s no sequence of operations we can apply without breaking the invariant on the way.
So we’re stuck; we have to weaken the invariant. Instead of preserving “a + b = 0”, we just preserve “a + b = 0 so long as the reader is the sole holder of a lock that governs read and write access”, say.
With this new, weaker invariant, we can take the read/write lock over a and b and make modifications, and then release the lock with a + b = 0, without ever having violated the invariant.
The new weak invariant hasn’t been violated. Has it been “perturbed”? No. At least, not on any interpretation I can come up with.
So where are we? We have the “ideal” invariant (which Raymond’s talking about):
And the “pragmatic” invariant (weakened so that we can actually mutate our model):
- a + b = 0 if you hold the lock.
Under this interpretation, of course it’s okay to violate (“perturb”, in Raymond’s terminology) the ideal invariant, since it’s just a convenient simplification to sit in a coder’s mental model. The real invariant is the weakened, pragmatic invariant, and it, by definition, is never violated.
Some coders work as if the ideal invariants actually hold
In the real world, probably the majority of coders I run into are of what I’d call the pragmatic-productive style. This style of coder tries approaches serially until something seems to work, and then, bang, moves on to the next business problem that needs solving.
Many (certainly not all) coders who work this way don’t “waste time” thinking about “pragmatic” invariants (e.g. “I need the lock if I want a consistent read of a and b”).
As a result, they write code that works under a single-threaded regime breaks horribly in multi-threaded land. I’m not talking about the incredibly difficult-to-eliminate “thread-safe code is almost impossible for humans to write” threading bugs, I’m talking about the trivial “this was never going to work from the moment two requests arrived at the web server within a second of each other” threading bugs.
There’s a lot of education/evangelism floating around geared towards educating this style of coder to embrace concurrency in a way that will produce code that works, as far as possible.
Some coders work as if invariants don’t matter at all
My bigger concern is that many coders don’t really think in terms of invariants much at all.
They’ll know that each row in your OrderLine table has to belong to a row in the Order table, sure.
But they won’t be able to explain what consistency rules they’re applying to the OrderLineHistory temporal table (with effective start date and effective end date) which they just created and populated. These consistency rules are invariants, and no-one can consume (read) the model without making assumptions about those invariants. It’s the responsibility of the designer to determine those invariants, and set about satisfying them in the code that’s written, communicating them to the people who depend upon the model, and enforcing them wherever feasible.
But, as I say, pragmatic-productive coders tend not to be very interested in this stuff.
As a tangent, invariants are great assertion-fodder, and it can be nice when writing unit tests to also implement a quick re-check of any assertions that have no framework-provided safety-net. This consistency check can be run after each test to verify that in addition to any directly expected behaviour your test was written to assert, the code under test doesn’t result in an inconsistent model.
They may or may not be smited. Smoten? Smitten?
Anyway, the result of ignoring invariants is systems that are difficult to describe, reason about, implement, test, and maintain. If you can’t tell me what invariants hold over the model you just designed, then you haven’t actually designed a model yet. And code that’s built to manipulate a model whose invariants haven’t been thought through will be a super-happy-fun-time fountain of bugs, keeping everybody entertained all the way up to a delayed acceptance test.
And that’s why pragmatic-productive coders, who are all about the time-to-feature and delivering-business-value (neither of which is bad in itself) should care about invariants: because ignoring them increases time-to-feature and delays delivery of business value.