Much less effort, a step toward perfection. And perhaps we know why ...

A Bit of Planning

We began the day with a long discussion of how we wanted to calculate density. Two main outcomes:

  1. The customer wants the sector-shaped density calculation to be centered, not on the aiming point, but on the center of mass of the pattern. The thought is that the center of mass tells him how to adjust his aim, and the pattern tells him how to adjust his gun. So be it.
  2. The customer also decided that the rectangular density grid can be origined at the top left of the paper for all he cares. The grid will be small, and the information will be much the same no matter whether the cells are origined at the target, the center of mass, or the top left. So top left it is.

We also discussed how to proceed. One possibility was to code the existing density calculations into submission. We decided, rather, to treat them as they were originally written, namely as spikes experimenting with how to do the calculation. We decided to TDD forward and, ultimately, to throw those spike versions away.

We chose to work on the rectangular density question first, because it’s simpler and would cause us to face most of the problems. And we considered a few different designs:

  1. We could just send a message to the ShotPattern, as we do now, returning an array of ints. That array needs to be interpreted as a two-dimensional array, which means that the ShotPattern, and the user of the density calculation, have a shared understanding of how to create and subscript that array. That's not good code communication, as it leaves an important fact hanging in cryptic code.
  2. We could, instead, tell the ShotPattern how to grid itself, and then send repeated messages to it, asking for the counts in whatever grid elements we cared about. We sketched a test for that, looking like this:
    // @Test
    //  public void grid00() {
    //      pattern = new ShotPattern("");
    //      pattern.gridInInches(2,2);
    //      assertEquals(20, pattern.hitsInGrid(0,0));
    //  }
    The idea is that hitsInGrid could give the row and column numbers of any cell in the grid, and the ShotPattern can return them. That seemed nearly good.
  3. We could instead have a Grid object that represents a rectangle by width and height. The Grid would know the ShotPattern, and we can send the Grid a message asking it to give the hit count in a given grid location. We wrote a test for that, as well:
       @Test
        public void grid00() {
            pattern = new ShotPattern(folder+"tm1subset.bmp", 4.0, 3.0);
            RectangularGrid grid2x2 = new RectangularGrid(pattern,1,1);
            assertEquals(1, grid2x2.hitsIn(0,0));
        }

You’ll note from the fact that the second test is more complete than the first, and not commented out, that we decided to go with it. The idea in the new ShotPattern constructor is that we’ll tell the pattern its dimensions in inches, and it will share the responsibility of mapping that to pixels with the PatterningSheet, in some way to be determined. For testing, we chose a bitmap that’s 406x300, and decided to grid it in 4x3 inch chunks, and looking at it, decided that there was probably one pixel in the 0,0 grid location, starting from top left:

image

So that’s our test, and the end of our planning.

"Major Refactoring"

In the course of this discussion we decided to grid the pattern in inches, using double precision, to create a new object type Grid (to first show up as RectangularGrid), to move the Grid around on top of the pattern, asking the pattern to tell us how many hits are in there. We know that the pattern is presently coordinatized in pixels, translated from top left to the center of the page. Clearly everyone is going to have to be changed, “all the way down”, to convert “the entire system” to inches instead of pixels.

We spoke about who should know about pixels, and agreed that the PatterningSheet, which reads the file, will clearly know, and that possibly the ShotPattern will, and perhaps it will not. The Hits should be recast in terms of double-precision inches. And who knows what else will change?

This is the thing that everyone fears: a previous design decision turns out to be wrong or inadequate, and now we have a “major refactoring”, requiring lots of downtime, on our hands. We paused to talk about this, because careful readers or victims of direct contact with Chet and me know that we don’t believe in “major refactorings”.

Keep an eye on what happens. If we’re successful, you’ll see these new ideas and data structures go into the system without a lot of downtime. I’m not sure we’re ready to write the book on how to do this yet, but we’re pretty sure we can demonstrate it.

Test, Then Code

Given that test, we were down to a half hour of work time. So we just followed our nose. The constructor didn’t exist, Eclipse told us, so we built it, resulting in this:

public class ShotPattern {
    int width;
    int height;
    List<Hit> hits = new ArrayList<Hit>();

    ...

    public ShotPattern(String fileName, double widthInInches, double heightInInches) {
        PatterningSheet sheet = new PatterningSheet(fileName);
        hits = sheet.getHits();
        width = sheet.widthInPixels();
        height = sheet.heightInPixels();
    }

We’re clearly not using the width and height in inches yet, but we presume we’ll come to that as time passes. We did decide that we needed the pixel width and height, and asked the sheet to give those to us. Eclipse mentioned that we didn’t have those methods, so we built, in PatterningSheet:

   public int widthInPixels() {
        return farian.getWidth();
    }

    public int heightInPixels() {
        return farian.getHeight();
    }

In short, we just asked the raster to tell us the pixel widths. It’s also worth noting that I made the mistake of forgetting that our raster is named, not raster, but farian. When you get cute, you get bitten. We should fix that real soon now before it gets worse. We also changed the references to width and height in the PatterningSheet to all refer to the methods above. Well, we changed the ones we saw. There may still be some, which should also be fixed, but I’m not going there now.

Those steps made the constructor code compile. Eclipse also said that we needed a RectangularGrid object. We created that:

public class RectangularGrid {

    private double width;
    private double height;

    public RectangularGrid(ShotPattern pattern, double widthInInches, double heightInInches) {
        this.width = widthInInches;
        this.height = heightInInches;
    }

    public int hitsIn(int gridX, int gridY) {
        return 0;
    }

}

This reflects the design decision to manage the grids in inches to a bunch of decimal places, so we’ll use double for that. We expect to change the whole system over to double as we go forward, without ever doing a major refactoring.

At this point, the code compiles, and our test is red. We are returning hitsIn as zero and we expect one.

Moving to Implementation

We wrote a test, and made it fail. We could have gone to the famous “Fake It Till You Make It” pattern of Test-Driven Development, and made the test return 1, and then moved on to the next test for the implementation. That didn’t seem interesting, and we had fifteen minutes left before lunch. So we moved on to the implementation. We wrote some intention-driven code in the hitsIn() method:

   public int hitsIn(int gridX, int gridY) {
        rectangle = new Rectangle2D.Double(gridX*width, gridY*height, width, height);
        return pattern.countHits(rectangle);
    }

Simple enough. We know the height and width of the grid elements. Given that we want row gridX and column gridY (maybe we should name them row and column?), and given that the row and column start at (0,0), then the rectangle we want to check in the pattern is the one shown above, from gridXwidth, gridYheight, of size width by height. We create that rectangle, and ask the pattern to count the hits in that rectangle, in ShotPattern:

   public int countHits(Rectangle2D rectangle) {
        int count = 0;
        for (Hit hit: hits) {
            if (rectangle.contains(hit.getX(), hit.getY()))
                count++;
        }
        return count;
    }

This code echoes the code in the hit-counting spike that you’ve seen before, so it was easy to type in. It doesn’t work, because the getX and getY of Hit return pixel coordinates, translated to the center of the sheet, not inch-based coordinates.

So the test is still red, and the “bug” is in the highlighted line above. We don’t know whether we should change the existing getX and getY, or implement new getXinInches methods. I suspect the latter.

But we are at a red bar, with more of the code in place, and we are just about down to the last step to make this test run. And best of all, we haven’t broken any other tests. We’re green, except for our new test.

And it was 11 o’clock, we’d been coding for a full half hour, and it was time for lunch, Chet and Ron style. So we went to Buffalo Wild Wings, braving the amazing double roundabout at Lee Road. The fact that you’re reading this shows that we survived.

Learning (such as it is)

Last time I spoke about “Shibumi”, translated as “Effortless Perfection”, remarking that we had attained nothing like that on that day, but that Shibumi is a place we’re walking toward rather than the place we live. The work I’m describing here was smooth, comfortable, productive. We were mindful of what we were doing and how it was going. Really perfect in how it felt, though I’d not argue that the work itself is without flaw. We did feel that it was as good as we could have made it, as good as we were capable of.

What’s the difference? Why was Thursday good, and Wednesday … not so good?

Chet pointed out one tactical difference at lunch. Wednesday we tried to drive our development from a FitNesse test, and Thursday we drove it from a JUnit test. (Not NUnit. Thanks, Kim.) Starting with JUnit tests helps us to take smaller bites. Smaller bites give us a chance to recognize earlier that we’re on the wrong path, so that the correction is just a natural adjustment rather than “Oh hell where are we?”

But there’s more. On Wednesday, we tried to use some spike code directly, as if it were something that was robust and ready to go. I suspect that’s because we generally try to put code away in a clean and robust state, even if it’s incomplete. We haven’t been doing that on this project. We’ve been making little forays into the forest of graphics and geometry, and leaving the resulting code in the system. Possibly our troubles on Wednesday relate to having left the place a bit messy in preceding days.

I don’t recall, on Wednesday, that we had a sense of impending doom. Rather, we went along very quickly, wired up our test, got it to go red as expected, and then as soon as it should have worked, found ourselves in a mess of trouble due to a few mistakes working together. We had miscalculated how many items there should be in the output; the method we were using to create the data didn’t properly set up the data size; we had forgotten our translation of the picture from 0,0 to target center.

When the effect you’re seeing is a result of layers of bugs, it becomes confusing. You find a bug. Aha! You fix it. The program still doesn’t work. What was wrong with your fix? So you debug or something and discover that your fix worked. It’s something else. You find that. Aha! You fix it. Lather, rinse, repeat.

All the time, confidence and understanding are draining away, yet you think you’re making progress. Then, finally, maybe the program even works … but your confidence and understanding are quite likely at a new low.

Well, I said “you” above, but I meant me. Since I imagine that everyone is like me, I imagine that some of you may recognize yourselves in what’s above. If not, pencil “Ron” over all the occurrences of “you”.

Is there a lesson there? Well, one certainly is to leave the code as clean as possible, so that there aren’t likely to be layers of defects showing up when we least need them. (I rarely need a defect, now that I think about it.)But maybe there’s a way to recognize the situation and use our mind a little differently, so that we expect layers and aren’t demoralized when our first brilliant fix doesn’t fix things.

A more important lesson, however, is one that I started preaching1 a long time ago.

When a defect slips through your tests, always follow the same procedure:
  1. Write a customer test showing the defect. (FitNesse)
  2. Write a programmer test showing the defect. (JUnit)
  3. Fix the defect, making the tests run. Write other tests as needed to generate the code for the fix.
  4. Think about what there is to learn about the whole system, and the way you've been working, based on the tests and code you just created.

We didn’t do that. We had a customer test showing the defect, and we dove right into the code. That brings us back to Chet’s original point: work from JUnit tests. Have the FitNesse tests, but remember that they are there to satisfy the customer and that they are likely not fine-grained enough to drive the actual code.

We live, and learn, and live, and relearn. Life is good. See you next time!


  1. No, this is not a religion. I am not saying that you have to do what I talk about. I'm reporting what happens to me, and what it makes me think about my work, your work, work in general.2
  2. "This is how I develop software. Take the parts that make sense to you. Ignore the rest.3"
  3. Ron Jeffries, random sig.4
  4. Recursive footnote.4