Flylib.com

Books Software

 
 
 

There s Only One Thing to Do


Theres Only One Thing to Do

Well, when the code is well and truly ruined, theres just one thing to do: back it out. The easiest way was to hold down Ctrl+Z (undo) until the code was backed up to a known good point. So we did that and then took a little time to reflect. Its important to reflect when things go wrong. You wont always have the right idea for whats next , but often you will.

I like to think that Chet and I are pretty calm about throwing code away. (Weve thrown away so much!) It always hurts but only until you do it. Then its like putting down a big bucket of water: a great relief. So we thought, and Chet reflected back on something he had said earlier in the process. We kept having to write procedural code inside the CustomerTest to manipulate the strings that make up a test. He had commented that the object wasnt helping us much, but we never followed up on it. We decided to think about it a little bit before we stopped for the day.



We Need an Object

Look at that code up there for SetInput. Either we have a two-pass algorithm or we have a complex setup to call a function that will be hard to write and that might still have a two-pass algorithm in it. Maybe I wasn t so wrong after all. The two-pass thing isn t as much a concern about speed, perhaps, as about our ability to express ourselves : we can t say what we want when we do it this way.

So we imagined some new object that holds input. We called it an InputCommand, and our proposed code looks like this:

private void SetInput(StringReader reader) {

InputCommand input = InputCommand(reader);
model.Lines = input.CleanLines();
model.SelectionStart = input.SelectionStart();

If we can do something like that, it will be quite clean! So we imagine an object that will take the input from the reader that holds the whole script. It will know how to produce the clean lines (the ones without the vertical bar), and it will know how to say where the SelectionStart (cursor location) is. We think we like this. And we re pretty sure it will be easy to write. Tomorrow we re going to start on it.

start sidebar
Lesson: Lessons Relearned Today

If You Can t Test It, It s Wrong

When we realized we couldn t readily test this code, we should have stopped coding. The lack of testability was telling us, long before anything else did, that there was at least one missing object.

Procedural Code Is a Very Bad Smell

When you find yourself writing procedural methods and utility methods and railing at how the objects aren t helping you, this, too, is telling you that there s a missing object.

Too Long Between Green Bars

After we made our one new unit test work, we went for a long time without a new test or a new green bar ”basically, all the time after lunch . This is at least a sign that you re stuck, and it should be used to trigger a rewind and a start in some new direction. Or even to start over in the same direction.

end sidebar

 



Conclusions for Today

We messed up. We finally figured out that we had. We backed up to last known good, maybe even going a little further back than we felt we had to. We did a quick retrospective and then called it a day. Maybe we wasted two or three hours, but we were confident that when we started over, everything would go much more smoothly.



Further Reflection

The next day, before starting the day s effort, Chet and I did a quick retrospective of the last day s, um, experience. We drew a couple of additional lessons over those above.

Program by Intention

We try always to write code that expresses our intention. (See, for example, the Test First, by Intention chapter in Extreme Programming Installed [Addison- Wesley, 2001].) Good code has methods with names that reveal their intention. And a good method that does two things procedurally needs to be refactored, using Extract Method, to express what those things are.

When we program, we all generally have some intention in mind: First, I ll scan through all the files, looking for the ones that are updated. Then I ll make copies of those. When programming by intention, we make ourselves aware of these intentions as they come to us. Sometimes we then immediately write, first, a few lines of code to get the files and then a few more to make the copies. When we do this, the code does not express our intention. Here s an example of some code, from our CustomerTest.cs, that doesn t express intention as well as it might:

private String[] ArrayToEnd(StringReader reader) {

ArrayList result = new ArrayList();
String line = reader.ReadLine();
while (line != null && line != "*end") {
result.Add(line.TrimEnd());
line = reader.ReadLine();
}
String[] answer = new String[result.Count]; result.CopyTo(answer);
return answer;
}

Let s figure out this code. First we build an ArrayList containing all the lines of the input StringReader, up to "*end" or the end of the reader. Then we allocate an array of Strings of the right size and copy the ArrayList into it. Then we return the array.

That code might be better if it looked something like this:

private String[] ArrayToEnd(StringReader reader) {

ArrayList result = ArrayListToEnd(reader);
return ArrayListToArray(result)
}

It might be even better with better names ”we could discuss that. In any case, we could argue that we should use Extract Method to refactor out those two methods, and Compose Method to create the new version of ArrayToEnd.

{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}

Programming by intention goes a step further than expressing intention after the fact. When we program by intention, we reflect on what we intend to do and we write the simple method first. If we know that we re going to create the array of strings by first creating an ArrayList and then copying it into an array of Strings, we say so. We write the short version of the method first, and then we write the two submethods .

Proceeding this way has a number of advantages. First, we don t have to remember so much. As soon as we know what we intend to do, we write it down. Then we just refine it until the code is there. Second, the code comes out well- factored without as much refactoring. We get better code, and it takes less time!

So why don t we do that all the time? We re not sure. We think it has to do with unfamiliarity with the language. We re so focused on how we might write legal statements in C#, and on which objects in C# might help us, that we forget to play our best game. Our first resolution is to return to programming by intention. Keep an eye on us, and see if we live up to our resolution.

Better Code Management

I might not have mentioned it before, but we have a simple code manager that we use as we develop this application. It s written in Ruby, and all it does is this: whenever we type cm in a command window, all the files of the source directory are copied to an archive directory. The file foo.cs, if dated today at 3:47:21 PM, will be named foo.cs.20020821154721 in the archive. All we have to do is type cm once in a while, and all our code is backed up. Works well enough for most of what we do, which is typically small programs. We have a little restore function, of course, that copies all the files back, with their value as of some given time.

You re probably asking yourself, if they used this cm thing, why did they have to Ctrl+Z yesterday to get back to a safe point? Did they have six saved versions or only five? Well, to tell you the truth, in all the excitement, we lost count ourselves. We forgot to type cm at crucial points in the development, so we didn t have a good point to restore to, other than the beginning of the day.

That was today s second resolution: have more points to back up to. That will make it easier to back up when we make a mistake, and we ll be less encouraged to keep plunging on, deeper and deeper into the depths of coding depravity.