Sunday, December 13, 2009

Frontline story from programming bidding conventions

When working on bidding overcalls last night I had one of those moments where I had to pause and take a look back. I was working on implementing the following from Pavlicek's excellent online beginners lessons (lesson 7):

If your right-hand opponent opens the bidding one of a suit and you have a five-card or longer suit (excluding the enemy suit), you may be able to make a suit overcall.

This plain English text turns out to be very information-dense.  The first part of the sentence, "If your right-hand opponent opens the bidding one of a suit," implies that one of the following bidding scenarios took place:
  • ONE-SUIT
  • PASS, ONE-SUIT
  • PASS, PASS, ONE-SUIT
 When trying to get that in code, I ended up quickly in a tangled web of statements like


if (bidCount==1 &&
    getCalls().get(0).getBid().getTrump().isSuit() &&
    getCalls().get(0).getBid().getValue() == 1
   ) {
     return true;
}


... and that was just the simplest case. Ever since I read in Kerevsky's book a story about Ward Cunningham's writing of a date constructor as november(20, 2005)[1], I have been a lot more careful about writing readable code even in obscure places. I rewrote the above a few times, and finally settled for the following:

public boolean mayOvercall() {
  if (bidCount == 1) {
    if (is1Suit(0)) {
      return true;
    }
  } else if (bidCount == 2) {
    if (isPass(0) && is1Suit(1)) {
      return true;
    }
  } else if (bidCount == 3) {
    if (isPass(0) && isPass(1) && is1Suit(2)) {
      return true;
    }
  }
  return false;
}

private boolean isPass(int i) {
  return PASS.equals(calls.get(i).getBid());
}

This is nothing terribly fancy, no domain specific language, just some appropriately named methods. The whole experience took about half an hour, and if you go back to the original specification, it is much clearer now why Gnubridge is still far from supporting Standard American bidding convention. Human language, especially when spoken by an expert, is incredibly dense. This is just an illustration of what has been a common experience to me in translating from user stories to code.

Another realization I had was how much easier it would have been to just go ahead and code it into a convoluted web of nested IFs and method calls. This was a small piece of a small bidding rule, and my time on the project is limited. I'm sure Gnubridge code is littered with this sort of unreadable garbage, and it was just a lucky call that made me rewrite it somewhat clearer. It seems like combating this sort of unreadable code is an uphill battle. I know we have coding standards, static analysis tools, peer review, etc. at our disposal to maintain code quality. And yet, based on this example, the code seems to naturally tend towards a chaotic mess. The expressive and readable code that comes from proper division of responsibilities is just an ephemeral occurrence, an unstable equilibrium about to roll back into chaos soon after attentive eye of a developer focuses away.


[1] Kerevsky, Refactoring to Patterns, chapter 2

No comments:

Post a Comment