Home-Cooked Software: I Built My Wife the App the Market Was Never Going to Make
My wife lifts to build a habit. She is not chasing a one-rep max or a number on a leaderboard. She wants to show up, do the session, feel capable, and come back. She was running a beginner barbell program through the official app, and the program is built on linear progression: add weight every single session. That works for a while, and then it gets hard, fast. The bar is heavier than last time, every time. Miss the jump and the app treats it as a failure to recover from.
For someone whose goal is to keep the habit alive, that is exhausting in exactly the wrong way. The app had one definition of a good session, heavier than the last one, and no setting let it be anything else. So I built her one that could. It is a barbell tracker called Ledger, it runs at ledger.jasonmatthew.dev, the source is on GitHub, and its one real feature is that a good session can be the same weight.
This post is not really about that app. It is about why the app she had was never going to do this, why I could build the replacement in a couple of evenings, and why that second fact is a bigger deal than it sounds.
The Idea Is Old. Only the Price Changed.
The instinct is to call this an AI story. It mostly is not. The idea that you should build software for a specific person instead of buying something general has a name and a lineage, and the lineage is twenty years deep.
Clay Shirky named it in 2004. He called it situated software: “software designed in and for a particular social situation or context.” He set it against what he called the Web School, “where scalability, generality, and completeness were the key virtues.” His argument then is almost word for word the argument now. Software was not built for tiny groups because of three scarcities: “the expense of adequate hardware, the rarity of programming talent, and the sparse distribution of potential users.” Remove the scarcities and building for a dozen people stops being absurd.
Robin Sloan put it more plainly in 2020: “An app can be a home-cooked meal.” He built a messaging app for four people, himself and his parents and his sister, and never shipped it to anyone else. His point about what that buys you has stuck with me: “it won’t change unless we want it to change. There will be no sudden redesign, no flood of ads, no pivot to chase a userbase inscrutable to us.” Maggie Appleton revived the frame in 2024 and called the person who does this a barefoot developer, someone embedded in a community with just enough skill to build for it. Geoffrey Litt and the team at Ink & Switch carry the same thread into the LLM era under the banner of malleable software.
So the idea is not mine and it is not new. What changed is the price. Shirky watched the scarcities erode over decades. The build cost did not hit zero in 2004, it just stopped being prohibitive. What is happening now is the same curve, steeper. The thing that used to take days takes hours. That is the whole event.
Why the Market Was Never Going to Build It
Here is the economic shape of it, and it is the part worth slowing down on.
The barbell app she had is good software. It is not lazy or badly made. It encodes one specific philosophy of training, the heavier-every-session model, because it has to serve a hundred thousand people and that model is the one most of them signed up for. Appleton’s line is the sharp version: “Industrial software can only target the biggest problems for the most people, ideally wealthy people with disposable income.” A feature that says “let a good session be the same weight” serves the lifter who is optimising for showing up rather than for load. That is a real person. She is just not the median person, and the median is who the product is for.
So the feature was never withheld out of malice. It was withheld by arithmetic. Building and maintaining it costs engineering time, and engineering time only pays back across a large enough audience. The long tail of “things that fit exactly one person” stayed unbuilt because no one could justify the cost of building it for one person. That is the calculus. And it inverts the moment the cost of building approaches the cost of an evening. Appleton again, on what makes home-cooked software possible at all: “This kind of software doesn’t even need to make a profit; it just has to operate at a very low cost.”
This is the reframe I want to land. It is not “AI lets you add features.” It is that an entire category of software, the category that fits one person perfectly, was economically impossible and is now merely a Tuesday. You stop adopting the generic system that fits a thousand people approximately and start building the one that fits you exactly, because for the first time you can.
The Achievable-Session Feature, Concretely
The feature is a progression with a hold band. You do your work sets, the last is as many reps as possible, and the reps decide what happens next. The middle of the range does nothing, on purpose:
/**
* reps < floor -> decrease (-2.5, or -5 when well short)
* floor <= reps < ceiling -> hold: you hit your sets, the weight stays
* reps >= ceiling -> increase, and only when you clear it comfortably
*/
export function topSetDeltaKg(amrapReps: number, floor: number, ceiling: number): number {
if (amrapReps < floor) return amrapReps - floor <= -3 ? -5 : -2.5;
if (amrapReps < ceiling) return 0;
const over = amrapReps - ceiling;
if (over >= 4) return 7.5;
if (over >= 2) return 5;
return 2.5;
}
The line that matters is the one that returns zero. Linear progression has two outcomes, heavier or failed. The hold band adds the third one she needed: stay here, and it still counts. The bar climbs only when she clears the top of the band comfortably, so progress arrives when it is already easy instead of as a demand. She does not know what a hold band is and she does not need to. She knows the app stopped making her dread the next session.
It Was a One-Line Change, And That Is the Whole Point
What let me build this in evenings instead of weeks was a boundary. Every lift carries a method, and a method is a small module behind one interface:
interface MethodModule<S> {
kind: MethodKind;
init(lift: Lift): S;
prescribe(state: S, lift: Lift): Prescription;
advance(state: S, completed: SetResult[], ctx: AdvanceCtx, lift: Lift): { newState: S; event?: ProgressionEvent };
}
const REGISTRY = {
linear: linearModule,
manual: manualModule,
weeklyAmrap: weeklyAmrapModule,
};
A method is really a definition of what progress means. Linear means heavier every time. The hold-band method means progress is optional and earned gently. Adding the second definition was one new module, one arm on a union type, and one line in that registry. No database migration. The lesson outlasted the code: the app she had was opinionated about progress because progress was hardwired into it. Make it pluggable and the tool stops insisting your goal is the same as its author’s.
The Part That Is Genuinely Hard
The most interesting piece technically is the rest timer, and it is also where the “anyone can build this now” story needs an honest edit.
The naive rest timer is a setTimeout in the page. On a phone it fails right when you need it: you start your rest, lock the screen, the browser throttles or kills the timer, and the notification never comes. The clock cannot live in a tab the user is about to walk away from. So it does not. When she starts a rest, the app posts her push subscription and the duration to a Cloudflare Worker, which hands them to a Durable Object keyed by that subscription:
export class RestTimerDO extends DurableObject<Env> {
async schedule(subscription: PushSubscriptionJSON, seconds: number) {
await this.ctx.storage.put('subscription', subscription);
await this.ctx.storage.setAlarm(Date.now() + Math.max(1, seconds) * 1000);
}
async alarm() {
const sub = await this.ctx.storage.get<PushSubscriptionJSON>('subscription');
if (!sub) return;
const status = await sendWebPush(sub, this.env);
if (status === 404 || status === 410) await this.ctx.storage.delete('subscription');
}
}
The clock is server-side now. The Durable Object alarm fires whether the tab is open, backgrounded, or closed, then sends a payload-less Web Push that wakes the service worker. The VAPID authentication is hand-rolled against the spec with Web Crypto, no push library, about sixty lines. I knew the setTimeout would die before I wrote it, knew the alarm belonged in a Durable Object, knew what a payload-less push was for. That knowledge was load-bearing, and a language model did not supply it.
This is the honest counterpoint, and it is best made by the people most optimistic about the idea. Appleton, whose whole talk is a case for home-cooked software, is blunt about the limit: language models will happily write your frontend, but “user authentication and logging in would be a whole different thing”, and on security, multiplayer, and real deployment, “the developer experience is not quite there yet.” The evidence backs the caution. A randomised trial from METR found that early-2025 AI tools actually slowed experienced developers by around 19% on their own mature codebases, even though those developers felt faster. Read that carefully and it is not an argument against the personal-software case. It is the strongest argument for it. AI helps most on small, greenfield, single-user tools and least on large systems with years of accumulated context. A barbell tracker for one person is the first thing. That is precisely why this worked and why I am not claiming it generalises to everything.
Even Karpathy, who coined the term for the giddy version of this, scoped it honestly. Vibe coding, he wrote, is “not too bad for throwaway weekend projects.” The trick is knowing which of your projects is a throwaway weekend project and which one your wife is going to open every session for the next year. The second kind still needs an engineer. It just needs far fewer engineer-hours than it used to.
Build the Thing That Fits You
The first usable version of Ledger came together over a lunch break, a lot of it typed into Claude Code on my phone. The rest was a couple of evenings: over a hundred tests, a dark theme, unit handling, plate maths, backups, the progression engine. The prototype was an hour. The thing she actually uses was the evenings, and the evenings are where the engineering went.
That gap, between prototype and a tool someone relies on, is real and AI has not closed it. What it has done is move the whole curve down far enough that building for an audience of one finally clears the bar. The app the market was never going to make my wife took me two evenings to make her, not because I am special but because the price of going from “her goal is different from this tool’s” to “the tool fits her goal” fell through the floor.
The generic system that fits a thousand people approximately used to be your only option, because the bespoke one that fits you exactly was too expensive to exist. That constraint is lifting. The interesting question is no longer whether you can build the thing that works for you. It is increasingly just whether you will.