<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>it&apos;s me, a dev</title><description>A single-author technical blog for opinionated long-form posts.</description><link>https://blog.itsmea.dev/</link><item><title>The Spec Is the Deliverable. The Code Is Build Output.</title><link>https://blog.itsmea.dev/posts/spec-is-the-deliverable/</link><guid isPermaLink="true">https://blog.itsmea.dev/posts/spec-is-the-deliverable/</guid><description>When you build with an AI in the loop, the spec is the artifact that matters and the code is build output. Here is the defensible version of that claim, with its honest limits.</description><pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here&apos;s the claim, and I&apos;ll defend it the rest of the way down: when you&apos;re building with an AI in the loop, the spec is the artifact that matters. The code is build output. Treat it that way and a lot of the second-order problems with AI-assisted development get quieter. Treat it the old way (code as the source of truth, spec as the thing you wrote once for the kickoff deck) and you&apos;re going to have a bad time about six months in.&lt;/p&gt;
&lt;p&gt;I know how that sounds. Bear with me.&lt;/p&gt;
&lt;h2&gt;What I mean by &quot;spec&quot;&lt;/h2&gt;
&lt;p&gt;Not a Word doc. Not a Confluence page that nobody updates. I mean a living, precise, markdown-shaped description of what the system does, what it doesn&apos;t do, what the invariants are, what the failure modes look like, and how you&apos;d know it was broken. The kind of thing that, handed to a competent dev team, lets them rebuild the thing without playing twenty questions with you. Detailed enough to generate from. Detailed enough to test against. Detailed enough that you&apos;d notice if it was wrong.&lt;/p&gt;
&lt;p&gt;Most teams don&apos;t write that document. They write something shorter and call it a spec, then let the code drift, and the code becomes the only truthful description of the system. That&apos;s been a problem forever. AI just makes the problem more obvious, and a lot more expensive to ignore.&lt;/p&gt;
&lt;h2&gt;Why this matters more with AI in the loop&lt;/h2&gt;
&lt;p&gt;The old workflow was simple: spec is approximate, code is precise, code wins. Fine. A senior dev reads the code, reconstructs intent, makes the change. Slow but honest.&lt;/p&gt;
&lt;p&gt;The new workflow is different. You prompt, the model writes, you review, you ship. The code is now downstream of two things: the prompt and the model. Change either one and the code changes. So what&apos;s actually persistent across the project? Not the code. The code is a snapshot of what the model produced for a particular prompt on a particular Tuesday. The thing that persists, the thing you can hand to another dev (or to another model in eighteen months) and get something equivalent back, is the spec.&lt;/p&gt;
&lt;p&gt;Code becomes a build artifact. The spec becomes the source.&lt;/p&gt;
&lt;p&gt;Right? It feels backwards. Let me earn it.&lt;/p&gt;
&lt;h2&gt;The honest concession, up front&lt;/h2&gt;
&lt;p&gt;The strongest argument against everything I just said: LLM output is non-deterministic. If I regenerate from the same spec tomorrow, I get &lt;em&gt;similar&lt;/em&gt; code, not &lt;em&gt;identical&lt;/em&gt; code. So the tested, reviewed code in the repo is the real source of truth, because that&apos;s the only artifact you can actually prove things about. The spec is just a wish.&lt;/p&gt;
&lt;p&gt;That argument is correct. I want to be very clear about this, because I&apos;ve seen people get themselves in trouble pretending otherwise. You will sometimes hear &quot;specs make regeneration almost deterministic.&quot; That phrase is doing a lot of work, and most of it is bad work. The gap between &quot;deterministic&quot; and &quot;almost deterministic&quot; is exactly where the silent failures live: the small percentage of regenerated code that&apos;s subtly different, ships unreviewed, and breaks something in production three weeks later. &quot;Almost deterministic&quot; is a comfort phrase. Delete it from your vocabulary.&lt;/p&gt;
&lt;p&gt;So I&apos;m not claiming the spec lets you regenerate the system on demand. I&apos;m claiming something narrower and, I think, more useful.&lt;/p&gt;
&lt;p&gt;A bigger caveat while I&apos;m being honest. I haven&apos;t run this on a real client engagement yet. The four projects I&apos;m drawing on are private, small-to-medium things I built on my own time. It works surprisingly well at that scale, well enough that I&apos;m writing this post about it, but I want you to weight the evidence accordingly. I&apos;m telling you what I&apos;ve seen, not what I&apos;ve shipped to a paying customer with an SLA. The shape of the argument should hold under client conditions (the failure modes I&apos;m describing get worse at scale, not better, which means the discipline matters more, not less) but I haven&apos;t proven that, and I&apos;m not going to pretend I have.&lt;/p&gt;
&lt;p&gt;With that on the table: the stability I got out of AI-assisted coding did not come from the spec magically reproducing the code. It came from senior judgment. Mine. Encoded as specs. Enforced by TDD. Backed by code review I&apos;d have done anyway. The spec wasn&apos;t a substitute for that judgment. It was the format I used to &lt;em&gt;encode&lt;/em&gt; it so the AI could play along.&lt;/p&gt;
&lt;p&gt;So the defensible version of the claim is this. Disciplined senior practice makes AI output stable, and the spec is the artifact that carries that discipline through the handoff. The generated code gets regenerated at dev time, reviewed hard, then frozen and version-pinned at handoff. Nobody regenerates code in production. (If your CI is calling out to Claude to rebuild a service, we need to have a different conversation.)&lt;/p&gt;
&lt;p&gt;The spec is not a regeneration button. It&apos;s the thing that means the code in the repo isn&apos;t an illegible black box six months from now.&lt;/p&gt;
&lt;h2&gt;Where this earns its keep: POCs and MVPs that don&apos;t stay POCs&lt;/h2&gt;
&lt;p&gt;I want to scope this honestly. I&apos;m talking about POC and MVP work. Greenfield. Small teams. Tight loops. This isn&apos;t a claim about refactoring a fifteen-year-old monolith. That&apos;s a different problem and probably a different post.&lt;/p&gt;
&lt;p&gt;But here&apos;s the thing about MVPs: a lot of them are lying about being MVPs. The &quot;throwaway prototype&quot; gets a customer. The customer pays. The thing is now load-bearing, and nobody is going to give you a three-month rewrite window to do it properly. You&apos;re maintaining the prototype now. Congratulations.&lt;/p&gt;
&lt;p&gt;This is where the spec earns out. When the MVP quietly graduates to production (which is most of the time, in my experience) the receiving team inherits two things. They inherit the code, which the AI wrote and which a senior reviewed. And they inherit the spec, which describes what the code is &lt;em&gt;supposed&lt;/em&gt; to do, what invariants matter, and what the tests are protecting. Without the spec, all they have is a pile of plausible-looking TypeScript and a prayer.&lt;/p&gt;
&lt;p&gt;To be clear about the handoff audience: I&apos;m not claiming the spec is something you hand to a non-technical client. The client gets demos and outcomes. The spec is for the dev team who picks it up next. People who can read a precise document and act on it. That&apos;s a real audience, and it&apos;s underserved.&lt;/p&gt;
&lt;h2&gt;What this looks like in practice&lt;/h2&gt;
&lt;p&gt;Light on the tooling, because the argument doesn&apos;t depend on it. But for grounding: markdown specs in the repo, alongside the code, version-controlled together. Tests written from the spec, not the implementation. Code generated with Claude or Cursor against the spec and the tests. Review with the assumption that the AI is a fast junior who sometimes lies confidently. Freeze at release, pin the model version in the spec metadata if you regenerated anything material, and move on.&lt;/p&gt;
&lt;p&gt;The discipline isn&apos;t exotic. It&apos;s stuff senior devs already do. The only real shift is &lt;em&gt;where you spend your effort&lt;/em&gt;. You spend it on the spec and the tests, because that&apos;s the leverage point. You spend less on the code itself, because the code is, increasingly, a derivable output.&lt;/p&gt;
&lt;h2&gt;Vibe coding, briefly&lt;/h2&gt;
&lt;p&gt;I should say what I&apos;m not arguing against. &quot;Vibe coding&quot; has a literal version (don&apos;t read what the AI produced, just ship it if it runs) and I think that version is bad and will hurt you. But people also use &quot;vibe coding&quot; to mean any iterative, conversational, prompt-driven workflow, and that&apos;s fine. That&apos;s actually good. I do that. The difference is whether there&apos;s a spec underneath the conversation, anchoring it. Prompt iteratively, sure. Just have something durable to iterate &lt;em&gt;against&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;The thing I genuinely don&apos;t know&lt;/h2&gt;
&lt;p&gt;Here&apos;s where I get to be uncomfortable in public. I cannot yet tell you whether any of this works for someone who isn&apos;t a senior architect.&lt;/p&gt;
&lt;p&gt;My honest worry is that the engine here is judgment, and the spec is just how I serialize my judgment. If that&apos;s true, then a less experienced dev with the same workflow gets faster, more confident slop. Output that &lt;em&gt;looks&lt;/em&gt; like mine but isn&apos;t load-bearing in the same way, because the spec they wrote doesn&apos;t encode the right invariants. They didn&apos;t know which invariants to encode.&lt;/p&gt;
&lt;p&gt;I&apos;m credible on the rest of this post precisely because I&apos;ve been both the decider and the implementer on these projects. That&apos;s also why I can&apos;t answer this question yet. I can&apos;t separate &quot;this works&quot; from &quot;this works when I do it.&quot; I haven&apos;t run the experiment where I hand the method to a mid-level dev and watch what happens over six months without me in the loop. (And honestly, I haven&apos;t run it on a real client engagement either, which is the other shoe I&apos;m waiting to drop.)&lt;/p&gt;
&lt;p&gt;That&apos;s what I&apos;m watching for next. Whether the spec is a vehicle for judgment or a substitute for it is the question that decides whether any of this scales past the people who already know what they&apos;re doing. I have a guess. I&apos;d rather not publish it until I&apos;ve seen it.&lt;/p&gt;
&lt;p&gt;Until then: write the spec. Treat the code as build output. Don&apos;t regenerate in production. And be honest with yourself about who, exactly, is the source of the stability.&lt;/p&gt;
</content:encoded></item><item><title>I Built This Blog With AI. Here&apos;s What Actually Mattered.</title><link>https://blog.itsmea.dev/posts/building-this-blog-with-ai/</link><guid isPermaLink="true">https://blog.itsmea.dev/posts/building-this-blog-with-ai/</guid><description>A candid account of building this blog AI-first — where the spec held, where the AI confidently lied, and the four times I had to overrule it.</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The blog you&apos;re reading was built almost entirely by an AI. I want to tell you how, because the interesting parts weren&apos;t the ones I expected — and because I keep making a particular claim: that the spec is the deliverable and the code is build output. It would be a little rich to make that claim and then not show the work.&lt;/p&gt;
&lt;p&gt;So here&apos;s the work, including the parts where the AI was confidently wrong.&lt;/p&gt;
&lt;h2&gt;I wrote the spec first, and then I got interrogated&lt;/h2&gt;
&lt;p&gt;I started with a &lt;code&gt;spec.md&lt;/code&gt;: purpose, scope, stack, a content model, routing, invariants, failure modes, and a &quot;how done is verified&quot; checklist. Opinionated, with explicit non-goals. Then, instead of asking the AI to build it, I asked it to &lt;em&gt;grill&lt;/em&gt; me on it.&lt;/p&gt;
&lt;p&gt;That was the first surprise: the most valuable hour was the one before any code existed. The AI walked the design tree one decision at a time, each with a recommendation, and it kept finding places where the spec contradicted itself or had quietly gone stale. It referenced a &lt;code&gt;design.md&lt;/code&gt; that didn&apos;t exist. It assumed a sample post that wasn&apos;t there. And the stack table was written against tooling idioms that had already moved on — the spec wanted an older Tailwind and an older Astro content-collections API, both superseded. The AI checked the current docs and re-grounded the spec against today&apos;s reality.&lt;/p&gt;
&lt;p&gt;None of that was code. All of it was the spec getting sharper. By the time we wrote a line, the spec was internally consistent and matched the tools that actually exist now.&lt;/p&gt;
&lt;h2&gt;The spec stayed the source of truth — because reality kept attacking it&lt;/h2&gt;
&lt;p&gt;Here&apos;s the part I find most convincing in hindsight. The spec didn&apos;t survive contact with the build unchanged. It survived &lt;em&gt;because&lt;/em&gt; every collision got written back into it.&lt;/p&gt;
&lt;p&gt;A few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The spec had an invariant: the production image must be under 50 MB. Halfway through, the base web-server image we&apos;d specified had quietly bloated to 92 MB. The invariant caught it — not me, not the AI&apos;s good intentions, the &lt;em&gt;invariant&lt;/em&gt; — and we switched to a slim variant and landed at 22 MB. A constraint I&apos;d written months earlier caught a regression in a dependency I didn&apos;t write.&lt;/li&gt;
&lt;li&gt;A library version I&apos;d pinned didn&apos;t exist. The package manager had started silently skipping build steps that the image actually needed. The pagination URLs the spec described weren&apos;t the ones the framework produces.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these was a small lie the spec told about the world. Each one got corrected in the spec, not just patched in the code. That&apos;s the discipline I keep coming back to: the code drifts, and the spec is where you record what you learned so the next person (or the next model) doesn&apos;t relearn it the hard way.&lt;/p&gt;
&lt;h2&gt;The AI lied to me. Confidently. More than once.&lt;/h2&gt;
&lt;p&gt;There&apos;s a failure mode people don&apos;t talk about enough: &quot;almost deterministic&quot; output — the small fraction that&apos;s subtly wrong, ships unreviewed, and breaks something later. I got to watch it up close, on my own project.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The AI &quot;verified&quot; the custom 404 page by searching the output for the string &lt;code&gt;404&lt;/code&gt;. That string appears in &lt;em&gt;both&lt;/em&gt; my page and the web server&apos;s built-in error page. False positive. It reported success. The page was fine, as it happened — but the &lt;em&gt;check&lt;/em&gt; was worthless, and it took a second look to notice.&lt;/li&gt;
&lt;li&gt;It tested against a stale container image and briefly turned a non-bug into a &quot;bug.&quot;&lt;/li&gt;
&lt;li&gt;It wrote a health check that pinged &lt;code&gt;localhost&lt;/code&gt;, which resolved to IPv6, which the server wasn&apos;t listening on. Confident, plausible, broken.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every one of these was caught the same way: by &lt;em&gt;running the thing&lt;/em&gt;. Curling the endpoint. Inspecting the container&apos;s actual health status. Building the image fresh. Not by the AI&apos;s say-so, and not by mine reading the diff and nodding. The verification is the tax that turns &quot;looks right&quot; into &quot;is right,&quot; and there is no skipping it. An AI that can produce a plausible check is also an AI that can produce a plausibly wrong one.&lt;/p&gt;
&lt;h2&gt;The four times I said no&lt;/h2&gt;
&lt;p&gt;If the AI was the implementer, the judgment was still mine, and it showed up mostly as refusals.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The about page contradicted itself.&lt;/strong&gt; The draft described me as &quot;an architect who still writes the code&quot; — on a blog whose entire thesis is that the AI writes the code now. I caught it; we rewrote it to something truer: I stay in the code and I&apos;m on the hook for the result, whether I typed it or reviewed it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A config change almost touched the wrong scope.&lt;/strong&gt; A git identity tweak that should have been local to this repo — I stopped to confirm it wasn&apos;t global before letting it through. It was local. I was still right to check.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The trademark line.&lt;/strong&gt; I wanted the site to be playful about its domain pun. The AI&apos;s job was partly to draw the line: embrace the &lt;em&gt;spirit&lt;/em&gt;, not someone else&apos;s intellectual property.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The privacy calls.&lt;/strong&gt; Keep my CV out of the repo. Generalize client work for confidentiality. Use an alias address, not my personal one. The AI flagged these; I decided them.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A guardrail also flat-out refused to rewrite published history until I explicitly authorized it. I was glad it did.&lt;/p&gt;
&lt;h2&gt;What I still can&apos;t tell you&lt;/h2&gt;
&lt;p&gt;Now the caveats, because I&apos;d rather state them than have you find them. This is one small, low-stakes, greenfield project — a personal blog, not a system with an SLA. A few of the &quot;done&quot; checks are still unrun: I haven&apos;t put it through a real performance/accessibility audit or validated the feed against an external validator; the structure is built to pass both, but built-to-pass and verified-as-passing are different claims, and I know better than to conflate them.&lt;/p&gt;
&lt;p&gt;And the honest meta-point: I can&apos;t fully separate &quot;this worked&quot; from &quot;this worked when I did it.&quot; I knew which invariants to write, which contradictions mattered, when the AI was bluffing. The spec was where I encoded that judgment so the AI could play along. Whether someone without that judgment gets the same result, or just faster, more confident slop, is the question I genuinely don&apos;t know the answer to.&lt;/p&gt;
&lt;p&gt;But as a demonstration of the claim, this&apos;ll do: the code here was mostly machine-written, and it&apos;s not a black box, because the spec next to it says what it&apos;s supposed to do and why. Treat the code as build output. Keep the spec honest. And run the thing before you believe it.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Update (2026-06-08):&lt;/strong&gt; I&apos;ve since written the underlying argument up on its own — why the spec, not the code, is the artifact that lasts: &lt;a href=&quot;https://blog.itsmea.dev/posts/spec-is-the-deliverable/&quot;&gt;The Spec Is the Deliverable&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item></channel></rss>