For the purposes of this book, were declaring Undo to be done. We built our XML Notepad without preparing in any explicit way for Undo and then called upon ourselves to implement Undo anyway. We started with the simplest Undo we could imagine: make a copy of everything and slam it back when an unUndo is called for. This worked quickly and well, but we felt that it wasnt realistic as a solution, both because it was almost too easy and because it was clearly inefficient in storage. So we decided to optimize it.
Perhaps it was the focus on optimization, perhaps it was my reduced ability to think through pain, or perhaps it was just inexperience with doing Undo, but in any case, there were a couple of false starts, which you can read about in the next chapter. Each of these had the same characteristic, in retrospect. Each of the false starts tried to solve a fairly large problem in a fairly complex way. The problem was You are given two states of the notepadfind a representation that compactly represents the difference between the states. The solutions started by thinking about what could happen and by drawing conclusions about the state of the lines and selection before and after. Those conclusions were incorrect, and the mistakes in my thinking turned up in the code, usually rather late in the process.
In earlier days, I would have said, ruefully, that I should have designed more. Thats not my view now, and Id like to suggest that the history here shows why. Again and again during Undo, the best things happened when we thought and programmed at the same time, not first thinking and then trying to do what we thought. Now dont get me wrongI do think before I start, and I always used to think while I was programming a design that had been thought out beforehand. But I find that when I am working to an existing design, the more I understand that design, the more I try to make it work outeven if in fact it cannot work out. I am less likely to listen to what the code is telling me, because I feel committed to the design.
In the version of optimized Undo that worked well, there was also a difference of focus. Instead of thinking of the two big snapshots and how to reconcile them, we looked at what kinds of events we could deal with and then dealt with them. We focused on the biggest improvement first, namely, ordinary typing.
The most important thing to observe about the Undo story is that the systems overall design did not get in the way, and we did not have to make sweeping changes all over the system to make Undo work. Now it might be that we were lucky, or that subconsciously we were somehow preparing for Undo. I dont think thats credible: as you have seen in the course of the book, I can barely program consciously! It might even be that we somehow cheated, but again, I think the evidence is clear that Im not that clever.
Frankly, I was concerned that the result would be other than it has turned out. Especially with my early attempts at Undo turning out badly , I was starting to worry. Yet even when I was having trouble, the trouble wasnt with the existing designit was with the inherent difficulty of Undo. Certainly I could have spread the work out over time, but it would have been just as difficult and perhaps more so, when the systems design was more vague. When you read the next chapter, keep in mind that during all that trouble, Undo was already working, and I got in trouble trying to make it better.
I think we see here that incremental development works well, evolving from simple design by refactoring, with our primary focus on removing duplication. I believe that it works because modularity works. When we have good abstractions in the code that make sense, they can usually be assembled in any order that we need, and they can usually be enhanced by adding new abstractions that come up. And simple design plus refactoring creates modularity.
Youll have to draw your own conclusions about your own work, but my conclusions are that simple design, combined with continuous design improvement, results in code that is easy to change when new requirements, even tricky ones like Undo, come along. As Ill talk about in Chapter 31, Project Retrospective, the code we have isnt perfect by any means. Yet our focus on removing duplication, on making common ideas use common code, has served us well enough. Undo went into the system as an addition and as a rather tricky one at that. But it did not bend or break the design in any way. Undo is difficult on its own, but our design accepted it without needing to be changed in any substantial way.
Your mission is to decide for yourself. This extensive Undo section is evidence that might induce you to experiment. Your own experiments will help you decide the right balance for you. Now read on and look at my mistakes!