Ali Rathore

June 2026

I built it one validation too early

The abstractions I have had to delete were not wrong, they were premature, built to answer a question the work had not yet asked.

I spent three weeks building the wrong thing well. I was making a system where a model generates an interface, and before I had watched a single person use it, I had given it a conflict-free replicated data type so that many people could edit at once, a kernel of fifteen composable block types, a state machine governing the transitions between them, and fourteen tools for the model to call. It was clean. It was tested. I deleted almost all of it and started over with plain reactive signals and a small relational document, and the rebuild did more in a fraction of the code. The first version was not bad engineering. It was good engineering aimed at problems I had invented.

The multi-user piece is the clearest tell. A replicated data type is the correct way to let several people edit one document without stepping on each other, and it is genuinely hard to retrofit, which is the reasoning I used to justify building it first. But nobody was editing together. There was one user, me, and there would be one user for a long time, and the entire apparatus existed to serve a second user who did not exist and might never. I had paid, in complexity that touched every other decision, for a guess about the future dressed up as foresight. The signal-backed rewrite has a clearly marked empty slot where sync will go if two people ever need it, and that slot has cost me nothing while it sits empty.

It keeps happening, and once I started looking I could see the same mistake under the other things I have thrown away. I built machinery to rewrite source code as you dragged elements around a canvas before I had established that dragging was even the right way to edit a generated interface, and it was not, so the machinery went in the bin with the gesture it served. I stood up a graph over a corpus to support the multi-hop questions I was sure people would ask, and the questions turned out to be joins and aggregates that a graph answers no better than the plain approach, so the graph was effort spent on a workload that never arrived. Each time, the abstraction was a bet that a class of problem existed, placed before I had seen a single instance of the class.

sync for many editorssource rewriting for draga graph for multi-hopbuiltdeleted
Each bar is what I built. The dark head is the part the problem turned out to need.

The reason premature is the right word, and not just wrong, is that the abstraction was sound and would have been the correct call at the moment its problem actually showed up. There is nothing incorrect about a replicated data type, a source rewriter, or a graph. I built each of them at a competent level. The error was entirely in the timing: I committed to the general mechanism while the specific need was still hypothetical, and a hypothetical need cannot tell you the shape the mechanism should take, so you guess the shape, and you guess it wrong, because the real problem when it arrives is never quite the one you imagined defending against.

I want to end this somewhere honest rather than tidy, because the obvious lesson, build the simple thing and abstract later, is wrong often enough to be dangerous. Some structure you genuinely cannot add after the fact. If a pipeline does not carry the source of every fact from the very first stage, no cleverness at the end recovers it, and there the early, expensive, seemingly premature commitment is the only one that works. So the rule is not “abstract late.” The rule is to know which abstractions are cheap to add the day you need them and which are impossible to add the day after, and to spend your foresight only on the second kind. I get that judgment wrong in both directions. The three weeks I deleted were the cheap kind I treated as the impossible kind, and I have no doubt I am, somewhere right now, making the reverse mistake and will find out when it is too late to add the thing.