The first part of How Software Is Built Today focused on polishing a detail of the user interface. Polish matters, but a lot of our software these days is all on the surface. That’s great for tiny applications that everyone needs, like an alarm clock or a telephone dial, because everyone can share the costs of getting everything exactly right.
But Tinderbox Six isn’t an alarm clock. There are lots of moving parts behind the user interface. Some people always think that’s a sign of malign design, that everything should be made simple. But things aren’t always simple, and anyway the people who want simple software are the people who hate us.
The point is to make things as simple as they can be, and no simpler. Everyone’s everyday knowledge work is incredibly complex; if we want to even begin to help with that, we’re going to have some complexity. Here’s an example of another piece of Tinderbox and its evolution.
A Tinderbox note is a collection of lots of attributes, each of which has a value. The name of the note is an attribute called $Name. The note has a $Color. It might have a $Prototype. If it has some $Text, you can find out things about it like its $WordCount. Because everything’s a value, it’s easy to say things like “Make all my overdue tasks red,” or “Keep old reminders for a month, and then move them to the archives.”
Or, to take an example from the recent discussion of Book Notes In Tinderbox, “Assume that I start reading a book the day I enter it in Tinderbox. If I delay for some reason, I’ll correct it later.”
Now, from the very beginning I could see that we’d need several kinds of values:
and we need to be able to move easily between them:
Value *urlValue=new URLValue(…);
string s=urlValue->AsString()
We can ask any kind of Value to represent itself as a string, a number, a Boolean, whatever.
Originally, everything in Tinderbox used Values directly, so the code was filled with things like this:
Value* v=Get(“Xpos”);
int x=v->AsNumber();
And these eventually get written as Get(“…”)->AsNumber()
. And that leads in turn to Get(“…”) ->AsAction() -> EvaluateFor(thisNote,withThatContext)
. The chain of objects starts to read like a short story.
Now, these chains violate something called The Law Of Demeter, which everyone agrees isn’t really a law. It’s really The Suggestion of Demeter. But at some point, all those arrows means that the object you’re working on – say, the object that knows how to draw the bevels around the edge of a note – needs to know all about how actions work, and how values work. That’s a lot of knowledge for a gizmo that paints bevels. Pretty soon, everything needs to know about everything. That’s lousy design.
So we can hide all almost everything about Value inside the Note class. Now, no one calls Get(“…”) to get a value; instead, you call GetString(“…”) to get a string, and it gets the value for you. This is like Don Draper and the phone; he doesn’t fuss with a phonebook, he asks a secretary to call Joe. So, now hardly anything needs to know about Value itself, and that’s good because Value gets complicated and tricky. (The disadvantage of having everything be a value is that everything is a Value, so there are lots of values and lots of little improvements in values. A lot of the time, when a new Tinderbox has come out and it’s a lot faster, that’s been some sort of improvement in handling Values.)
The disadvantage, though, is that where we used to have one all-purpose method to Get() a value, now we have GetString() and GetNumber() and GetURL() and whatnot. And everything that has values needs to have each of these methods. And they all have to work, and they all have to be fast. That’s lousy design too – just a little less lousy than the first one.
Most of the time, a note has a value. We just want to look at it, almost always to convert it to a string or a number or an aardvark. So the note owns a value, and Get() just takes a peek. But there are a few attributes where that doesn’t work. For example, $WordCount might change at any moment, either because you’re typing, or because some rule changed the text. We don’t want to make a new Value (and delete the old one) every time you press a key. So we make a new Value object for $WordCount when someone asks for it, and hand it to whoever wants a peek. But we still own that Value, even though we’ll never use it again. So we have a deal with the customer:
We hold onto ownership of the temporary Value we just created. You can do whatever you want with it, but you can’t keep it; you have to give it back right away, because you’re just looking. And once you give it back, we’ll hang onto it until someone else asks for a $WordCount, and when they do we’ll delete the old Value and make a fresh one.
And that works just fine. Except there’s been one special attribute that forgot to do this back in 2006 or so. It just started giving away Values to whoever came by, handing them out like canapés. This created a small memory leak, one that was a bear to track down because it’s small and it only leaks when you use this specific attribute.
Now, I got started with lab software for tiny embedded computers with almost no memory to spare, so I’m really fierce about leaks. But we’ve been leaking these little values for years, and now that everybody has gigabytes of memory, hardly anyone noticed. But it’s bad style, and today (finally!) I found it.
Over time, hunting this has taken days of work. (You could say that we should have caught it with unit tests, but we didn’t do Test Driven Development in 2002 and neither did you.) Again, as in Part I, the business case for doing it right is weak. (It was weaker still in 2002, when no one’s computer stayed up for the weeks it takes for the leak to make a dent, even if you’re using the defective Attribute.) It’s even harder to recover the costs here. (Yes, getting it right in the first place would have been nice, but there are currently 24 subclasses of Attribute and that’s a tricky number – big enough that one is bound to be wrong, but too small to justify automating the subclass generation or to merit condensing all those classes into the parent.)
What puzzles me most is, where do people learn how to do this? When to extract classes and when not, when to ignore Demeter, when to hunt that last leak? I try sometimes to talk to people at conferences, but mostly this kind of implementation is not what they do. I tried this years ago at a hypertext workshop and got blank stares — partly because, even at the Hypertext conference, hardly anyone was building systems anymore. In a busy year, the roster might have included Cathy, Frank, Haowei, Polle, Peter, Claus, Kumiyo, and me. I think there have been years when I was pretty much it.
This also makes me marvel at the ability of someone like Keith Blount (Scrivener) who learned this craft so quickly, with so few resources available, because he wanted to get something built.
It ought to be simpler, but no simpler than it can be.
This longish post has about 1362 words, which I found out by checking $WordCount. It was composed and edited entirely in Tinderbox Six, which stayed up throughout. Another milestone.