<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ben Lai — Blog</title><description>Notes from the desk and the road.</description><link>https://databookman.com/</link><language>en-us</language><atom:link href="https://databookman.com/feed.xml" rel="self" type="application/rss+xml"/><item><title>Local models won the long tail</title><link>https://databookman.com/blog/local-models-won-the-long-tail/</link><guid isPermaLink="false">https://databookman.com/writing/local-models-won-the-long-tail/</guid><description>The frontier wins demos. A 70B model on one good GPU wins the two hundred calls per day workflow nobody tweets about. The economics flipped this year.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was on a call with a CTO who runs a developer-tools company. They started with GPT-4 for everything. Over the last six months they moved seventy percent of their volume to a local model running on a single H100. The remaining thirty percent is on Claude or Gemini, depending on the task. Their inference bill went from thirty-four thousand dollars a month to twelve hundred.&lt;/p&gt;
&lt;p&gt;This is not a story about whether local models match frontier on paper. They don’t, on paper. It’s a story about where the actual call volume goes once a product ships. The frontier handles the hard five percent — the open-ended generation, the genuinely novel reasoning, the user-facing chat. The other ninety-five percent is classification, extraction, formatting, retrieval reranking, summarisation of structured input. A 70B-class model finetuned on the company’s own traffic for an afternoon will outperform a generic frontier model on those tasks, because the eval set is the company’s actual data.&lt;/p&gt;
&lt;h2 id=&quot;what-changed&quot;&gt;What changed&lt;/h2&gt;
&lt;p&gt;Three things made the math obvious in the last twelve months:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open-source quality.&lt;/strong&gt; The current generation of open weights closed enough of the quality gap on standard tasks that the cost difference stopped being defensible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inference engines.&lt;/strong&gt; vLLM and friends made batched throughput on a single GPU competitive with managed inference at lower volumes than people realised.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Finetuning got cheap.&lt;/strong&gt; A QLoRA on five thousand of your own examples, on a rented A100 for three hours, beats prompt engineering for almost any narrow task.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-diagnostic&quot;&gt;The diagnostic&lt;/h2&gt;
&lt;p&gt;Pull a week of your highest-volume agent endpoint. Categorise the requests by task type. If sixty percent or more fall into one or two narrow categories, you are paying frontier prices for a problem a finetune solves better.&lt;/p&gt;
&lt;p&gt;The frontier model will still be in your stack. It just shouldn’t be in your hot path.&lt;/p&gt;</content:encoded><category>cost</category><category>infra</category><category>llm</category></item><item><title>Customer support is product research wearing a costume</title><link>https://databookman.com/blog/customer-support-is-product-research/</link><guid isPermaLink="false">https://databookman.com/writing/customer-support-is-product-research/</guid><description>I answered support tickets for the first six months of the company. Then I stopped, and within a quarter we shipped three features nobody asked for. The inbox knew what to build. I had stopped reading it.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I answered support tickets for the first six months of the company. Then I stopped, and within a quarter we had shipped three features nobody asked for. The inbox knew what to build. I had stopped reading it.&lt;/p&gt;
&lt;p&gt;Support is the cheapest, highest-signal product research function a small company has. Customers tell you precisely what’s wrong, on their own time, in their own words. They give you a paid panel of users who are willing to write down what frustrates them — for free. Then most teams hand the inbox to a CS hire whose job is closing tickets fast, and the founder never reads it again.&lt;/p&gt;
&lt;h2 id=&quot;what-you-lose-when-you-outsource-it&quot;&gt;What you lose when you outsource it&lt;/h2&gt;
&lt;p&gt;Three things, in order of which one hurts first:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The wording.&lt;/strong&gt; Customers describe problems in language your team doesn’t use. That language is also how prospects describe the problem on calls. You lose the vocabulary if you only see closed-ticket summaries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The frequency.&lt;/strong&gt; “How often does X come up” is the question every roadmap argument turns on. The CS team knows the answer; the founder learns it third-hand and gets it wrong.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The shape of the bad questions.&lt;/strong&gt; The questions that “shouldn’t” be there — “how do I X” when X is documented — are the ones where your docs failed. You only see those if you read tickets.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-the-founder-should-actually-do&quot;&gt;What the founder should actually do&lt;/h2&gt;
&lt;p&gt;Read tickets for an hour every Friday. Not closing them — reading them. Look for: the same question asked three different ways, the workaround a customer described that’s better than your intended flow, the feature request that’s actually a bug they’re working around.&lt;/p&gt;
&lt;p&gt;The CS team can close the tickets. The founder reads the inbox. These are not the same job.&lt;/p&gt;
&lt;h2 id=&quot;the-honest-part&quot;&gt;The honest part&lt;/h2&gt;
&lt;p&gt;It’s tempting to stop because tickets are unpleasant. Some are angry. Some are confused. Some are written badly. That’s the job. The unpleasantness is the signal. A company where the founder thinks “support has it under control” is a company that’s shipping the wrong things, on time.&lt;/p&gt;</content:encoded><category>support</category><category>product</category><category>founders</category></item><item><title>The eval you don&apos;t have, production already wrote for you</title><link>https://databookman.com/blog/the-eval-production-already-wrote/</link><guid isPermaLink="false">https://databookman.com/writing/the-eval-production-already-wrote/</guid><description>Every customer-reported failure of an LLM feature is a test case you refused to file. The dataset is free; you&apos;re throwing it away nightly.</description><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I asked a team how they evaluated their agent. They had a deck. The deck had four slides about MMLU and one chart of an internal benchmark with twelve examples on it. I asked how the agent was doing. They pointed at the chart. I asked how it was doing in production. There was a pause.&lt;/p&gt;
&lt;p&gt;The answer was in their support inbox. Forty tickets a week, half of them screenshots of the agent saying something wrong, the other half of the agent doing something wrong. None of those tickets had ever been turned into an eval. They were rotting in a Slack channel where they’d be deleted in ninety days.&lt;/p&gt;
&lt;h2 id=&quot;the-dataset-you-already-own&quot;&gt;The dataset you already own&lt;/h2&gt;
&lt;p&gt;Three sources, in order of how cheaply you can mine them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Human-edited outputs.&lt;/strong&gt; Every time a user accepted, then edited, the agent’s draft, you have a labelled example: input → bad output → corrected output. Capture both halves.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thumbs-down with comments.&lt;/strong&gt; The thumb is noise. The comment is gold. Most teams aggregate the thumbs and throw away the text.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Re-asks.&lt;/strong&gt; When a user immediately re-prompts after the agent answered, that’s a failure signal too. Log them. Group them. Ten re-asks of the same shape is a category.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;why-nobody-does-this&quot;&gt;Why nobody does this&lt;/h2&gt;
&lt;p&gt;Because there is no glamour in writing eval cases from a support queue. It feels like data entry. It is data entry. And it’s the only way your evals will ever look like the requests your users actually send, instead of the requests your team made up at offsite.&lt;/p&gt;
&lt;p&gt;The team with the synthetic benchmark and the great-looking dashboards is not the team with the working agent. The team with the working agent has two thousand messy real-world cases pulled from production every week, and someone whose job is to keep that pipeline alive.&lt;/p&gt;
&lt;p&gt;Your customers wrote your test set. You just have to take it.&lt;/p&gt;</content:encoded><category>evals</category><category>agents</category><category>quality</category></item><item><title>Observability is a tax you pay before you owe it</title><link>https://databookman.com/blog/observability-is-a-tax/</link><guid isPermaLink="false">https://databookman.com/writing/observability-is-a-tax/</guid><description>The first incident is when you discover you don&apos;t have logs. The second is when you discover the logs aren&apos;t searchable. By the third you have observability, and a story about how expensive it would have been not to.</description><pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The first incident is the one where you discover you don’t have logs. The second is the one where you discover the logs you have aren’t searchable. By the third, you have observability, and a story about how expensive it would have been to not have it.&lt;/p&gt;
&lt;p&gt;Observability is a tax. It costs money in tooling, time to instrument, and discipline to keep current. There is no quarter where adding tracing to a new service is the feature the customer asked for. There is no demo for a histogram of p99 latencies. The work is unglamorous, and like most unglamorous work it pays in incidents you don’t have.&lt;/p&gt;
&lt;h2 id=&quot;the-minimum-you-owe-yourself&quot;&gt;The minimum you owe yourself&lt;/h2&gt;
&lt;p&gt;Three things, in the order I’d add them to a fresh project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;A request ID that travels.&lt;/strong&gt; Every request gets one. Every log line carries it. Every error carries it. The first thing a customer sends you when something is wrong is a screenshot — the request ID is what turns the screenshot into a trace.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One graph that says “is the site working right now.”&lt;/strong&gt; Not ten. Not a dashboard with twenty panels. One number a non-engineer can look at and answer yes or no. The rest is debugging support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An alert on user-visible failure, not on system noise.&lt;/strong&gt; “Error rate above 1%” with a sane window. Not “CPU above 70%” — that wakes you up for a thing your customers don’t care about.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-you-can-wait-on&quot;&gt;What you can wait on&lt;/h2&gt;
&lt;p&gt;Three things, in roughly the order most teams obsess over and shouldn’t:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Tracing every internal call.&lt;/strong&gt; You don’t need spans inside spans inside spans. You need the request boundary, the database boundary, and the external-API boundary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom metrics for every business event.&lt;/strong&gt; Add them when an incident shows you you needed one. Anticipating is more expensive than the incident.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The fancy SaaS tier.&lt;/strong&gt; Most teams hit revenue-relevant problems on the free tier. The paid features are real, but they’re cheaper to add later than to maintain early.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The trick is to instrument early enough that you have ground to stand on during an incident, and late enough that you didn’t waste six months gold-plating telemetry for a service that got rewritten.&lt;/p&gt;</content:encoded><category>observability</category><category>ops</category><category>software-craft</category></item><item><title>One worktree per agent</title><link>https://databookman.com/blog/one-worktree-per-agent/</link><guid isPermaLink="false">https://databookman.com/writing/one-worktree-per-agent/</guid><description>Sub-agents and tool-loop trees are not parallelism. Git worktrees are. The unit of useful concurrency in a coding swarm is the working tree, not the model call.</description><pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Watched someone’s coding-agent demo last month. Six “agents” working in parallel on the same codebase, supposedly. Three of them were stepping on the same files. One was waiting on the other’s edit to compile. Two were debating whose change to keep. The aggregate throughput was lower than one engineer running quietly.&lt;/p&gt;
&lt;p&gt;What was missing was the cheapest piece of plumbing in git: &lt;code&gt;git worktree add&lt;/code&gt;. A worktree gives each agent an independent checkout of the same repo on the same disk, sharing the object store, isolated in everything that matters. Nothing about the tooling around AI coding has caught up to this yet, even though the primitive is twenty years old.&lt;/p&gt;
&lt;h2 id=&quot;what-changes-when-each-agent-owns-a-tree&quot;&gt;What changes when each agent owns a tree&lt;/h2&gt;
&lt;p&gt;Three things, in order of how often they break a swarm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No file-level conflicts.&lt;/strong&gt; Two agents can edit the same file in different trees and the merge happens at PR time, not at edit time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No build-state interference.&lt;/strong&gt; One agent’s &lt;code&gt;cargo build&lt;/code&gt; doesn’t poison another’s &lt;code&gt;target/&lt;/code&gt;. Test runs don’t race.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No “did I save?” doubt.&lt;/strong&gt; Each tree is a real branch. The agent’s work is a commit in its own history, not a guess at what’s currently on disk.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-merge-step-is-the-actual-product&quot;&gt;The merge step is the actual product&lt;/h2&gt;
&lt;p&gt;The reason most parallel coding setups underperform is not the agents. It’s that nobody built the merge. Three independent worktrees that all open three independent PRs and then sit there waiting for a human is not a swarm — it’s a backlog.&lt;/p&gt;
&lt;p&gt;If you want parallel coding agents to be more than a demo, the missing piece is an automated merge agent. It reads the three PRs, identifies overlap, asks the model to resolve textual conflicts, runs the test suite, and either merges or hands you one synthesised PR with the open questions surfaced.&lt;/p&gt;
&lt;p&gt;Until that exists, three agents in three worktrees are the same throughput as one agent that knows how to commit.&lt;/p&gt;</content:encoded><category>agents</category><category>dev-workflow</category><category>git</category></item><item><title>Your first hire is a multiplier or a manager. Pick.</title><link>https://databookman.com/blog/your-first-hire/</link><guid isPermaLink="false">https://databookman.com/writing/your-first-hire/</guid><description>The two failure modes of first hires are &quot;the second me&quot; who does what I do twice and &quot;the senior person&quot; who manages me instead of working. The good first hire does neither.</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The two failure modes of first hires are “the second me” who does what I do twice, and “the senior person” who manages me instead of working. The good first hire does neither. They take work I can’t already do, and they refuse to manage.&lt;/p&gt;
&lt;p&gt;This is not a hot take. It’s the version of every founder-hire blog post that founders ignore because they have a specific candidate in mind and the candidate sounds great. Three months later they’re paying twice for the same skill and meeting four times a week to discuss why.&lt;/p&gt;
&lt;h2 id=&quot;the-multiplier-hire&quot;&gt;The multiplier hire&lt;/h2&gt;
&lt;p&gt;A multiplier extends the surface area of what the company can do. You’re a backend engineer; they’re a designer-engineer who can ship a landing page and a Figma in a week. You’re a closer; they’re an integrator who can do the post-sale handoff. You can do half their job, badly. They can do half of yours, decently. The Venn overlap is small, and that’s the point.&lt;/p&gt;
&lt;p&gt;The check: ask the candidate to describe a project they did entirely alone, end to end. If the answer fits your current gap, hire. If the answer sounds like “I led a team that…”, stop.&lt;/p&gt;
&lt;h2 id=&quot;the-manager-hire&quot;&gt;The manager hire&lt;/h2&gt;
&lt;p&gt;A manager hire is correct exactly once: when you already have three people doing different jobs, you can’t be the bottleneck for all of them, and a person whose primary skill is unblocking other people will replace more of your week than they cost. That moment is later than founders think. It is usually employee five or six, not two.&lt;/p&gt;
&lt;p&gt;The check: ask yourself how many people the manager hire would manage on day one. If the answer is “you and one contractor,” the answer is “not yet.”&lt;/p&gt;
&lt;h2 id=&quot;what-to-do-instead&quot;&gt;What to do instead&lt;/h2&gt;
&lt;p&gt;Hire the second person who can ship something you can’t. Hire the third the same way. Resist the “we need a head of X” instinct until you can clearly point at three people who would have a clear week of work for that person to coordinate.&lt;/p&gt;
&lt;p&gt;By then you’re not making the first hire anymore. You’re making the fifth. The decision is much easier when you’re not lonely.&lt;/p&gt;</content:encoded><category>hiring</category><category>teams</category><category>founders</category></item><item><title>The output token tax</title><link>https://databookman.com/blog/the-output-token-tax/</link><guid isPermaLink="false">https://databookman.com/writing/the-output-token-tax/</guid><description>Inputs got cheaper this year. Outputs didn&apos;t. Your verbose agent is paying both halves of a bill that quietly stopped being symmetric.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I looked at last quarter’s bill for an agent that summarises ticket queues for a forty-person engineering team. The input cost was flat against the year before. The output cost had tripled. Nothing about the workload had changed.&lt;/p&gt;
&lt;p&gt;Two things happened to LLM pricing in the last twelve months. Input prices kept dropping — providers competed for context-window land. Output prices stopped dropping, and on some tiers went up. Reasoning models charge separately for the thinking tokens you never see. The asymmetry is now five to ten times: a token in costs ten cents per million, the same token out costs a dollar.&lt;/p&gt;
&lt;p&gt;Most agent code was written when the two sides cost roughly the same. So the prompts ask the model to “think step by step” and “explain your reasoning” and “return the answer in JSON with a &lt;code&gt;rationale&lt;/code&gt; field”. Each of those instructions is a tax on the most expensive part of the bill, paid every call, forever.&lt;/p&gt;
&lt;h2 id=&quot;where-the-tax-is-hiding&quot;&gt;Where the tax is hiding&lt;/h2&gt;
&lt;p&gt;Three places, in roughly descending order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Reasoning fields you never read.&lt;/strong&gt; The agent fills in &lt;code&gt;rationale&lt;/code&gt; so the prompt feels rigorous. Nobody reads it downstream. Delete the field.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restated context.&lt;/strong&gt; “Based on the user’s request to update their billing address, I will…” — the model is parroting the input back at output prices. Tell it not to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verbose tool plans.&lt;/strong&gt; “First, I will call &lt;code&gt;get_user&lt;/code&gt;. Then I will call &lt;code&gt;update_address&lt;/code&gt;. Then…” Multi-tool reasoning loops generate output before each call. Move the planning to a single &lt;code&gt;extract_intent&lt;/code&gt; call and let the executor be silent.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-diagnostic&quot;&gt;The diagnostic&lt;/h2&gt;
&lt;p&gt;Print the average output tokens per call from your fleet. If it’s bigger than 200, you have prose where you wanted JSON. If it’s bigger than 1,000, you have an essay generator.&lt;/p&gt;
&lt;p&gt;Cut prompts. Tighten schemas. Stop asking models to think out loud unless you want a transcript. The bill rewards brevity now in a way it didn’t a year ago.&lt;/p&gt;</content:encoded><category>cost</category><category>llm</category><category>agents</category></item><item><title>Postgres is a queue. Stop reaching for Kafka.</title><link>https://databookman.com/blog/postgres-is-a-queue/</link><guid isPermaLink="false">https://databookman.com/writing/postgres-is-a-queue/</guid><description>A team I know spent six weeks operationalising Kafka for a workload doing two hundred messages per second. Postgres did the same job with forty lines and a SELECT FOR UPDATE SKIP LOCKED.</description><pubDate>Fri, 15 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A team I know spent six weeks operationalising Kafka for a workload doing two hundred messages per second. Three nodes. ZooKeeper. Schema registry. Two engineers who had not used it before, becoming the on-call for it. They were proud. They had a job-queue product.&lt;/p&gt;
&lt;p&gt;Postgres did the same job in forty lines of code. &lt;code&gt;SELECT FOR UPDATE SKIP LOCKED&lt;/code&gt; is in every recent version. Throughput on a &lt;code&gt;t3.medium&lt;/code&gt; is in the low thousands of messages per second without breaking a sweat. Delivery semantics are exactly-once because you’re inside a transaction. The thing you’ve already got running and paying for can do this.&lt;/p&gt;
&lt;h2 id=&quot;when-postgres-is-enough&quot;&gt;When Postgres is enough&lt;/h2&gt;
&lt;p&gt;Three conditions, in roughly decreasing order of generosity:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Throughput under a thousand events per second.&lt;/strong&gt; Most internal queues live here. The “we might scale” argument fails on the math — a tenfold growth still fits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Producers and consumers are on the same database.&lt;/strong&gt; If the consumer needs to update other tables when it processes, doing it in one transaction is the killer feature you give up by moving the queue out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You can tolerate a Postgres outage taking the queue down too.&lt;/strong&gt; For most internal workloads, your app already can’t survive a Postgres outage. The queue going with it doesn’t change your availability story.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;when-you-actually-need-kafka&quot;&gt;When you actually need Kafka&lt;/h2&gt;
&lt;p&gt;Three real reasons, in order of how rarely they apply:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multi-consumer fan-out with replay.&lt;/strong&gt; You want N independent consumers each reading the same events, with the ability to rewind. Postgres can simulate this badly; Kafka does it natively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sustained throughput over ten thousand events per second&lt;/strong&gt; with low producer-side latency requirements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You already have a team running Kafka.&lt;/strong&gt; This is the most honest reason and the one nobody says out loud.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If none of those apply, you do not need Kafka. You need a &lt;code&gt;jobs&lt;/code&gt; table, a small daemon that polls it, and a Sunday afternoon.&lt;/p&gt;
&lt;p&gt;The point isn’t that Kafka is bad. It’s that the boring option is usually load-bearing in ways the exciting option won’t be for another two years. By then the team that picked boring shipped twice.&lt;/p&gt;</content:encoded><category>postgres</category><category>infrastructure</category><category>simplicity</category></item><item><title>Every agent should have a passport</title><link>https://databookman.com/blog/every-agent-should-have-a-passport/</link><guid isPermaLink="false">https://databookman.com/writing/every-agent-should-have-a-passport/</guid><description>We onboard humans with identity, scope, audit, and approval. Agents get an env var and a system prompt. The next round of incidents will be about this.</description><pubDate>Wed, 13 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We onboard a junior engineer with an email, an MFA token, a scoped role, and a manager who has to sign off when they touch production. Then we deploy an LLM agent with a system prompt and an API key and wonder why audit logs look like a haunted house.&lt;/p&gt;
&lt;p&gt;We have spent fifteen years arguing about identity for humans. Every security conference has a slide on it. Every IAM team has a whiteboard with &lt;code&gt;WHO IS DOING THIS THING?&lt;/code&gt; underlined twice.&lt;/p&gt;
&lt;p&gt;Then we shipped agents. The agent has no employee ID. No role. No manager. It runs under the access of whichever engineer set the env var. When it does something wrong, the closest thing we have to attribution is a Slack channel called #incidents.&lt;/p&gt;
&lt;h2 id=&quot;what-a-passport-is&quot;&gt;What a passport is&lt;/h2&gt;
&lt;p&gt;Four things, at minimum:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An identity that isn’t borrowed.&lt;/strong&gt; Not the dev’s account. Not “the key in the env file”. A first-class identity in your IAM, with rotation and revocation built the same way they are for humans.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A scope.&lt;/strong&gt; Right now most agents carry the equivalent of a diplomatic passport — they go anywhere, talk to anything, write to any bucket. We give them this because permissions are tedious to wire up. Then we are surprised when one of them drops the wrong table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A stamped trail.&lt;/strong&gt; Every action the agent takes leaves a record that survives outside its own context window. The model can hallucinate what it did. The audit log cannot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A consul.&lt;/strong&gt; Someone — human or a higher-trust agent — who authorizes the actions that matter. Reads flow. Writes ask. Money moves on paper.&lt;/p&gt;
&lt;h2 id=&quot;the-two-objections-youll-hear&quot;&gt;The two objections you’ll hear&lt;/h2&gt;
&lt;p&gt;The first is that an agent is “just a script”, and scripts don’t need passports. This is two years out of date. The script doesn’t write its own instructions; the agent does. The script can’t be socially engineered through a forwarded email; the agent can.&lt;/p&gt;
&lt;p&gt;The second is that all this is “too heavy for prototypes”. Sure. Most things are too heavy for prototypes. But the point of the passport metaphor isn’t to slow the agent down. It’s to keep you from retrofitting identity onto code that was never written with it in mind.&lt;/p&gt;
&lt;p&gt;We did that once already. Fifteen years of human identity bolted onto systems that originally assumed every user was the operator. It was expensive. Most of us hated it.&lt;/p&gt;
&lt;p&gt;We don’t have to do it twice.&lt;/p&gt;</content:encoded><category>agents</category><category>security</category><category>audit</category></item><item><title>Pricing pages are written for people who won&apos;t buy</title><link>https://databookman.com/blog/pricing-pages-are-written-for-people-who-wont-buy/</link><guid isPermaLink="false">https://databookman.com/writing/pricing-pages-are-written-for-people-who-wont-buy/</guid><description>I spent two weeks rewriting our pricing page. Conversion didn&apos;t move. What moved conversion was rewriting the sales email — the document everyone who actually paid had already read by then.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I spent two weeks rewriting our pricing page. Conversion didn’t move. What moved conversion was rewriting the sales email — the document everyone who actually paid had already read by then.&lt;/p&gt;
&lt;p&gt;This was embarrassing to admit. The pricing page is the thing every founder optimises because it’s public, trackable, and feels load-bearing. The sales email is the thing every founder neglects because it’s per-account, dull, and not something you can A/B test cleanly. Both are documents about price. Only one of them is read by people who buy.&lt;/p&gt;
&lt;h2 id=&quot;what-a-pricing-page-is-actually-for&quot;&gt;What a pricing page is actually for&lt;/h2&gt;
&lt;p&gt;Three audiences, in the order they’re usually weighted wrong:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;People who will never buy.&lt;/strong&gt; The biggest visitor cohort. They came from a tweet. They will spend nine seconds. The page exists for them mostly to be skim-able and not embarrassing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;People who already decided to buy and need a number.&lt;/strong&gt; They want the price. They want to know if you charge per-seat or per-usage. They do not want a “talk to sales” CTA in the way of that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;People who almost decided to buy but lost momentum.&lt;/strong&gt; Smallest cohort. They came back from a thread, a Slack rec, a saved tab. They need one more reason. This is where pricing-page copy can earn money — and where most pages just repeat the homepage.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-actually-converts&quot;&gt;What actually converts&lt;/h2&gt;
&lt;p&gt;For B2B SaaS at a small scale, the document that converts is almost never the pricing page. It’s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An onboarding email that’s specific to what they signed up for&lt;/li&gt;
&lt;li&gt;A pricing rationale paragraph in the trial-end email that addresses why-not-cheaper questions&lt;/li&gt;
&lt;li&gt;A “here’s what’s included in your tier” page that exists inside the product, after login&lt;/li&gt;
&lt;li&gt;A short PDF a customer can forward to their finance team&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these are public. None of them get blog posts written about them. All of them outperform the pricing page for actual revenue.&lt;/p&gt;
&lt;h2 id=&quot;what-id-do-differently&quot;&gt;What I’d do differently&lt;/h2&gt;
&lt;p&gt;Spend the two weeks rewriting your highest-traffic post-signup email. Read the actual replies your customers send when they ask “what does this cost.” Write the pricing page last, and make it small.&lt;/p&gt;</content:encoded><category>pricing</category><category>saas</category><category>founders</category></item><item><title>The 3-person team is the new 50-person team</title><link>https://databookman.com/blog/the-three-person-team/</link><guid isPermaLink="false">https://databookman.com/writing/the-three-person-team/</guid><description>A friend&apos;s startup did $4M in revenue last year with three engineers and a tax contractor. Their competitor did $6M with forty-two people. The competitor has more meetings.</description><pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A friend’s startup did four million in revenue last year with three engineers, one designer, and a contractor who handles taxes. Their main competitor did six million with forty-two people. The competitor has more meetings, more brand, and a head of marketing whose entire job is justifying the head of marketing. My friend’s team has none of that. They also have 5.7× the revenue per person.&lt;/p&gt;
&lt;p&gt;This number — 5.7× — keeps showing up. There was a Hacker News thread earlier this year with hundreds of CTOs in the comments arguing about it. The lean AI-native startups average $3.48M revenue per employee. Traditional SaaS averages $610K. The gap is not a rounding error. The gap is the entire game.&lt;/p&gt;
&lt;h2 id=&quot;what-changed&quot;&gt;What changed&lt;/h2&gt;
&lt;p&gt;The piece of work that used to fund a full headcount — design, then frontend, then backend, then DevOps, then docs, then support — now fits in a senior engineer’s afternoon. Some of it is AI doing the boring parts. Some of it is the boring parts having been deleted, because AI made the alternative cheap enough that we stopped pretending the boring parts mattered.&lt;/p&gt;
&lt;p&gt;This will sound triumphant if you are the senior engineer in question. It will sound bleak if you were the docs hire. Both reactions are correct. The story is not “AI made everyone more productive”. The story is “AI made the people in the middle redundant, and the people at the edges twice as valuable as before.”&lt;/p&gt;
&lt;h2 id=&quot;what-you-should-be-hiring-like&quot;&gt;What you should be hiring like&lt;/h2&gt;
&lt;p&gt;If you are starting something now, your first hire is a person who can do two adjacent roles competently and a third one passably. Design Engineer. Eng-PM. Ops who can write Rust. The hand-off mode — designer hands Figma to engineer, engineer hands ticket to QA — is the most expensive line item on your P&amp;amp;L and you don’t know it because the bill is paid in calendar invites.&lt;/p&gt;
&lt;p&gt;If you are forty-two people already, the news is harder. You can’t refactor your way down to three. But you can refactor your way down to twelve, and twelve is enough to win a market that used to require fifty.&lt;/p&gt;
&lt;h2 id=&quot;what-you-should-be-careful-about&quot;&gt;What you should be careful about&lt;/h2&gt;
&lt;p&gt;Three-person teams beat fifty-person teams on revenue per head. They lose on three other things that matter: institutional memory, hiring depth, and the ability to absorb a single quitting.&lt;/p&gt;
&lt;p&gt;The honest take is that the small team is a higher-variance bet. Most three-person teams die. The ones that don’t are the comparison everyone uses, which is survivorship bias dressed up as a business model.&lt;/p&gt;
&lt;p&gt;So: bet small if you can carry the variance. Hire wider if you can’t.&lt;/p&gt;</content:encoded><category>ai</category><category>business</category><category>teams</category></item><item><title>Your prompt isn&apos;t cache-shaped</title><link>https://databookman.com/blog/your-prompt-isnt-cache-shaped/</link><guid isPermaLink="false">https://databookman.com/writing/your-prompt-isnt-cache-shaped/</guid><description>I asked a team what their cache hit rate was. The lead said &quot;we cache responses, right?&quot; That single misunderstanding was costing them about fourteen thousand dollars a month.</description><pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I asked a team what their prompt cache hit rate was. The tech lead said “we cache responses, right?” That isn’t what cache means here, and that single misunderstanding was costing them about fourteen thousand dollars a month.&lt;/p&gt;
&lt;p&gt;Prompt caching has been generally available across the major providers for over a year. The discount is real — typically ninety percent off cached tokens after the first hit, sometimes more. The catch is that almost nobody designs their prompts to be cacheable, so almost nobody gets the discount.&lt;/p&gt;
&lt;p&gt;The rule is mechanical: caches match by prefix, not by content. If the first token of two requests differs, neither hits cache. If you put a timestamp at the top of the system prompt — “Today is May 19, 2026” — every single call is a cold cache. If you interleave user history before the system prompt, every conversation is a cold cache. The expensive bits — the system prompt, the tool definitions, the few-shot examples — are exactly the bits you want frozen at the start of every request.&lt;/p&gt;
&lt;h2 id=&quot;what-cache-shaped-looks-like&quot;&gt;What cache-shaped looks like&lt;/h2&gt;
&lt;p&gt;Three rules cover most of it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Static block first.&lt;/strong&gt; System prompt, role, formatting rules, tool definitions, few-shot examples — all of it before anything that varies. Order matters. A 4 KB static prefix that changes once a quarter is an asset; one buried under the user message is dead weight.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic block last, and clearly delimited.&lt;/strong&gt; User input, current state, retrieved context — at the end, in a single block, so the cache boundary is obvious to humans reviewing the prompt later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No clocks, no UUIDs, no random salts in the prefix.&lt;/strong&gt; They look harmless. They cost you the entire month’s caching benefit.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-diagnostic&quot;&gt;The diagnostic&lt;/h2&gt;
&lt;p&gt;Look at your cache hit rate. If it’s under seventy percent on a high-volume agent, your prompt isn’t cache-shaped. The fix is usually a one-day refactor of the prompt template plus a tweak to your retry/log code so it doesn’t perturb the prefix.&lt;/p&gt;
&lt;p&gt;The savings won’t show up as a dramatic moment. They’ll show up as next month’s bill being a third of last month’s, and nobody noticing because the dashboards weren’t tracking it. Track it.&lt;/p&gt;</content:encoded><category>prompts</category><category>cost</category><category>agents</category></item><item><title>The cheapest dependency is the one you delete</title><link>https://databookman.com/blog/the-cheapest-dependency/</link><guid isPermaLink="false">https://databookman.com/writing/the-cheapest-dependency/</guid><description>I removed a logging library last quarter. Build time dropped twelve seconds, bundle dropped four hundred KB, the on-call rotation forgot it existed. We had been paying rent on it for three years.</description><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I removed a logging library from our build last quarter. The change was forty lines of deletes and one new helper function. Build time dropped twelve seconds. Bundle dropped four hundred KB. The on-call rotation forgot it existed. We had been paying rent on it for three years, and the only thing it did better than &lt;code&gt;console.log&lt;/code&gt; was come with a JSON schema and a security advisory every six months.&lt;/p&gt;
&lt;p&gt;The cheapest dependency is the one you delete. This is so obvious it sounds like a fortune cookie. It also describes work that almost nobody does, because the work has no story attached to it. There is no Friday demo for “I removed a thing.” There is no LinkedIn post for “I subtracted.” The marginal hire optimises for what they can add to a codebase. The codebase optimises for what it can remove.&lt;/p&gt;
&lt;h2 id=&quot;why-deletions-dont-happen&quot;&gt;Why deletions don’t happen&lt;/h2&gt;
&lt;p&gt;Three reasons, in rough order of which is the most embarrassing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Nobody owns it.&lt;/strong&gt; The person who installed the dependency is gone. The person inheriting it isn’t sure if removing it is safe. The middle option — leaving it — never gets second-guessed in a code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The shell game of “we might need this someday.”&lt;/strong&gt; We don’t. We don’t. We will not. The thing that justifies keeping a library “for the future” almost always turns out to be a five-line function you write fresh when the future arrives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deletion looks like nothing.&lt;/strong&gt; The PR is small. The diff is mostly red. There is no feature attached. Reviewers approve it slower than feature work because there’s nothing visible to validate against.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-it-costs-you&quot;&gt;What it costs you&lt;/h2&gt;
&lt;p&gt;Each dependency is a small ongoing tax: surface area for CVEs, time spent on version-pin bumps, build slowness, mental load when reading the codebase (“what does this import do?”). Most of these are too small to feel individually. They aggregate.&lt;/p&gt;
&lt;p&gt;A team I worked with took two engineers’ time for a week to do nothing but subtract. They removed eighteen npm packages, two Postgres extensions, and an entire microservice. They didn’t ship a feature that week. They got two minutes back on every build for a year.&lt;/p&gt;
&lt;p&gt;It is a real piece of work. It is rarely a glamorous one.&lt;/p&gt;</content:encoded><category>software-craft</category><category>dependencies</category><category>simplicity</category></item><item><title>You&apos;re optimizing the wrong axis of agent cost</title><link>https://databookman.com/blog/the-wrong-axis-of-agent-cost/</link><guid isPermaLink="false">https://databookman.com/writing/the-wrong-axis-of-agent-cost/</guid><description>I watched a team spend a quarter dropping their per-call latency by 40%. The bill kept going up. The bottleneck was never speed.</description><pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I watched a team spend a quarter dropping their per-call latency by 40%. The bill kept going up. They were proud of the latency win — the dashboards were beautiful — but they were optimizing the wrong axis. Their bottleneck was not speed. It was that they were using a frontier model to do regex.&lt;/p&gt;
&lt;p&gt;There are three axes most agent fleets can be tuned on. In rough order of how often teams pick the wrong one:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt; — how fast a single call returns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accuracy&lt;/strong&gt; — how often the call is right.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Routing&lt;/strong&gt; — which model handles the call in the first place.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Teams obsess over latency and accuracy, because they’re the axes the dashboard renders. The axis that actually moves the bill is routing.&lt;/p&gt;
&lt;h2 id=&quot;the-shape-of-the-actual-cost&quot;&gt;The shape of the actual cost&lt;/h2&gt;
&lt;p&gt;Run a profiler on your agent fleet. Not a latency profiler — a token profiler that tells you how many tokens are flowing through which model at which price tier.&lt;/p&gt;
&lt;p&gt;The shape will surprise you. In every fleet I have looked at, somewhere between 60% and 80% of the volume is doing things that an old GPT-3.5-class model could have done for one-twentieth the cost. Format a response. Pick from a list. Confirm a yes/no. Restate a parameter. The frontier model is invoked because the team didn’t want to write a router, and the router is what would have saved them.&lt;/p&gt;
&lt;h2 id=&quot;what-good-routing-looks-like&quot;&gt;What good routing looks like&lt;/h2&gt;
&lt;p&gt;A good router is boring. It’s a function. The function reads the request and decides which model gets it. Sometimes it’s a small finetune. Sometimes it’s a regex. Sometimes it’s a single line of code that says &lt;code&gt;if intent in {&amp;quot;yes&amp;quot;, &amp;quot;no&amp;quot;}: return cheap_model.invoke(prompt)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A bad router is “we’ll add caching later”. Caching is not routing. Caching helps when you ask the same question twice. Routing helps when you ask different questions of different difficulty. The bill is shaped by the second case, not the first.&lt;/p&gt;
&lt;h2 id=&quot;the-diagnostic&quot;&gt;The diagnostic&lt;/h2&gt;
&lt;p&gt;Open last month’s API invoice. Divide the total cost by the number of agent invocations. If the number is bigger than half a cent, you are probably calling a frontier model for things that did not need one.&lt;/p&gt;
&lt;p&gt;If the number is bigger than five cents, your fleet is on fire and you are paying for the smoke.&lt;/p&gt;</content:encoded><category>agents</category><category>cost</category><category>engineering</category></item></channel></rss>