Combating the lava-layer anti-pattern with rolling refactoring

Mike Hadlow blogged about the lava-layer anti-pattern, describing, which I have ranted about in nearly every talk I do, the nefarious issue of opinionated but lazy tech leads introducing new concepts into a system but never really seeing the idea through all the way to the end. Mike’s story was about different opinions on the correct DAL tool to use, but none of them actually ever goes away:


It’s not just DALs that I see this occur. Another popular strata I see are database naming conventions, starting from:

  • tblOrders
  • Orders
  • Order
  • t_Order

And on and on – none of which add any value, but it’s not a long-lived codebase without a little bike shedding, right?

That’s a pointless change, but I’ve seen others, especially in places where design is evolving rapidly. Places where the refactorings really do add value. I called the result long-tail design, where we have a long tail of different versions of an idea or design in a system, and each successive version occurs less and less:

Long-tail and lava-layer design destroy productivity in long-running projects. But how can we combat it?

Jimmy’s rule of 2: There can be at most two versions of a concept in an application

In practice, what this means is we don’t move on to the next iteration of a concept until we’ve completely refactored all existing instances. It starts like this:


A set of functionality we don’t like all exists in one version of the design. We don’t like it, and want to make a change. We start by carving out a slice to test out a new version of the design:


We poke at our concept, get input, refine it in this one slice. When we think we’re on to something, we apply it to a couple more places:


It’s at this point where we can start to make a decision: is our design better than the existing design? If not, we need to roll back our changes. Not leave it in, not comment it out, but roll it all the way back. We can always do our work in a branch to preserve our work, but we need to make a commitment one way or the other. If we do commit, our path forward is to refactor V1 out of existence:






We never start V3 of our concept until we’ve completely eradicated V1 – and that’s the law of 2. At most two versions of our design can be in our application at any one time.

We’re not discouraging refactoring or iterative/evolutionary design, but putting in parameters to discipline ourselves.

In practice, our successive designs become better than they could have been in our long-tail/lava-layer approach. The more examples we have of our idea, the stronger our case becomes that our idea is better. We wind up having a rolling refactoring result:


A rolling refactoring is the only way to have a truly evolutionary design; our original neanderthal needs to die out before moving on to the next iteration.

Why don’t we apply a rolling refactoring design? Lots of excuses, but ultimately, it requires courage and discipline, backed by tests. Doing this without tests isn’t courage – it’s reckless and developer hubris.

Generic variance in DI containers