Meditation on Software 1

[October 31, 2021]

1

In addition to pretending to know something about abstruse mathematics I have a day job where I pretend to know something about software development. And as with abstruse mathematics, I’m constantly frustrated with it. I think there is something very wrong with the way we write code.

It seems to take an unreasonable amount of effort to do just about anything with software. And we still don’t seem to do anything particularly well (how many millions of person-hours are spent on fixing bugs?).

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

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

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

I suppose it is all working, kinda, in that humanity is churning out more software to solve problems and make money every day, but this just can’t be ideal. The human race is spending too much human effort to make software that doesn’t work very well and simply doesn’t do very much. I like to fantasize about how to do better.


2

Or perhaps it is that there is something medieval about how we write code, and we are still in the dark ages, like mathematics before algebra and calculus were discovered. I suspect that the way software is written in, say, five hundred years – if we haven’t run out of breathable air or microchips or whatever by then – will be mostly unrecognizable compared to how it’s done today, and at best as a species we are like 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 hypothetical test for assessing how good humanity is at writing software:

Suppose we send a spaceship of, say, 1000 colonists off 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.

The question is: can the colonists confidently expect to be able to handle whatever software challenges comes up?

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. But today it is definitely ‘no’. 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 otherwise geniuses, 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 might be made to mechanical engineering. If I open the hood of my (gas-powered) car and look at the engine, I don’t know how it works, but I feel like I have at least a hope of figuring it out. Apart from, well, 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, in the same way, 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, for the most part, no such property. The best I know of is browser devtools letting you view any old website, but it barely counts. My hope is that someday making sense of any software is as straightforward as making sense of 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:

  • Constructing natural numbers out of successor functions is an irrelevant gimmick.
  • There will no concept of ‘undefined behavior’ that survives the typechecker.
  • 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. But it’s just an illustration.