Weeks ago I wrote an article on how a particular rule of thumb misses the point when it comes to tests. The article spawned comments on Twitter and other media; some people agree with its contents and some others think I’ve misinterpreted the rule. You can read the full text to draw your own conclusions, but one thing that caught my attention is how some people follow rules of thumb as if they were laws given to us by some deity.

Wikipedia says that rules of thumb are “a principle with broad application that is not intended to be strictly accurate or reliable for every situation”. In a world where people think they have no time to read a 300 pages book we are seeing more and more authors publishing material as a compilation of quick rules.

I do a lot of coaching gigs and rules of thumb come in quite handy for those. They work just fine for people in what is described in the Dreyfus model of skill acquisition as:

STAGE 1: NOVICE

Normally, the instruction process begins by decomposing the task environment into context-free features which the beginner can recognize without benefit of experience. […] The beginner is then given rules for determining an action on the basis of these features. To improve, the novice needs monitoring, either by self-observation or instructional feedback, so as to bring his behavior more and more completely into conformity with the rule.

In coaching gigs you often find pressure to enable the team and deliver. Defining a set of rules of thumb helps going faster. Feedback comes from pairing and static analysis tools.

The drawback is that it is very likely that it will create a cargo cult mentality. The coach leaves the project but the rules and the draconian checkstyle configuration stay.

When I started leading and coaching teams I had this problem all the time. I would work with a team in one or more successful releases and then leave the project. A while later a look at the code base would show me that they were still thinking about code this way:

public void run() {
    line = parseLine(line);
    if (line.length()  0) {
        try {
            Blah blah = new Blah (line);
            if (BlahType.SENDER == blah) {
                onSender(blah);
            } 
            if (BlahType.FOO == blah) {
                onFoo(blah);
            } 
            if (BlahType.BAR == blah) {
                onBar(blah);
            } 
            if (BlahType.STATUS == blah) {
                onDirect (blah);
                logger.info("Received event: " + blah);
            }
        } catch (Exception ex) {
            onException(ex);
        }
    }
}

The only thing that the rules and checkstyle configurations I left them with would do was require the code above to be written like this:

public void run() {
    line = parseLine(line);
    if (line.length()  0) {
        try {
            Blah blah = new Blah (line);
            run1(blah);
            run2(blah);
        } catch (Exception ex) {
            onException(ex);
        }

    }

    private void run1(Blah blah){
        if (BlahType.SENDER == blah) {
            onSender(blah);
        } 
        if (BlahType.FOO == blah) {
            onFoo(blah);
        } 
    }

    private void run2(Blah blah){
        if (BlahType.BAR == blah) {
            onBar(blah);
        } 
        if (BlahType.STATUS == blah) {
            onDirect (blah);
            logger.info("Received event: " + blah);
        }
    }

Instead of trying to reduce complexity or creating a better model those tools just led developers to play an annoying beat-the-static-analysis-tool game.

At first I gave up on rules of thumb and checks altogether. After a while I realised that the problem wasn’t necessarily in having those rules, but in how I was presenting them. The team didn’t think of the rules as guidelines, they thought of them as condensed wisdom they were not supposed to challenge.

At some point I acquired a copy of Arthur J. Riel’s Object-Oriented Design Heuristics, a book full of rules of thumb. In the preface, though, he says:

[…] I refer to these 60 guidelines as “heuristics,” or rules of thumb. They are not hard and fast rules that must be followed under penalty of heresy. Instead, they should be thought of as a series of warning bells that will ring when violated. The warning should be examined, and if warranted, a change should be enacted to remove the violation of the heuristic. It is perfectly valid to state that the heuristic does not apply in a given example for one reason or another. In fact, in many cases, two heuristics will be at odds with one another in a particular area of an object-oriented design. The developer is required to decide which heuristic plays the more important role.

And that’s how I started presenting rules of thumb to novices. The rules are there just to question your design; they do not say what’s wrong or right.

A broken rule invites learning. When a developer thinks she needs to break some rule we get everyone together for some minutes and discuss what the rule tries to achieve and why this situation may be an exception. After this quick session I often try to find an article or book chapter on the topic and email everyone.

It’s not about following the rules. It’s not about breaking the rules. It’s about listening to them.