I deleted every dependency in my side project A developer used the open-source coding agent pi, running Z.ai's glm-5.3-flash model, to strip all 112 Ruby gems and 134 npm packages from a small hotel check-in app called Fawlty, replacing them with 796 lines of hand-rolled code the agent wrote in under five hours. The agent reimplemented only the framework slices the app actually used, including a Postgres wire-protocol client, and the developer reports the app got smaller and faster to build, though he says he has not read the generated code and is uneasy about the approach. Fawlty is a small app for hotel staff that checks guests in and out of rooms. It’s a side project I use for experimenting. Until November 14, it ran on 112 gems and 134 npm packages. Now it runs on none. An agent did the rewrite in under five hours, and I haven’t read a line of what it wrote. I measured before and after. The app got smaller and faster to build, and the bill is 796 lines I now own. I’m not telling you to do this, because I’m not entirely comfortable with it myself. The idea came from Erik Meijer’s talk at AI DevCon New York this year, The AI Chop Shop https://www.youtube.com/watch?v=JCUUAS0cEDc . It covers a lot of ground, including software patents, copyright for AI-written code, and partial evaluation. Partway through, he asks why you’d rewrite what you depend on. His point, as I took it: why depend on a pile of code when your app only needs a small part of it? If you reimplement just the slices your app needs, you get what’s necessary, you drop the bloat, and you stop depending on someone else’s code. That was the easiest idea in the talk to test, so I started there. It’s one claim from a talk with a lot more in it, and this post says nothing about the rest. For most of my career, I’ve reached for a library first. For auth and encryption, I never considered anything else. You don’t write your own SCRAM client the password handshake Postgres uses . Someone who knows the protocol better has already written one, and every app that runs it helps find its bugs. That habit made sense when a person wrote the code, because the writing was the expensive part. Library reuse still works with an agent, and nothing stops an agent from using a library. But an agent can also reimplement only the slices an app needs. So I’m uneasy about it. That discomfort is where I tend to grow, though, so the idea felt worth challenging. I wrote one prompt, and it was short: This project uses Hanami and Svelte. I want to try an experiment. My goal is to remove all the dependencies from this project it’s fine to leave docker Instead of using gems and npm packages, implement only the peices of the frameworks and libraries that are actually being used in the application. Two follow-ups asked it to measure before and after, and told it I’d probably write about the result. Before it touched anything, the agent asked me four questions in one round. Should the dev and test tooling go too? Should it hand-write the Postgres wire protocol, since Ruby has no database driver in its standard library? Should a tiny reactive runtime replace the Svelte compiler? Should the measurement cover the full scoreboard? It recommended yes on all four, and I took its recommendation every time. After that, my part was typing “continue” twice: once after I’d started Docker, and once when the agent paused partway through. The harness was pi https://github.com/earendil-works/pi , an open source coding agent, running Z.ai’s glm-5.3-flash model on its high thinking setting. The run took 4 hours 54 minutes, and about 19 minutes of that went to waiting on me to start Docker. It used about 25 million tokens, nearly all of them cache reads, across 191 turns and 230 tool calls. 38 of those calls failed. I didn’t review the diff as it went, and I still haven’t. The agent built six pieces, in this order: Four dependencies got no replacement at all: hanami-view, hanami-validations, dry-operation, and dry-types. Nothing in the app ever called them, but they loaded on every boot anyway. Two bugs from the database client show what a driver does for you. Postgres rejected the agent’s first startup packet. Its error read “invalid startup packet layout: expected terminator as last byte.” So the agent sent five variants of the packet in separate connections, and only the one with a second trailing null got an auth request back. That extra null is what libpq, Postgres’s own C client library, appends to end the parameter list. The error message names the symptom and never mentions that second null. SCRAM failed next, because the server wanted the n,, header on the client’s first message. The agent then checked its proof math offline against the published example values in RFC 7677. That turned up the last bug: it had put a null terminator on the final auth message, where the protocol wants none. Before After Gems direct / total 18 / 112 0 / 0 npm direct / lockfile 7 / 134 0 / 0 Image build, no cache 68.1 s 1.3 s Cold boot to API ready 4.5 s 0.6 s Gem + npm install size 158 MiB ~0 MiB Docker images, total 1,309 MB 498 MB Memory, backend + frontend 237.8 MiB 28.2 MiB Latency p50 / p95 2.3 / 4.5 ms 1.7 / 3.9 ms Own code LOC 1,615 2,411 +796 These numbers come from one app, on one machine, with one run each way. Run the same thing on your app and you’ll get different ones. Nobody opened the new frontend in a real browser, either. The agent checked that the modules served, that the syntax was valid, and that the API answered, but not that anything rendered. I validated that myself. A couple of small behaviors changed too. Timestamps lost their time zone suffix, and a route that doesn’t exist now returns a JSON 404 where Hanami returned a 500. The latency row almost went the other way. During the after-measurement, /api/rooms took 46 ms while /health got faster. The agent suspected Nagle’s algorithm, which holds back small writes to batch them, meeting delayed ACKs on the Postgres socket. Before changing anything, it tested the theory and found a steady stall of about 45 ms on every query. Setting TCP NODELAY, the socket option that turns Nagle off, brought it down to about 12 ms. An isolated test put the steady state near 2.9 ms, and the agent put the rest down to restart noise. The final rerun gave the 1.7 ms in the table. The fix was one line. A framework’s database client sets that flag for you, so you never see the bug it prevents. That’s the case for the library. The four unused gems are the case against it, because they shipped in every image and nothing ever called them. I don’t know which story weighs more, and one run on one app can’t settle it. Deleting every package does take one risk off the table. With zero gems and zero npm packages, nobody can slip a malicious release into my dependency tree, because there isn’t one. The Ruby and Node runtimes and the Docker base images are still someone else’s code, but the package supply chain is gone. But the risk moved instead of vanishing. Those 796 lines used to be someone else’s problem, and now they’re mine. Some of them handle auth. A hand-written SCRAM client has no upstream, no CVE feed, and no patch release. If there’s a flaw in it, nobody else will find it for me, and I haven’t read it. I don’t have an answer for that. There’s more to this I haven’t thought through yet, and I’d rather leave it open than pretend I have. You don’t have to delete anything to try the first half of this. Pick one project this week and list the framework pieces your code actually calls. Then compare that list to what you installed. In Fawlty, four whole gems had zero callers, and they loaded on every boot.