Excel was not the problem
The spreadsheet ran my parts inventory well for years. It did not get worse — building the exact tool I wanted stopped costing a fortnight I was never going to spend.
I kept my electronics parts in a spreadsheet. One row per component, one column each for the code, the name, the package, the box it lived in, the quantity. It worked. I could search it, sort it, open it on any machine, and it never went down. When I say I replaced it, I want to be clear about what I am not saying: I am not saying the spreadsheet was bad.
This is the same argument I made about personal software, with a duller subject. The stencil tool replaced an evening of manual work. This one replaced something that was already fine.
What the spreadsheet could not be
A row tells you a part is in box B9. It does not tell you where in B9.
B9 has 57 parts in it. It is a compartment box — a grid of little lidded cells. Once a box holds more than a handful of components, “it’s in B9” means opening B9 and looking at every cell. The spreadsheet knew which box. The shelf needed to know which compartment, and there was nowhere sensible in a flat sheet to put that, because the number only means anything if it is also printed on the physical box.

That is the whole complaint. Not performance, not features, not scale — 331 parts across 12 boxes is nothing. The spreadsheet was a record of what I owned. I wanted an address for where it was.
For twenty years the answer to that would have been: adapt. Add a Bin column, maintain it by hand, accept that it drifts out of sync the first week you are in a hurry. Or buy inventory software built for a warehouse and shaped for someone else’s shelf. Both are the same trade — you bend to the tool, because building one is more expensive than the annoyance.
Why I would never have built it
I want to be precise about what stopped me, because it was not money and it was not skill. I could have written this. I have written harder things. It was that the tool was never going to be worth the evenings.
A parts app is not one job. It is a schema, and then a migration when the schema turns out wrong, and a sticker layout that has to survive a real printer, and a grid that draws a box the way the box actually is, and a phone-sized UI because that is where you stand when you use it. Every one of those is small. Together they are a project, and it is a project whose entire output is knowing where a resistor lives.
That is the calculation that never closed. Not “this costs too much” but “this is not what I want to spend a fortnight on.” I build boards. The inventory exists so that building boards is easier; spending three weeks on the inventory inverts the thing. So I did what everyone does with a good-enough tool — I kept the spreadsheet, added a column, and let it drift.
The maintenance side is worse, and it is the part that really kills these projects. Software you own asks for something every time reality moves. A new box type, a dependency that shifts underneath, a column whose nullability you have to reconstruct from memory a year later. A spreadsheet asks for nothing. Even if the build were free, I would have hesitated at owning the thing afterwards.
What changed is that the work compressed to fit the gaps I actually have. Fifty-one commits, spread over five months, land on seven days. The rest of those months I was doing other things and the tool sat there working. The July rewrite — design spec, implementation plan, a pure module with twenty-eight tests, an irreversible migration against live data, and the whole UI rebuilt on top — happened in nineteen commits in a single day. Alone, that is a fortnight I would have kept not scheduling, indefinitely.
And the ownership cost came down with it. When the shelf changes now, changing the tool is an evening rather than a project, so the tool tracks the shelf instead of quietly diverging from it the way the spreadsheet column did. The tool did not become free. It became small enough to fit in the space I had.
What I built instead
A Next.js PWA on Supabase, static-exported to GitHub Pages. Parts list with search and box filters, a part detail page with a quantity adjuster and a running log of every change, a storage view that draws each box as its real grid of compartments, printable stickers. It runs on my phone at the bench.

The import page reads .xlsx directly, and it accepts the old sheet’s own column headings — plus every near-miss spelling of them I could think of. That was deliberate. A migration you have to hand-clean is a migration you postpone.
Thirty-five prompts built the first version, and I kept them in the repo as
PROMPT_HISTORY.md. They are not impressive prompts. Number 17 is “When I set
bin count for a box, it still shows empty slots outside the range.” Number 32 is
“Show empty bins as green boxes.” That is what the work actually looks like:
standing at the shelf, noticing something wrong, typing the sentence you would
have said to a colleague.
The part I got wrong, then deleted
The first version had QR scanning. Every part got a barcode of the form
B9-115, a page printed the labels, and another page opened the camera to scan
them.
I used it approximately never. Sticking a QR label on a 0603 resistor’s
compartment and then pulling out my phone to scan it is slower than reading a
number. In July I deleted it: one commit, 376 lines out and 84 in, the scanner
component and the label grid and the html5-qrcode dependency all gone.
I do not think I could have reasoned my way to that. It sounded right. It looked right in the plan. It took building it and living with it on the shelf for sixteen weeks to find out that the shelf did not want scanning — it wanted big printed numbers.

Making the bin the address
The rewrite was the point where this stopped being a spreadsheet with a nicer front end.
bin_number had been nullable. NULL meant “unplaced”, and the storage grid
would synthesise a position for those parts by filling empty slots in item-code
order. That display position was never stored, so it changed whenever anything
else in the box changed. It looked like an address and behaved like a guess.
So: {box}-{bin} becomes required and stored. Box IDs printed on lids, bin
numbers printed on the compartments, and the app tells you B9-1. Both columns
NOT NULL, bin_number >= 1, barcode dropped entirely.
I wrote the design spec before any code, including an audit of the live data —
223 parts at the time, zero with a null location, 49 with an explicit bin. The
audit is what surfaced the awkward cases. REEL was a location with no matching
box row, so it became an ordinary box called REELS whose numbers are a logical
counter and never get printed. Some parts sat at bin numbers past their box’s
compartment count, which is real and had to stay legal — the app flags it and
lets me fix it by hand, rather than refusing the write.
All the bin arithmetic went into one pure module with no database in it: which bin is free, who else is in a bin, is this bin out of range. Twenty-eight tests cover it. The SQL migration wraps the backfill and the constraints in a single transaction, because a half-applied version leaves you with parts that have no bin and a NOT NULL column that rejects every write to fix them. It ran once, irreversibly, against real data.
The one function in that module that no longer runs anywhere carries a comment saying so — that it is the executable spec for a migration that has already happened, kept and tested as documentation, and not wired into anything live. Fifty-one commits over five months, and that comment is the one I am most glad is there.
Where the AI actually is
Nowhere in the app. No model, no API key, no inference. Search is ilike. The
importer is column-name matching. The bin allocator is a while loop. If you
grep the repo for “anthropic” or “openai” you get nothing.
The AI is upstream of all of it. It wrote the migration, the tests, the grid rendering, the sticker layout. I brought the shelf: which box is which, that bins have to be shareable because small parts genuinely share a compartment — B9’s 57 parts live in 30 bins, and bin 8 alone holds five — that a bin past the box’s capacity is a thing to flag rather than forbid. None of that is in any spec Claude could have read. All of it is obvious if you own the boxes.
What changed is not the spreadsheet and not my skills. It is that a custom-fit tool stopped requiring a block of time I was never going to give it. The spreadsheet was never winning on quality. It was winning because it was already there and the alternative cost weeks, and only one of those two things is still true.
Look at the tool you are working around rather than with. Not the one you hate — the one you have no complaint about, that you have quietly shaped yourself around because building the fitted version was never worth a fortnight. Check that number again.