← writing

I let Claude write my CMS for a month

I handed Claude 4.7 the keys to a new Squilla extension for thirty days. Here is what worked, what broke, and the honest line-count split.

For thirty days I ran an experiment on myself. Every new line of code in a fresh Squilla extension had to come from Claude 4.7 first. I could edit, I could reject, I could rewrite, but the first draft was never mine. The extension I picked was a comments system, because it touches everything I care about in Squilla: the CoreAPI, a gRPC plugin, Tengo hooks, SQL migrations, a React admin page, and the event bus. If AI can ship that, AI can ship most of what I build.

I want to tell you what actually happened, not what the marketing pages say.

Where Claude carried real weight

The boring parts vanished. Scaffolding a new folder under extensions/comments/ with a manifest, a plugin entry point, the gRPC server stubs, and the migration directory took about four prompts. Claude knew the shape of a Squilla extension from reading two existing ones, and it produced a manifest that declared the right capabilities on the first try. Things like data:write, nodes:read, events:publish. I expected to fight that. I did not.

The gRPC plumbing was the biggest win. Wiring HandleHTTPRequest, marshaling the request body, converting headers, returning a typed response, all of that is the kind of code I have written six times for six extensions and resent every time. Claude wrote it once, correctly, and threaded it through the comments routes in about an hour.

Migrations were also a strong area. I described the table shape in plain English. Two minutes later I had a clean SQL file with the right indexes, a sensible foreign key to content_nodes, and a down migration that actually worked. I have written migrations that did not survive their own rollback. This one did.

The admin UI React page came out workable. Tabs with counts, a search box, a pagination footer that matched the design language of the nodes page. It was not pretty enough to ship as is, but it was a real starting point, not a sketch.

The surprise

Claude caught a race condition I did not see. My event subscriber for comment.created was reading the moderation queue before the transaction had committed in another goroutine. On my machine it never fired because everything was fast. Claude flagged it during a refactor, asked if the subscriber should wait on a PublishSync instead of Publish, and explained why the current code would be flaky under load. I have been writing Go for years. I missed it. Claude did not.

That moment alone paid for the month.

Where it kept tripping

Twice, Claude invented a CoreAPI method that does not exist. Once it was core.node.attach_taxonomy, which sounds plausible and is not real. Once it was a streaming variant of core.data.query that I would actually love to have but have not built. Both compiled in its head. Neither compiled in mine.

It also could not stop putting emoji in log messages. I asked it three times to stop. It stopped for one file. The next file had a sparkle in an info log. I gave up and added a lint rule.

And it over-engineered. One time I asked for a small helper to normalize a user agent string. I got back three files, an interface, a factory, and a test suite with mocks for a string operation that fits on one line. I deleted all of it and wrote the line.

What I rewrote by hand

The moderation logic was mine. Claude can write a function that returns true or false. It cannot decide what your product considers spam, and it should not try. The rate limiter was also mine, because the algorithm I wanted needed to share state with another extension through the data store, and that shape was load-bearing for the whole feature. Claude produced a token bucket that worked in isolation and would have broken the moment a second instance booted.

Event payload shapes were the third rewrite. The shape of a comment.created event is a contract. Other extensions, including the email-manager rules engine, will key off those fields forever. I needed to design that, not let it emerge.

The honest split

I counted. Around seventy percent of the line count in the final extension was Claude. Around thirty percent was me rewriting, deleting, or replacing. That sounds bad for the AI until you realize the velocity. I shipped a comments extension that would have taken me two weeks of evenings in about five days of evenings. The thirty percent I rewrote was also the thirty percent that mattered most, and I had time to think about it because I was not also typing the boilerplate.

The actual lesson

AI does not replace taste. It amplifies it. If you have strong opinions about how a Squilla extension should be shaped, what an event payload should look like, when a function should stay one line, Claude will help you ship those opinions faster. If you do not have those opinions yet, Claude will help you ship a lot of code that nobody, including you, can maintain in six months.

I am keeping the experiment going. The next extension I build, I am letting Claude draft again. But I am also reading every line, because the second I stop reading is the second the inventions creep back in.

Want more like this?

Occasional, opinionated, no listicles.
all writing →