Meditation on Software 1

[October 31, 2021]

There is something very wrong with how we write code.


1

For instance:

It takes an unreasonable amount of effort to do anything with software. And we still don’t do anything particularly well. How many millions of person-hours are spent on fixing bugs, or understanding confusing code?

And every significantly-sized project has enough complexity to require a team of specialists to support it, and still there is no useful overlap with the complexity of any other project.

And every company and organization is duplicating each other’s work because none of the solutions can be shared.

And every advance in hardware efficiency is canceled out by inefficiencies in software, so that everything is barely performant enough, all the time, and gobbles up any energy we have available to give it.

It is all working – kinda – in that humanity is churning out more software every day, solving problems and making money. But this can’t be ideal. This human race is spending too much human effort to make software that doesn’t work very well and doesn’t do very much.

I like to fantasize about how to do better.


2

Really it is that there is something medieval about how we write code. We are still in the software dark ages, like mathematics before algebra and calculus were discovered. The way software is written in five hundred years – if we haven’t run out of breathable air or microchips or whatever by then – will, I expect, be mostly unrecognizable compared to how it’s done today, and at best we are, as a species, 20% of the way along that path. (My guess is that we’re at like 15% overall and then React pushes the number to 20% in a few places.)

Here’s a test for assessing how good humanity is at writing software:

Suppose a spaceship of 1000 colonists is traveling to another star system, light-years away. And suppose this ship has to be totally self-sufficient, including having the ability to support all of its software systems, fixing bugs and improvising solutions to whatever comes up on the journey, and likely building out whatever is needed once they get there.

Can the colonists confidently expect to be able to handle whatever software challenges comes up?

The answer is definitely ‘no’. The answer needs to be ‘yes’ if we are to colonize other star systems. I don’t want to ship off to another star system only to die partway of an unfixable bug in the life-support system. There is no way you can fill out the roster of the ship with expert software engineers, and there is no way a roster of non-experts, even if they are geniuses in other fields, can be expected to understand even one part of the ship end-to-end.

So we have work to do. It’s probably possible, but it will take some serious advances to get there.


3

An analogy can be made to mechanical engineering. I don’t really know how my (gas-powered) car works. But if I open the hood and look at the engine, I feel like I have at least a hope of figuring it out. Apart from… the electronics… I can clearly tell which parts interact with which other parts, and approximately what they do to each other. Presumably if I take those parts apart I can tell how they work, approximately, internally, although I may not be able to put them back together again, or machine new parts of the same quality, without a lot of specialization.

But the fact I can make progress at all is valuable. If I took a long road trip away from civilization with just a box of tools and spare parts I have at least a hope of handling whatever comes up.

The difference, I think, is that physical machines are constrained by fundamental requirements of causality. For a widget to affect a gizmo, it has to, like, touch it, and there has to be some motive force between the two, which I can view and manipulate myself. Its physical interaction affords it a property of scrutability that allows me to make progress on understanding it. And if you take the widget apart, its internal components have the same property.1

Software today has no such property. It works exactly how it works, and good luck figuring it out from the outside.

The best I know of is browser devtools letting you view any website and see how it’s styled, but it barely counts. I hope that someday, figuring out any software is as natural as figuring out a physical machine like an old car engine.

As should be clear from this comparison, it’s not enough that software is open-source (although that’s a start). It must also be conceptualized and built in a way that makes causation clear and scrutable, and it needs to be split into scrutable modules that ‘push’ and ‘pull’ on each other in a way that we can follow. Most importantly, it needs to be constructed in such a way that allows for the digital equivalent of ‘opening up the hood and looking inside’, and we need to have the tools at hand to do so.


4

I don’t think any of what I’m looking for exists today, outside of, perhaps, one-off proprietary solutions. But if I had to throw out some ideas, here’s where I think progress is happening:

The best IDE I know of is Chrome Devtools, except for the fact that it doesn’t let you write code (or really search for it, or really modify anything in a way that doesn’t get reversed the next time a callback is triggered). But it does something the rest of them don’t, which is let you record every piece of code that’s run on a page and inspect it to see what happened. Nevermind that this process is janky and error-prone; at least it exists. There is no future in having to add print() statements to find out what your code did.

The most scrutable way of writing code that I know of is in React. The declarative model is the right way to reason about UI code. The React Devtools are reasonably good at looking at something while it’s running, and, in some cases modifying it. Hooks are better than any other way I’ve ever seen to reason about side effects, although in every case the whole philosophy is hamstrung by being implemented in Javascript and having to transpile to the DOM. And the problem of data processing and externalities is, as far as I know, still an unsolved problem, despite the efforts of the Redux ecosystem.

(Perhaps in the not-too-distant future there is a version of the React whose shadow DOM is the DOM, and which runs in a language that doesn’t require dependency arrays, and which has first-class types built-in instead of shimmed on top, and in which you can’t make the mistake of forgetting to bind a function to the appropriate this, and whose debugger lets you follow asynchronous effects that are scheduled on later render frames. Wouldn’t that be a dream!)

At least when it comes to UI, there is a future where React Devtools, Figma, and your IDE are the same piece of software. And I think that in this world, user-facing code no longer has anything like unit tests, because it’s a waste of time to meticulously test code when you can look at it and observe it’s correct.

The best shell I know of is, I guess, Python. Bash and its descendants are a disaster and the world would be better off if they were entirely replaced. In the future there is no way that we’re going to be working in languages that use $PATH variables, that pipe unformatted string data through bizarrely-named commands inflected by obscure flags, or that require strings like \u001b[31m to colorize text. I mean, my god. (Once upon a time I had high hopes for TermKit but it never really got off the ground.)

I am not sure what the future of type systems is, but I know three things about it: 1. Constructing natural numbers out of successor functions is an irrelevant gimmick. 2. There will no concept of ‘undefined behavior’ that survives the typechecker, because that’s insane. 3. Refinement types are going to happen at some point. It will be considered antiquated to use a language that can’t specify the type of ‘integers greater than 5” in some ergonomic way.

Finally, I know this: most of the code written today isn’t any good, compared to what will be possible in the future. It’s not possible in today’s ecosystems to write something scalable, maintainable, and resilient to errors. It’s up to the frameworks and paradigms to development the art of programming to the point where it’s actually an efficient and accessible craft instead of a massive timesink for the whole human race.

  1. Of course this falls apart when chemistry gets involved; you actually do need some specialized knowledge to make sense of, say, the actual combustion process. And I definitely don’t know much about engines so maybe it’s way harder than I think.