Writing IV

Boring choices in operational software

A defence of Postgres, cron, and server-rendered HTML in the era of agentic everything.

29 April 2026 6-minute read

There is a type of conversation we end up in maybe three times a quarter. The setting is a partner’s office or a Zoom call with two screens shared. The firm has decided to commission a piece of operational software. The partner has read about LangGraph or Inngest or Temporal or some new agentic framework and would like to know whether we recommend it. The implication is that, of course, we will recommend it.

We will not recommend it. We will recommend Postgres, cron, and server-rendered HTML. The partner will be slightly disappointed.

This essay is for the partner.

The framing problem

A piece of operational software has a much longer life than the technology around it. If we ship a document-classification pipeline for a six-partner accounting firm in 2026, that pipeline will, with reasonable luck, still be running in 2031. The associate who joins the firm in 2030 and is asked to add a new document type to the pipeline will not have heard of the framework that was state-of-the-art when we wrote the original code. They will, however, certainly have heard of Postgres.

The architecture choice is not really a choice between technologies. It is a choice about who you expect to be reading the code in five years. If the answer is “the original engineer,” novelty is fine. In professional services firms, the answer is almost always “someone else.” Everything follows from that.

The boring stack

We default to the same shape on most engagements. It looks roughly like this:

A Postgres database. Tables, columns, indexes, foreign keys. The schema lives in version control as plain SQL migration files. Someone unfamiliar with the project can read the schema and understand what the system thinks the world looks like, in about ten minutes.

A small server that responds to HTTP. We write it in TypeScript or Python depending on what the firm’s existing engineers use. The server reads from Postgres, writes to Postgres, and renders HTML. It does not run a single-page application unless we have a specific reason; the back button works correctly without us writing any JavaScript to make it so.

A scheduled task runner. In production this is usually cron, sometimes a slightly more featured equivalent like Postgres-backed pg_cron or a small queue table polled by a worker. Tasks are written as command-line scripts that read from the database, do their work, and exit. They can be run by hand for debugging. They produce log lines. The log lines are timestamped and human-readable.

If the system needs to call a language model, it does so through a thin wrapper that records the prompt, the response, and the latency, against the user, the request, and the version of the prompt. The prompt itself is stored as a file in the repository. There is an evaluation harness that runs against fixed inputs whenever the prompt changes.

That is most of it. There are no microservices. There is no message bus. There is no service mesh. There is, especially, no agent framework with a name suggesting it can think.

What this saves

The most expensive thing in operational software is not the initial build. It is the next ten years of someone trying to understand what the original system was supposed to do.

The boring stack saves us from this in several ways at once. The schema is the documentation. The HTML is the UI specification. The cron logs are the runtime trace. The prompts are tracked the way any code change is tracked. Anyone with a working knowledge of these tools can, with a few hours of orientation, understand the system well enough to extend it safely.

It also saves us from a class of failure that gets unfairly blamed on AI. When a language-model-driven feature misbehaves in a system built on agentic frameworks, the partner asks the engineer why and the engineer, often, cannot say. The framework is doing something opaque on top of the model, and the engineer is reading framework documentation rather than reading what actually happened. In our boring stack, the answer to “why did the system make that decision” is always a row in a table, with a timestamp and a request ID.

When we break the rule

There are cases where the boring choice is wrong. We do not pretend otherwise.

If the firm’s volume genuinely requires concurrent agentic workflows that need to coordinate across long durations and survive process restarts, a workflow engine like Temporal earns its complexity. We have built on it for one engagement. The triggering condition was a workflow that was already, in its manual form, multi-day, multi-actor, and durable; the framework matched the problem.

If the system needs full-text and semantic search over a body of documents the firm wants to query in natural language, we will reach for pgvector inside Postgres rather than a separate vector database, because that is still the boring choice for most workloads at the scale a partnership actually has. We will only introduce a dedicated vector store if the dataset is large enough to require it.

If a language-model feature’s correctness depends on multi-step reasoning that we genuinely cannot decompose into a sequence of single-shot calls, we will write the multi-step orchestration ourselves rather than adopt an agent framework, because writing it ourselves means we own the failure modes.

What this looks like to a partner

What a partner sees, with this approach, is a system that runs in the background and stays out of the way. It does not need to be redeployed every time a dependency releases a new major version. It does not require expertise that only the firm’s original vendor possesses. It is auditable, in the literal sense that you can read what it did and why.

Five years from now, when the agentic frameworks of 2026 have either consolidated into something stable or been replaced by something different, the firm’s operational software will still be running, because the parts of the system that matter most to the firm are written in technology that does not change.

That is what we mean by boring. It is not a constraint. It is a deliberate aesthetic, and it is an investment in the firm’s ability to keep using what it owns.