Bad Code Examples Promote Bad Habits

Why is there so much bad code? Developers offer myriad excuses:

  • We just have to ship (i.e. we don’t have enough time to do it right).
  • We disagree as to what “bad” is.
  • We know the code is not clean but we don’t think that it’s a real problem (i.e. we don’t care).
  • The existing design makes it tough to do the right thing.
  • We don’t have enough tests to clean the code up safely (“if it ain’t broke don’t fix it”).

Missing from that list is inadequate education. Most programmers learn from looking at other peoples’ code–whether in delivered systems, open source, internet examples, or book examples. A quick probe of the beginner’s forum at JavaRanch demonstrates how horribly constructed novice programs can get. Many novices simply won’t know better–they copy working code snippets from elsewhere and modify it to their needs.

Let’s shift at least part of the blame where it belongs–to the author of the bad code that the novice copied. Most beginning programming language tutorials and books don’t discuss or teach proper design. Examples frequently demonstrate rampant duplication, intertwining of console output with business logic, clever constructs, and cryptic naming.

Even authors who should know better present examples with severe design flaws. They could at least caveat them (“cleaning up the obvious duplication in this example is an exercise left to the reader”). The better solution would be to find a way to code it properly and not show the bad code at all.

I’ve been working through examples in a couple Erlang books. Here’s a paraphrasing of one unfortunate theme presented in both:

loop() ->
    receive
        {Client, "smelt"} ->
            Client ! "a small silvery fish",
            loop();
        {Client, "agile"} ->
            Client ! "lithe",
            loop();
        {Client, "lean"} ->
            Client ! "trim",
            loop();
        {Client, _} ->
            Client ! "no definition found",
            loop()
     end.

You don’t have to know Erlang to see the obvious duplication in this short example. Sure, it’s just an example, designed to show a construct, don’t worry about the design, yadda yadda. Yet it’s at the heart of why most of our code sucks. I know little about Erlang so far, but it sure makes a lot more sense to separate handling a server request from domain logic:

loop() ->
   receive
      {Client, Word} ->
           Client ! definition(Word),
           loop()
 end.

(If we’ve gotten far enough to learn about spawning processes, we’ve certainly learned how to build a clean definition function.) By presenting it properly, the duplication gets eliminated, and the example may more readily suggest that the server loop is a potentially reusable construct, or at least a common idiom.

By presenting it poorly, I can bet you a large amount of Erlang code out there is similarly difficult.

Why allow beginners to learn poor coding practice, then have to re-train them?

Posted in coding, programming | 3 Comments

Collaborative (Dare I Say Pair?) Writing

I enjoy writing, though I’m not as prolific as I’d like to be. I tend to re-read what I wrote many times, often second-guessing it. Like any writer, I also find difficulty from time-to-time, getting stuck on how to phrase a paragraph or a sentence. I’ll typically mark things and return to them later. All-in-all, I can slam out text, but getting polished product out the door can be challenging.

2004-02-29 Ball point pen writing.jpg

I read and enjoyed Weinberg on Writing: The Fieldstone Method when it came out. I already had been employing its core idea to an extent: capture thoughts as they arise (index cards work great!), then incorporate them as appropriate in an article or book. Each idea on a card might act as a central idea around which writing can build, or it might be used to fill in a “hole” in the text. Emphasizing this technique has made writing a lot more fun, and I tend to not lose great thoughts that had popped into my head.

My best writing experience, however, has been collaborating online with Tim Ottinger, originator and co-author on Agile in a Flash. We had a slow winter–after writing a pile of articles for PragPub, we both got very busy with work, and did little writing. We finally found some time to commit to delivering a new Agile in a Flash post, Is Your Unit Test Isolated?

Two days ago, we got our headsets on, started up a Skype conversation, and talked for a few minutes to agree on what we’d work on. We started some single-threaded editing on a very rough draft at Blogspot, but that wasn’t effective, so I pasted it all into a Google Doc.

Google Docs doesn’t provide the best document editor in the world, but it is highly effective for collaborative writing. You can see each others’ cursor and typing as it occurs, and you can look at offline revisions if necessary for comparison.

Mostly we’re not talking via Skype (and in fact sometimes we’ll collaboratively edit without a voice conversation at all). Instead we’re communicating through the document. Tim will start re-working a paragraph that I might have just finished writing, and I’ll get out of the way. Sometimes I’ll watch and toss in a comment–just typing it right into the document–and other times I’ll go off and stab at a paragraph that Tim wrote. Or I might go off and re-read the document, looking for flow and completeness problems. It’s pretty haphazard, yet it works, and it’s fast. We might go back and forth at a paragraph a few times before we figure it works.

Collaborative writing works best if you grow a layer or two of skin. We’ve learned to be pretty direct: “[[ I don't like that ]],” Tim might type (the brackets are our way of setting off comments). Ok! Good technical writing requires harsh critique. Better Tim drop a pointed comment than a few thousand readers.

At times I’ll return to find something I’ve written completely gone or gutted and rewritten. It was a little disconcerting at first, but I’ve learned to trust Tim’s reworkings. I can always look at the old revision if I thought an important idea got deleted.

The unit test article took us about an hour to get to 98%. We let a couple outstanding concerns sit for a few hours, made some offline changes, and published it the following morning (yesterday).

As with pair programming, I’ve found such highly collaborative pair-writing to go much faster than writing on my own, and produce a far more effective product.

Posted in distributed, index cards, pairing, remote, writing | Leave a comment

TDD for C++ Programmers

File:C plus plus.svgRecently I’ve been getting a good number of calls from C++ shops interested in doing TDD, despite my heavy Java background. I took on some of the business and had to turn away some to avoid being swamped. Many other folks I know (name dropping time!)–Tim Ottinger, James Grenning, JB Rainsberger, others–have also reported doing C++ work recently.

Is TDD finally taking hold in C++ shops? Does TDD even make sense for C++? I think so, and two current customers believe they’ve been seeing great benefits come from applying it. Building and delivering a C++ TDD course recently helped me come back up to speed in the language to the point where I was comfortably familiar with all of the things I hated about it. :-) It makes no sense to take such a difficult language and stab at it without the protection of tests.

I’ve been simultaneously writing more (after a typical winter writing freeze) and looking at Erlang–a much cooler language, challenging in a different kind of way. Meanwhile, my editor at PragProg has been asking for new book ideas. Here were some of my thoughts:

  • Refactoring 2012
  • Modern OO Design (not template metaprogramming!) / Simple Design
  • Object-Oriented Design “In a Flash” (card deck, like Agile in a Flash)

No matter how hard I try to run screaming from C++, there it is right behind me. It’s indeed a powerful language, and there is gobs and gobs of code written in it, and it’s about time we started trying to figure out how to make the best of it. It’s not going away in my lifetime. I also think C++ programmers are not well-served in terms of writings on TDD out there.

So… I decided it was going to be TDD in C++. Tim Ottinger and I put together and just sent out a proposal for a book tentatively named TDD for C++ Programmers (with a catchy subtitle, no doubt). We hope there’s enough demand and interest to get the proposal accepted. If all goes well, we’ll be soliciting reviewers in a few weeks.

I look forward to writing again with Tim! More in an upcoming blog post about our collaborative writing experience.

Posted in agile, C++, coding, programming, TDD, test-driven development, unit testing, writing | 2 Comments

Legacy Quadrants for Increasing Confidence Coverage

Your legacy system presents you with a choice: Put a stake in the ground and attempt to do something about the daily pain it causes, or let it get incrementally and certainly worse. Often its challenges seem insurmountable–small improvements appear like ripples in the ocean. Perhaps an investment to improve the software product is not worth the return in value, given its future.

Chances are that most of the time, though, you’ll be stuck with your legacy system for some time coming, and it makes sense to put a foot down to stem the degradation. Working Effectively With Legacy Code provides you with primarily tactical guidance for preventing entropy, focusing mostly on individual code-level challenges. The Mikado Method provides you with primarily strategic guidance, focusing mostly on managing large-scale refactorings across a code base (legacy or not).

In terms of where to put your effort, the best strategy is always to begin with the areas of current impact–what needs to change for the feature you’re currently tackling? But if you can afford additional effort to add characterization tests–i.e. increase “confidence coverage”–consider this quadrant that pits rate of change against defects for a given class.

Legacy Quadrants

How might you determine what classes belong in which quadrants? Tim Ottinger devised a heat map while at one shop, a simple Python tool that cross-referenced JIRA tickets with trouble tickets reported in Git. (I got to help build it!) You might consider filtering the defects to ignore those with low severity.

The Trusty Old Engine. ”If it ain’t broke, don’t fix it!” Classes that work and aren’t changing should be the last thing you worry about. Sometimes code in these areas is tempting: it may exhibit obvious duplication, for example, that a refactoring weenie might view as a quick-win opportunity. Don’t waste your time–there are bigger fish to try.

The Can of Worms. You should have very few classes in this quadrant–having lots of  stable classes that exhibit many defects suggests a reset on how your team does triage. Fixing these troubled classes might seem like opportunity to make your customer happy. Open the code up, however, and you’re likely to find fear-inspiring code. You might view this quadrant as a sandbox for ramping up on legacy code techniques: Classes here will provide you the time to experiment without having to worry as much about contention issues with the rest of the team.

The Mystery. Getting all the tiny bits correct in the face of rapid change is a significant challenge. A single, low-defect class with lots of change could suggest at least a couple things: The changes are generally trivial in nature, or the class rampantly violates single responsibility principle (for example, a large controller class that doesn’t simply delegate but also does work). With respect to the latter, perhaps you’ve been fortunate so far, or perhaps you have good test coverage.

It might be worth a small amount of effort to prevent any future problems in such an SRP-violating class. The nice thing is that splintering off new classes can be a fairly quick and risk-free effort, one that should begin to isolate areas of risk. You might then be able to code a few key characterization tests without extreme difficulty.

The Garbage Disposal. Usually there are at least a few classes in your system that are very large and fraught with problems, and they change all the time. This is the obvious place to improve your confidence coverage.

Good design, which starts with the application of the SRP, starts to solve many problems that the garbage disposal represents. Small, single-purpose classes provide more opportunities for reuse, make performance profiling easier, expose defects more readily, and can dramatically improve comprehension of the code, to name a few benefits. A garbage disposal is a dangerous place–with lots of hands in one big sinkhole, contention is frequent and merges can be painful. Design that adheres to the SRP thus also makes continuous integration easier.

Posted in coding, legacy, mikado method, TAD, TDD, test-driven development, unit testing | Leave a comment

My First TDD Exercise

Finding a good first exercise for TDD learners can be challenging. Some of the things I look for:

  • Not so simple that it’s trivial, e.g. Stack, lest students dismiss it as pointless. There should be opportunities for students to make a few simple mistakes, but…
  • …not so complex that students mire in the algorithm itself instead of focusing on technique.
  • Something students can relate to–not so obscure that students struggle with understanding the domain or problem.
  • Games and fluff (bowling, fizz-buzz, e.g.) can create a first impression–and first impressions can be very hard to break–that TDD is for trivialities only, despite what you say. These things can be fine as later exercises.
  • Not something that already exists in the library. (“Why am building a multi-map when C++ already has one?”)
  • Can be test-driven in 10 to 15 minutes by an experienced TDDer, meaning that it’s probably a 30-to-45-minute exercise for newbies pairing up.
  • Provides a good demonstration of the ability to incrementally grow a solution, including opportunities to spot and remove duplication.
  • Provides an opportunity  to show how to deal with at least one exceptional case.

I’ve been demoing a stock portfolio tracker for some time–a simple collection class that allows purchases of stock symbols. With Java students, I follow up with a multi-map, a class that would be useful in most shops (though a similar jakarta implementation exists). Both have worked well.

The message an exercise sends can be damaging. As a second or third exercise, lately I’ve been using the Roman numeral converter kata. I think it’s a cool exercise that can show how you can build an elegant algorithm by following a simple, incremental approach. It’s had 99% effectiveness: Out of the past ~100 students, one guy–an algorithm specialist–took a completely negative view of TDD after it. His stance was that he could have derived the algorithm much more quickly using a top-down, non-test-driven approach. From that he dismissed TDD completely. During subsequent pairings, I think he saw some of its benefits (we talk a lot about algorithms and TDD), but it’s an uphill battle and it might have been too late.

Currently I’m experimenting with an exercise to implement the soundex algorithm. More on that in an upcoming blog post.

Dave Thomas’s PragProg CodeKata site is a great place to get ideas. You might also check out the TDD Problems site (to which I contributed a handful of problems).

Posted in agile, algorithms, coding, kata, TDD, test-driven development, unit testing | Leave a comment

Refactoring and Performance

In the first portion of the book Refactoring, Martin Fowler presents an example where he whittles a verbose method into an improved design. Here’s the original, more or less:

   public String statement() {
      double totalAmount = 0;
      int frequentRenterPoints = 0;
      Enumeration rentals = this.rentals.elements();
      String result = "Rental Record for " + getName() + "\n";

      while (rentals.hasMoreElements()) {
         double thisAmount = 0;
         Rental each = (Rental)rentals.nextElement();
         switch (each.getMovie().getPriceCode()) {
            case Movie.REGULAR:
               thisAmount += 2;
               if (each.getDaysRented() > 2)
                  thisAmount += (each.getDaysRented() - 2) * 1.5;
               break;
            case Movie.NEW_RELEASE:
               thisAmount += each.getDaysRented() * 3;
               break;
            case Movie.CHILDRENS:
               thisAmount += 1.5;
               if (each.getDaysRented() > 3)
                  thisAmount += (each.getDaysRented() - 3) * 1.5;
               break;
         }

         frequentRenterPoints++;

         if (each.getMovie().getPriceCode() == Movie.NEW_RELEASE &&
             each.getDaysRented() > 1)
            frequentRenterPoints++;

         result += "\t" + each.getMovie().getTitle() +
                   "\t" + String.valueOf(thisAmount) + "\n";
         totalAmount += thisAmount;
      }
   }

And here’s the (relevant portion of the) factored code. I think this is as far as Fowler takes the example:

   public void addRental(Rental rental) {
      rentals.add(rental);
   }

   public String statement() {
      StringBuilder result = new StringBuilder();
      appendHeader(result);
      for (Rental rental: rentals)
         appendDetail(result, rental);
      appendSummary(result);
      return result.toString();
   }

   private int calculateFrequentRenterPoints() {
      int totalFrequentRenterPoints = 0;
      for (Rental rental: rentals)
         totalFrequentRenterPoints += rental.getFrequentRenterPoints();
      return totalFrequentRenterPoints;
   }

   private double calculateTotalCost() {
      double totalCost = 0;
      for (Rental rental: rentals)
         totalCost += rental.getCost();
      return totalCost;
   }

The while loop in the original statement method intertwines three things: construction of a text statement, addition into a total cost, and addition into a total for frequent renter points. The refactored version separates and isolates each of these three behaviors.

At the point of presenting this code, Fowler discusses the performance implications: Instead of a single set of iterations, the code now requires three iterations over the same collection. Many developers object strongly to this refactoring.

I’ve done quick (and rough, but relatively accurate) performance measurements for this example. Things aren’t three times worse, as a naive guess might suggest; instead we’re talking about a 7% degradation in performance. That’s still a significant amount in many systems, maybe even too much to accept. In many other systems, the degradation is negligible, given the context.

Some conclusions: First, always measure, never guess. Second, understand what the real performance requirements are. Create end-to-end performance tests early on. Run these regularly (nightly?), so that you know the day someone introduces underperforming code.

The refactored, better design provides the stepping stone to many other good things. The small, cohesive methods can each be read and understood in a few seconds. You can glance at each, and almost instantly know they must be correct. They’re also extremely easily tested if you aren’t so trustworthy of glances.

Looking further at the code, you might also spot an abstraction waiting to come into existence–the collection of rentals itself. Producing a statement is a responsibility separate from managing this collection. You could create another class named RentalSet or RentalCollection. Moving the calculation operations into the new class is a trivial operation, given that the calculation logic is now completely divorced from statement generation.

Other benefits accrue. Fowler talks about the ability to provide support for an HTML statement, in addition to existing support for a plaintext statement. Migration to a polymorphic hierarchy is simple and safe with the refactored design.

Following the mantra of “make it run, make it [the design] right, make it fast,” you can return to the performance issue now that you have an improved design. Suppose you must restore performance and eliminate the 7% degradation. You could un-factor the loop extract and again intertwine three elements in the body of one for loop.

But since you have a simpler design, the optimization doesn’t need to impact the design so negatively:

   public void addRental(Rental rental) {
      rentals.add(rental);

      // optimization--cache totals:
      totalFrequentRenterPoints += rental.getFrequentRenterPoints();
      totalCost += rental.getCost();
   }

   private int calculateFrequentRenterPoints() {
      return totalFrequentRenterPoints;
   }

   private double calculateTotalCost() {
      return totalCost;
   }

(Note the rare, debatably worthwhile comment, which I could eliminate by another method extract.)

Small methods are about many things, ranging from comprehension to reuse. In this case, having an optimized design allowed for an even simpler solution to the optimization of performance.

Posted in agile, coding, performance | 1 Comment

Test-Driving a Heap-Based Priority Queue

It’s been over 25 years since I had to code a priority queue. Should I really need to? Java now supplies a priority queue, and an important lesson of software development is that you should first seek to reuse what you can. But it’s worthwhile to know how to build such things yourself, in case you’re in an environment where one doesn’t exist.

As usual, though, I’m mostly interested in honing my ability to incrementally grow things, including interesting data structures. What can I learn from test-driving a priority queue?

All a priority queue does is return elements from a queue in priority order. Per the Wikipedia page, there are many ways to implement a priority queue, including a couple bad ways. Right off the bat, there’s no way I could force a priority queue to use a binary heap–one of the better choices–given only the client needs. Test-driving a solution and building the “simplest” possible implementation would result in the worst-performing solution.

So how to force the issue? Well, the nice thing about a binary heap is that it has a very concisely defined structure, one that I can verify easily. (I’ll refer to a binary heap as simply a heap for the rest of this article.) A binary heap is a tree where:

  • The key for each node is greater than or equal to each of its children.
  • The tree is complete–the last level is filled left-to-right, and all other levels are filled.

I know I want to end up with a heap–I need the performance–and will use that as a guiding factor in test-driving a solution. Does this violate YAGNI? Maybe. Other options? I could introduce a performance-related test, one where I tried to verify the big-O of each of the three operations (adding a new item, peeking at the highest priority item, and removing the highest priority item–the last two of which are often combined into a poll method). That would be tough to do well, and still might not guarantee an implementation that used a binary heap.

No, I need to be able to peek at the implementation. TDD will still be a great help. I still want to build the heap incrementally–there’s a decent amount of code involved, and plenty of opportunities to get it wrong. I’ll start simple, however, and not worry about heap characteristics until I get basic queue functionality working:

import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class PriorityQueueTest {
   private static final Object ITEM_50 = "50";
   private static final Object ITEM_100 = "100";

   PriorityQueue queue;

   @Before
   public void create() {
      queue = new PriorityQueue();
   }

   @Test
   public void emptyWhenCreated() {
      assertThat(queue.isEmpty(), is(true));
   }

   @Test
   public void noLongerEmptyAfterAdd() {
      queue.add(ITEM_50, 50);

      assertThat(queue.isEmpty(), is(false));
   }

   @Test
   public void singletonQueueReturnsSoleItemOnPoll() {
      queue.add(ITEM_50, 50);

      assertThat(queue.poll(), is(ITEM_50));
   }

   @Test
   public void isEmptyAfterSoleElementRemoved() {
      queue.add(ITEM_50, 50);
      queue.poll();

      assertThat(queue.isEmpty(), is(true));
   }

   @Test
   public void returnsFIFOWithTwoAlreadyOrderedItems() {
      queue.add(ITEM_100, 100);
      queue.add(ITEM_50, 50);

      assertThat(queue.poll(), is(ITEM_100));
      assertThat(queue.poll(), is(ITEM_50));
      assertThat(queue.isEmpty(), is(true));
   }
}

I won’t bore you with the rote, incremental steps used to get to this point. For now the solution is simply a class that wraps an ordered list. There’s no support for pulling things out in other than FIFO order, since there are yet no tests that demand the capability.

import java.util.*;

public class PriorityQueue {
   private List<Object> heap = new ArrayList<Object>();

   public boolean isEmpty() {
      return heap.isEmpty();
   }

   public void add(Object item, int priority) {
      heap.add(item);
   }

   public Object poll() {
      return heap.remove(0);
   }
}

All those tests to get to this trivial bit of code? No big deal. About five minutes of work, and I’ve gotten some good cases documented and clamped down.

The next test makes things a little more interesting:

   @Test
   public void returnsInPriorityOrder() {
      queue.add(ITEM_50, 50);
      queue.add(ITEM_100, 100);

      assertThat(queue.poll(), is(ITEM_100));
      assertThat(queue.poll(), is(ITEM_50));
      assertThat(queue.isEmpty(), is(true));
   }

The implementation now requires actually using the priority, associating it with each item, hence the introduction of the Node class. It also means I need a little bit of “real heap logic” in the add method: If the second item is higher priority than the first, sift it to the front. Since there are only two items, it’s a hard-coded swap. The way I coded it also happens to work for single-element heaps.

import java.util.*;

public class PriorityQueue {
   private List<Node> heap = new ArrayList<Node>();

   class Node {
   final Object object;
   final int priority;

   Node(Object object, int priority) {
      this.object = object;
      this.priority = priority;
   }
   }

   public boolean isEmpty() {
      return heap.isEmpty();
   }

   public void add(Object item, int priority) {
      Node node = new Node(item, priority);
      heap.add(node);
      if (node.priority > heap.get(0).priority)
         siftUp();
   }

   private void siftUp() {
      Node temp = heap.get(0);
      heap.set(0, heap.get(1));
      heap.set(1, temp);
   }

   public Object poll() {
      return heap.remove(0).object;
   }
}

Interestingly, I still don’t have a proper tree with parents and children–just a list with a couple slots filled in. In looking about at ways to implement a binary tree, the most appealing is to simply build it in-place in an array–something easily done given the constraint that the tree must be complete. Using an array will make it easy (and fast) to navigate from parent to child and vice versa.

The tests could use a bit of clarity. I add a helper method to simplify my assertion (and distill it to one line):

   @Test
   public void returnsFIFOWithTwoAlreadyOrderedItems() {
      queue.add(ITEM_100, 100);
      queue.add(ITEM_50, 50);

      assertQueuePollingOrder(ITEM_100, ITEM_50);
   }

   @Test
   public void returnsInPriorityOrder() {
      queue.add(ITEM_50, 50);
      queue.add(ITEM_100, 100);

      assertQueuePollingOrder(ITEM_100, ITEM_50);
   }

   private void assertQueuePollingOrder(Object... expectedItems) {
      List<Object> items = new ArrayList<Object>();
      while (!queue.isEmpty())
         items.add(queue.poll());
      assertThat(items, is(Arrays.asList(expectedItems)));
   }

Time to move on to the more interesting cases.

Thinking a bit about the next test (thinking is allowed, in fact encouraged, in TDD, particularly when determining what to do next)… Adding a third element to the heap will require a bit more generalized logic, and a fourth element (which would represent the third level of the heap’s tree) even more so. All complex enough without worrying about the complexity of removal, which requires re-heapifying.

One thing at a time. Rather than solve both challenges at once, I’ll first build out support for adding elements. Instead of using polling to verify that adding worked properly, I’ll verify that the priority queue’s underlying heap is valid.

Instead of creating a new test, I introduce a new assertion in each of my two existing tests (abusing the guideline of one assert per test).

   @Test
   public void returnsFIFOWithTwoAlreadyOrderedItems() {
      queue.add(ITEM_100, 100);
      queue.add(ITEM_50, 50);

      assertQueuePollingOrder(ITEM_100, ITEM_50);
      assertValid(queue.heap());
   }

   @Test
   public void returnsInPriorityOrder() {
      queue.add(ITEM_50, 50);
      queue.add(ITEM_100, 100);

      assertQueuePollingOrder(ITEM_100, ITEM_50);
      assertValid(queue.heap());
   }

   private void assertValid(List<PriorityQueue.Node> heap) {
      assertSubheap(heap, Integer.MAX_VALUE, 0);
   }

   private void assertSubheap(
         List<PriorityQueue.Node> heap, int priority, int root) {
      if (root > heap.size() - 1)
         return;

      PriorityQueue.Node node = heap.get(root);
      assertThat(priority > node.priority, is(true));

      assertSubheap(heap, node.priority, leftChild(root));
      assertSubheap(heap, node.priority, rightChild(root));
   }

   private int rightChild(int index) {
      return index * 2 + 2;
   }

   private int leftChild(int index) {
      return index * 2 + 1;
   }

Now I can quickly write the next failing test (with a lame name):

   @Test
   public void heapifiesThreeElements() {
      queue.add(ITEM_50, 50);
      queue.add(ITEM_75, 75);
      queue.add(ITEM_100, 100);

      assertValid(queue.heap());
   }

… and get it to pass quickly too:

   public void add(Object item, int priority) {
      Node node = new Node(item, priority);
      heap.add(node);
      int rightmost = heap.size() - 1;
      if (node.priority > heap.get(0).priority)
         siftUp(rightmost);
   }

   private void siftUp(int rightmost) {
      Node temp = heap.get(0);
      heap.set(0, heap.get(rightmost));
      heap.set(rightmost, temp);
   }

Another test:

   @Test
   public void heapifiesMultipleLevels() {
      add(50, 75, 100, 125);
      assertValid(queue.heap());
   }

This time it requires a bit more manipulation of existing code. I rename siftUp to simply swap, because that’s all it does, and the “sift up” algorithm is in the body of the add method.

   public void add(Object item, int priority) {
      heap.add(new Node(item, priority));
      int current = heap.size() - 1;
      while (priority > parentPriority(current)) {
         swap(current, parent(current));
         current = parent(current);
      }
   }

   private int parentPriority(int index) {
      return heap.get(parent(index)).priority;
   }

   private int parent(int index) {
      return (index - 1) / 2;
   }

   private void swap(int x, int y) {
      Node temp = heap.get(x);
      heap.set(x, heap.get(y));
      heap.set(y, temp);
   }

It’s not bad, but I want each method to be at one level of abstraction, so I modify the add method:

   public void add(Object item, int priority) {
      addNewNode(item, priority);
      siftUpFromLast(priority);
   }

   private void addNewNode(Object item, int priority) {
      heap.add(new Node(item, priority));
   }

   private void siftUpFromLast(int priority) {
      int current = heap.size() - 1;
      while (priority > parentPriority(current)) {
         swap(current, parent(current));
         current = parent(current);
      }
   }

I think that’s it for add! To confirm, I use the example from Wikipedia:

   @Test
   public void integrationTest() {
      add(1, 2, 3, 7, 17, 19, 25, 36, 100);
      assertValid(queue.heap());
   }

Oh yeah–all those test lines required to add elements were getting annoying, so I added the following helper in the test (and updated other tests correspondingly):

   private void add(int... priorities) {
      for (int priority: priorities)
         queue.add(String.valueOf(priority), priority);
   }

On to polling. The general algorithm: capture value at front of heap (the highest priority per add‘s post-condition), move the rightmost item to the front, and re-heapify. To re-heapify: compare the item at the root with both its children, swap with the larger if necessary, and re-heapify the subtree.

From a test perspective, all we need to verify is that the highest priority item was returned and that the completeness post-condition held.

   @Test
   public void removeAnswersHighest() {
      add(20, 30, 40, 100, 50, 60);
      assertThat(queue.poll(), is((Object)"100"));
   }
   @Test
   public void removeReheapifies() {
      add(20, 30, 40);
      queue.poll();
      assertValid(queue.heap());
   }

After incrementally introducing changes based on those two test failures, here’s the explicit bit of code that covers lists up to three elements:

   public Object poll() {
      Object result = heap.remove(0).object;
      if (heap.size() == 2) {
         if (heap.get(0).priority < heap.get(1).priority)
            swap(0, 1);
      }
      return result;
   }

The addition of another item generates the next test failure:

   @Test
   public void removeReheapifyMustConsiderBothChildren() {
      add(40, 20, 30, 10);
      queue.poll();
      assertValid(queue.heap());
   }

I use programming by intention to express the algorithm clearly, starting with the poll method:

   public Object poll() {
      if (heap.size() == 1)
         return heap.remove(0).object;

      Object result = heap.get(0).object;
      reheapify();
      return result;
   }

   private void reheapify() {
      moveLastToFirstSlot();
      if (priorityLessThanEitherChild(0))
         swap(0, highestPriorityChild(0));
   }

The reheapify method has the odd side effect of eliminating the first (highest) element by overwriting it. That this is not explicit in the poll method suggests there’s probably a better expression of the entire algorithm.

I flesh out the trivial supporting methods:

   private void moveLastToFirstSlot() {
      Node rightmost = heap.remove(heap.size() - 1);
      heap.set(0, rightmost);
   }
private boolean priorityLessThanEitherChild(int index) {
      int priority = heap.get(index).priority;
      return priority < leftPriority(index) ||
             priority < rightPriority(index);
   }

   private int highestPriorityChild(int index) {
      return leftPriority(index) > rightPriority(index)
             ? leftChild(index)
             : rightChild(index);
   }

   private int rightPriority(int index) {
      return priority(rightChild(index));
   }

   private int leftPriority(int index) {
      return priority(leftChild(index));
   }

   private int priority(int index) {
      if (index > heap.size() - 1)
         return Integer.MIN_VALUE;
      return heap.get(index).priority;
   }

   private int leftChild(int index) {
      return index * 2 + 1;
   }

   private int rightChild(int index) {
      return index * 2 + 2;
   }

It’s a good chunk of code, but it still fit comfortably within my five-minute cycle limit.

Hey–I recognize those leftChild and rightChild methods from my test! I’ll go make ‘em static so the test can re-use them.

About the only other interesting logic is rooted in the priority method, which returns Integer.MIN_VALUE if there’s no item at an index. That allows comparisons in priorityLessThanEitherChild to quietly fail when no child exists.

Almost there.  The final push is to force repeated swaps from root down through multiple subtrees:

   @Test
   public void removeMustReheapifyMultipleLevels() {
      add(50, 40, 20, 30, 10);
      queue.poll();
      assertValid(queue.heap());
   }

Getting a passing test requires a nice, small increment of code (and a TPP transformation from if=>while. Recursion is one up in the priority list–should I refactor this method?).

   private void reheapify() {
      moveLastToFirstSlot();
      int i = 0;
      while (priorityLessThanEitherChild(i)) {
         int child = highestPriorityChild(i);
         swap(i, child);
         i = child;
      }
   }

Both add and poll methods appear to be complete. Here’s a passing test that fires end-to-end:

   @Test
   public void pollsInPriorityOrder() {
      add(40, 20, 60, 80, 10, 30, 90, 70, 50);
      assertQueuePollingOrder(
         "90", "80", "70", "60", "50", "40", "30", "20", "10");
   }

Boy, do those quotes suck. Well, it’s time to do some global clean-up, so I’ll fix that along with some other things. Here’s the test class after some various cleanup for expressiveness:

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.*;
import org.junit.*;

public class PriorityQueueTest {
   PriorityQueue queue;

   @Before
   public void create() {
      queue = new PriorityQueue();
   }

   @Test
   public void emptyWhenCreated() {
      assertThat(queue.isEmpty(), is(true));
   }

   @Test
   public void noLongerEmptyAfterAdd() {
      queue.add("", 50);

      assertThat(queue.isEmpty(), is(false));
   }

   @Test
   public void singletonQueueReturnsSoleItemOnPoll() {
      queue.add("abc", 50);

      assertThat(queue.poll(), is((Object)"abc"));
   }

   @Test
   public void isEmptyAfterSoleItemRemoved() {
      queue.add("", 50);
      queue.poll();

      assertThat(queue.isEmpty(), is(true));
   }

   @Test
   public void returnsFIFOWithTwoAlreadyOrderedItems() {
      add(100, 50);
      assertQueuePollingOrder(100, 50);
      assertValid(queue.heap());
   }

   @Test
   public void returnsTwoUnorderedItemsInPriorityOrder() {
      add(50, 100);
      assertQueuePollingOrder(100, 50);
      assertValid(queue.heap());
   }

   @Test
   public void heapifiesThreeItems() {
      add(50, 75, 100);
      assertValid(queue.heap());
   }

   @Test
   public void heapifiesMultipleLevels() {
      add(50, 75, 100, 125);
      assertValid(queue.heap());
   }

   @Test
   public void removeAnswersHighest() {
      add(20, 30, 40, 100, 50, 60);
      assertThat(queue.poll(), is((Object)"100"));
   }

   @Test
   public void removeReheapifiesTwoItems() {
      add(20, 30, 40);
      queue.poll();
      assertValid(queue.heap());
   }

   @Test
   public void removeReheapifyMustConsiderBothChildren() {
      add(40, 20, 30, 10);
      queue.poll();
      assertValid(queue.heap());
   }

   @Test
   public void removeMustReheapifyMultipleLevels() {
      add(50, 40, 20, 30, 10);
      queue.poll();
      assertValid(queue.heap());
   }

   @Test
   public void pollExhaustsHeapInPriorityOrder() {
      add(40, 20, 60, 80, 10, 30, 90, 70, 50);
      assertQueuePollingOrder(90, 80, 70, 60, 50, 40, 30, 20, 10);
   }

   private void assertQueuePollingOrder(Integer... expectedItems) {
      List<Integer> items = new ArrayList<Integer>();
      while (!queue.isEmpty())
         items.add(Integer.parseInt((String)queue.poll()));
      assertThat(items, is(Arrays.asList(expectedItems)));
   }

   private void add(int... priorities) {
      for (int priority: priorities)
         queue.add(String.valueOf(priority), priority);
   }

   private void assertValid(List<PriorityQueue.Node> heap) {
      assertSubheap(heap, Integer.MAX_VALUE, 0);
   }

   private void assertSubheap(List<PriorityQueue.Node> heap, int priority, int root) {
      if (root > heap.size() - 1)
         return;

      PriorityQueue.Node node = heap.get(root);
      assertThat(priority > node.priority, is(true));

      assertSubheap(
         heap, node.priority, PriorityQueue.leftChild(root));
      assertSubheap(
         heap, node.priority, PriorityQueue.rightChild(root));
   }
}

And here’s the implementation:

import java.util.*;

public class PriorityQueue {
   private List<Node> heap = new ArrayList<Node>();

   static class Node {
      final Object object;
      final int priority;

      Node(Object object, int priority) {
         this.object = object;
         this.priority = priority;
      }

      public String toString() {
         return String.valueOf(priority);
      }
   }

   public boolean isEmpty() {
      return heap.isEmpty();
   }

   public void add(Object item, int priority) {
      addNewNode(item, priority);
      siftUpFromLast(priority);
   }

   private void addNewNode(Object item, int priority) {
      heap.add(new Node(item, priority));
   }

   private void siftUpFromLast(int priority) {
      int current = heap.size() - 1;
      while (priority > parentPriority(current)) {
         swap(current, parent(current));
         current = parent(current);
      }
   }

   private int parentPriority(int index) {
      return heap.get(parent(index)).priority;
   }

   private int parent(int index) {
      return (index - 1) / 2;
   }

   private void swap(int x, int y) {
      Node temp = heap.get(x);
      heap.set(x, heap.get(y));
      heap.set(y, temp);
   }

   public Object poll() {
      if (heap.size() == 1)
         return heap.remove(0).object;

      Object result = heap.get(0).object;
      reheapify();
      return result;
   }

   private void reheapify() {
      moveLastToFirstSlot();
      int i = 0;
      while (priorityLessThanEitherChild(i)) {
         int child = highestPriorityChild(i);
         swap(i, child);
         i = child;
      }
   }

   private void moveLastToFirstSlot() {
      Node rightmost = heap.remove(heap.size() - 1);
      heap.set(0, rightmost);
   }

   private boolean priorityLessThanEitherChild(int index) {
      int priority = heap.get(index).priority;
      return priority < leftPriority(index)
          || priority < rightPriority(index);
   }

   private int highestPriorityChild(int index) {
      return leftPriority(index) > rightPriority(index)
         ? PriorityQueue.leftChild(index)
         : PriorityQueue.rightChild(index);
   }

   int rightPriority(int index) {
      return priority(rightChild(index));
   }

   int leftPriority(int index) {
      return priority(leftChild(index));
   }

   private int priority(int index) {
      if (index > heap.size() - 1)
         return Integer.MIN_VALUE;
      return heap.get(index).priority;
   }

   List<Node> heap() {
      return heap;
   }

   static int rightChild(int index) {
      return index * 2 + 2;
   }

   static int leftChild(int index) {
      return index * 2 + 1;
   }
}

What else is there to complain about?

  • My test names could use some work. It’s hard to derive good test names when you’re driving incremental implementation of an algorithm, instead of an interface.
  • I probably have some tests I can get rid of.
  • The tests could be composed better. Worse, the test knows about the Node class (which the client need not know about). Maybe binary heap verification should be built into the heap implementation itself.
  • Should there be a PriorityQueue class and a separate Heap class? I thought about this a while and didn’t see the value.
  • I don’t use the Comparable interface to “simplify” and externalize comparisons (i.e. allow the client to specify how priorities compare).
  • I don’t have any tests that show what happens when priorities are equal.
  • I don’t use safety braces (i.e. braces around single-line if statements)–that’s why I have tests. I don’t make everything private that could be private (package access is fine by me, particularly in tests since I don’t have to type the access modifier).

What do I like?

  • I was able to test-drive the implementation, sticking to the five-minute rule for getting green bars. Things were a bit close a couple times (was there a more incremental way?).
  • I think the algorithm as implemented is very easy to follow (read each of the add and poll methods), particularly when compared to what is typically built (here’s one implementation; see, for example, the percolateDown method).

What do you think? Is test-driving a priority queue a good kata? I lay down the challenge flag for a better progression and implementation (shouldn’t be hard).

Posted in coding, kata, TDD, test-driven development, unit testing | Leave a comment

Don’t Stop Reading…

When writing, be careful of presenting an opposing point first with the notion that you’ll make your counterpoint in a subsequent paragraph or page. That first pitch is often as far as some readers get. Royce’s article “Managing the Development of Large Software Systems” led to decades of waterfall for this reason.

Recently, a pair-developer expressed concern over the use of multiple returns in a short method. I said I knew about the long-standing rule but that it created little value for very short methods. Two days later, I heard the same concern during another pairing session. Twice in one week? Curious! I asked where they’d read about the rule. “Uncle Bob’s Clean Code” was the surprising response. “Hand me your copy!” I insisted, a bit worried, and quickly located the text in the functions chapter. The very last paragraph on page 48, starting a new section, states: “Dijkstra rules of structured programming… [say] …every function … should have one entry and one exit. Following these rules means that there should only be one return statement in a function…” Oh! Yeah, I learned that 30 years ago. Really, Bob?

Page 49, top paragraph: “”While we are sympathetic to the goals and disciplines of structured programming, these rules serve little benefit when functions are very small. It is only in larger functions that such rules provide significant benefits.”

Oh, ok. Whew! Crisis of faith averted.

Readers: Inattentive reading can be embarrassing!
Writers: Don’t assume readers will get to your point. Make it first.

Posted in coding, pair programming, pairing, writing | 2 Comments

Not Giving Up on vi(m)

I wrote this blog as a reaction to reading James McKay’s blog entry, “:q!,” in which he describes abandoning vim. A story familiar to me, but I fortunately never abandoned it for good. I do have to agree that Intellisense and other wonders of modern IDEs make them the preferred environment for things like Java, but I still get a lot of use out of vim.

In the past, I’d be forced to use vim while, say, having to do a small amount of my work on Solaris servers, but then I’d be back on Windows shortly thereafter. I loaded RedHat at home many years ago but it didn’t stick. Then I tried Ubuntu… a couple times, and Un*x finally stuck. Yet I was still only occasionally using vim, which meant I was a perpetual newb, at a “level 1″ of vim capability (I’ll sadly call the scale the VCM, or vim capability model).

VCM level 1 is the ability to get in and out of the tool and do basic editing (search, page forward, change/replace text, occasional use of the dot operator, etc.). I remember that I too thought the only way to delete many lines was via count-based commands (e.g., 10d). At VCM level 1, vim beeps at you a lot, and you don’t understand why people swear by it.

What changed things was a result of being forced into having to hit vim on a daily basis for an extended period of time. I had the opportunity to work with Tim Ottinger a bit, and we would pair from time to time. Tim is the author of a great article on vim, “Use vim Like a Pro.” I won’t mention pairing after this paragraph, but I will say that without the ability to pair with someone at a higher VCM level, I might never have advanced much from my level 1 proficiency.

I’m making up the VCM based on my history, of course, but it might just work for others, too. VCM level 2 is marked by use of these critical elements of vim:

  • regular use of the dot operator
  • use of hjkl for cursor movement
  • ability to manage buffers, registers, and split windows
  • ability to visually mark code
  • regular use of f and t in combination with other commands; use of other context-based movement commands
  • use of ctrl-n for auto completion
  • awareness and occasional use of macros
  • occasional use of help facility to learn something new
  • regular (no pun intended) use of regex
  • use of ctags to aid code navigation

Reaching level 2 was important: At level 1, I simply thought vim was a frustrating, not-very-powerful tool. At level 2, I am fairly effective when editing, and faster at many tasks than most people in their GUI editors. I also see that there is much more to learn and master.

Tim is probably at VCM level 4: mastery of most, if not all, of vim’s features. I suppose that marks Level 3 as when you have ingrained all the major facilities (i.e. most everything that Tim covers in Use vim Like a Pro), are trying to ingrain something new on a regular basis (I’ve now habituated the use of ~ to toggle case, for example), have considerably customized your .vimrc, and are making an active attempt to do everything in the most efficient way possible. I am getting into level 3 now, but I haven’t used vim heavily in a while.

During my fortunate opportunities to learn vim from Tim, I discovered there were a few things about vim that he didn’t know. Shocker! It’s an extensive tool, but I guess what that means is that there’s a VCM level 5. Let’s define that as mastering VCM Level 4 plus everything that Tim doesn’t know now.

Posted in IDEs, pairing, vim | Leave a comment

TDD Kata: Roman number converter

I’ve introduced the whole-number-to-Roman-numeral kata in my test-driven development training, not out of sadistic interest, but because I think it offers some excellent lessons about TDD. I do find it a bit amusing that good developers often struggle badly with the derivation of the algorithm, even after I give them my guidelines for the exercise:

  • Don’t assume that your tests must go in order 1, 2, 3, 4, 5, 6, 7, … Seek the test that would make for the next simplest increment of code. Seek patterns–if you’ve figured out the pattern for 1, 2, 3, then what’s the next series of numbers that follows the same pattern?
  • When you refactor incrementally, make similar code look exactly alike, and then get rid of the duplication.
  • Do not prematurely optimize code. Avoid using code-expressiveness “optimizations:” use while loops and simple decrement constructs instead of for loops and post-decrement operators, and let them stand until your algorithm is complete.

After they complete the exercise, we discuss Uncle Bob’s Transformation Priority Premise (TPP), and I ask them to re-run the exercise for homework (and to get them used to the idea of a kata) with the TPP in mind.

The first time I attempted the Kata some years ago, I got lost in the weeds. I didn’t give up; I scrapped a good amount of code, thought a bit harder about the ordering of tests, and eventually made my way to the finish line. I noted at least a couple insights were required, and came up with the guidelines above as a result in order to help students keep on track.

“Let’s start at the beginning, a very good place to start:”

   @Test
   public void convertIntToRoman() {
      assertThat(convert(1), is("I"));
   }

The implementation is the topmost TPP transform, ({}->nil):

   String convert(int arabic) {
      return null;
   }

Quickly following, the passing implementation (nil->constant):

   String convert(int arabic) {
      return "I";
   }

Moving along to the next test:

assertThat(convert(2), is("II"));

There might be a way to get a higher-level transform, but (unconditional->if) seems simplest to me:

   String convert(int arabic) {
      if (arabic == 2) return "II";
      return "I";
   }

The conversion for 3 indicates a repeating pattern:

   assertThat(convert(3), is("III"));

…which begs for a loop (if->while):

   String convert(int arabic) {
      String roman = "";
      while (arabic-- > 0)
         roman += "I";
      return roman;
   }

Performance geeks: never mind the use of String vs StringBuilder/StringBuffer. I want to get the algorithm working first, and all that rot clutters up the code a bit.

Hmm. I didn’t follow my guideline, and used post-decrement where I probably should have just introduced a separate statement to decrement the arabic variable. (Oh and another sin, I’m manipulating the argument. Ah well.)

Now a bit of thought. One, two, three, ten! No, four sir! No, ten. I have support for I, II, III. If I seek a similar pattern, it’s likely to do the least to my code in terms of transforms required, so what if I head to X, XX, and XXX?

   assertThat(convert(10), is("X"));

I can get that to work with a simple (unconditional->if), but it’s horrid:

   String convert(int arabic) {
      if (arabic == 10) return "X";
      String roman = "";
      while (arabic-- > 0)
         roman += "I";
      return roman;
   }

That’s ok–adding a test for 20:

   assertThat(convert(20), is("XX"));

…starts to make things look better, as I introduce (if->while) again:

   String convert(int arabic) {
      String roman = "";
      while (arabic >= 10) {
         roman += "X";
         arabic -= 10;
      }
      while (arabic-- > 0)
         roman += "I";
      return roman;
   }

Those two while loops look an awful lot alike. I can make them look almost exactly alike–I replace my post-decrement (see, shouldn’t have introduced it yet) with a separate statement at the end of the loop body, and adjust my comparison expression:

  String convert(int arabic) {
      String roman = "";
      while (arabic >= 10) {
         roman += "X";
         arabic -= 10;
      }
      while (arabic >= 1) {
         roman += "I";
         arabic -= 1;
      }
      return roman;
   }

It’s clear that these while loops differ only by their data. A bit of insight suggests introducing a table-lookup, allowing the implementation to be factored to the following:

   static int[] ARABIC_DIGITS = { 10, 1 };
   static String[] ROMAN_DIGITS = { "X", "I" };

   String convert(int arabic) {
      String roman = "";
      for (int i = 0; i < ARABIC_DIGITS.length; i++) {
         while (arabic >= ARABIC_DIGITS[i]) {
            roman += ROMAN_DIGITS[i];
            arabic -= ARABIC_DIGITS[i];
         }
      }
      return roman;
   }

(In Java, I tried once building the above as a map, but since the algorithm demands traversing the digits in reverse order, it ends up being a good deal more code than the simple but slightly obnoxious paired arrays.)

Huh. Look at that. A test for 30 should pass too, and it does.

But is it a bit too much implementation? Maybe–now that I support I’s and X’s, I want to be able to combine them, yet it looks like the following tests will pass right off the bat:

   assertThat(convert(11), is("XI"));
   assertThat(convert(33), is("XXXIII"));

And they do! I wonder if there’s a way I could have coded so those tests failed first, but I’ve not thought about it much. Maybe I could have put the second loop in an else block. Ideas?

What about 5?

  assertThat(convert(5), is("V"));

It’s just another digit; adding support for 5 to the table gets this test to pass:

   static int[] ARABIC_DIGITS = { 10, 5, 1 };
   static String[] ROMAN_DIGITS = { "X", "V", "I" };

And now these little combinations involving 5 also pass:

   assertThat(convert(8), is("VIII"));
   assertThat(convert(27), is("XXVII"));

I have to imagine similar tests involving L, C, D, and M should all pass too.

Instead, I’ll hit that pesky one, 4:

   assertThat(convert(4), is("IV"));

The joy of four is that if I try to get there with subtraction, I’m in a world of hurt. This is where students usually go really, really wrong. But, wait… insight! What if I consider that “IV” is just another digit, like any other Roman digit, except that it requires two characters instead of one to represent?

   static int[] ARABIC_DIGITS = { 10, 5, 4, 1 };
   static String[] ROMAN_DIGITS = { "X", "V", "IV", "I" };

Voila. At this point, I’ve covered the various scenarios, now it’s just a matter of fleshing out the table with support for all of the Roman digits. How about I just write a couple tests that fire across all cylinders?

      assertThat(convert(2499), is("MMCDXCIX"));
      assertThat(convert(3949), is("MMMCMXLIX"));

The final “table:”

static int[] ARABIC_DIGITS =
{ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
static String[] ROMAN_DIGITS =
{ "M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I" };

We haven’t had to touch the algorithm for a while. It still holds up!

   String convert(int arabic) {
      String roman = "";
      for (int i = 0; i < ARABIC_DIGITS.length; i++) {
         while (arabic >= ARABIC_DIGITS[i]) {
            roman += ROMAN_DIGITS[i];
            arabic -= ARABIC_DIGITS[i];
         }
      }
      return roman;
   }

I did a search for code that solves this challenge, and had to laugh at the ugliness of a number of the implementations. There were some clever but convoluted ones, some awful ones, and a few that had the same implementation here.

TDD can’t derive algorithms, so they say. I take a different view. An algorithm is simply a “step-by-step procedure for calculations,” per Wikipedia. And TDD is great at helping drive out the vast majority of step-by-step procedures.

Oh–they mean complex algorithms. I see. That’s ok: Most of what we must implement on a day-to-day basis can be done with reasonably simple algorithms. In fact, I’d warrant that far less than a percent of any given system requires anything but simple algorithms that any journeyman programmer can easily devise. (And I’ve seen some very “complex” systems, such as BaBar and the Sabre GDS.)

Complex algorithms do require leaps achieved often only by appropriate insights, sometimes gained only after extensive thought about the problem, and sometimes never reached by a given single mind. TDD will not necessarily help your mind achieve those insights. However, its incremental nature can in some cases give you enough pause and isolation on a small piece of the problem, to the point where the insight needed almost screams at you.

Posted in algorithms, kata, TDD, test-driven development | Leave a comment