on the usability of editable software
I’m aware of two occasions on which Knuth advised editing existing code, as opposed to simply using it. One mention is in this interview, advocating for “re-editable” code instead of the fashionable reusable code, although it doesn’t amount to much more than that statement. In Coders at Work he describes a system of working that’s basically patch and diff. He writes a program, the master version that works for him, and ships it out. People receive and it and then modify it with change files so it works for them.
This is a rather different way to approach the needs of multiple users than the industry standard, which is to incorporate all the changes and variations anybody could want into a single version for everybody. If we consider some sort 80/20 rule where 80% of the users need 20% of the features, but it’s always a different 20%, this will have quite the impact on the size of our code base. I think that’s rather an exaggeration, with 15% features shared by nearly all users, and some variation in the extra. But conceptually, we could be shipping much smaller, simpler software if users could modify it to their own ends.
Some time ago I bought a dining room table and chairs. The first thing I did, immediately after taking delivery, was drive the chairs over to another business to be reupholstered. The originals weren’t bad, but they weren’t to my taste, and there weren’t any other chairs matching all of desired, affordable, and critically, available. So I bought what was available, then I had them modified, because that’s something you can do to things. Modify them.
Software can be modified as well. Or could be, if we didn’t go to such lengths to avoid it. There’s this notion that a software program needs to be finished, and polished, and work out of the box. That end users cannot be expected to be programmers. I’ll note that I did not reupholster my chairs by myself. They were basically functional as shipped, which certainly should be true of software as well, but I wasn’t forced to upgrade them entirely by myself. I could probably learn to be an upholsterer, but it was not necessary. Neither did I have to work with a specially trained upholsterer familiar with this model of chair. I took one sample over to the shop, he looked it over, and said yeah, I can do this no problem.
There are two forces at work leading to an unfortunate feedback loop in which software is difficult to modify, and then seeks to avoid modification, further complicating it. First, we don’t consider modification as a positive outcome for the user. We seek to package everything up to avoid local changes. Second, the way software is deployed causes local changes to be very expensive, creating pressure to push everything upstream. Neither of these must be the case however.
What if we made software with the expectation that end users would make at least a few changes? This would greatly simplify things. We don’t need to worry about every use case. Or even most use cases, really. Just provide the essential features. But isn’t that creating more work for end users? Not if the program is easy enough to work with. As anyone familiar with software development knows, the difficulty of adding new features or modifying existing ones grows very quickly, much faster than linearly, with the total number of features. They interfere with one another. By reducing the number of shipped features, we reduce the difficulty of modification. Anybody can do it (or have somebody do it for them).
The more users we try to appease out of the box, the harder things become for those we haven’t served yet. A more rigorous analysis would attempt to model costs and benefits, do the math, etc. I’ll leave it at noting that the combination of the 80/20 rule and superlinear complexity growth means we probably aren’t amortizing as much effort as we would hope by adding every feature to a single code base.
Another consequence of continuously adding features is that local changes become difficult to maintain. Every new release, local changes need to be merged and updated. The more releases there are, the faster the treadmill spins. The more each release changes, the steeper the treadmill incline. Very rarely do I see new development consider how local changes might be impacted.
An alternative approach to simpler editable software is complex programs with plugins. Witness browsers. Nobody wants to modify the browser itself, but maybe there’s a plugin, or extension, to do what you want. Unless the browser says your adblock list is too big. Or says the API you want is obsolete. And then going back into the main source tree to fix it so your extension still works the way you want is a major undertaking. There are other costs with large customizable programs as well. How often do we hear browser trouble, have you tried deleting your profile? Nobody knows what goes wrong, except it happens, and then you start over until next time.
How do we make editable software? Concretely? I’m still experimenting with this, but a couple ideas. Consider merging back not user submitted features, but the refactorings that make those features possible. This doesn’t require fully committing to abstract interfaces for every operation, but pulling some code that could be inline into a function allows someone to more easily replace that function in their tree.
Ultimately I think it’s at least worth experimenting to see if we can help more users by letting them help themselves.