six months of agents: the good, the bad and the ugly
it’s july, which with the rounding this post needs is half-time. in january i was refactoring my own agent loops by hand and writing about claude code’s hooks. by june, a team of agents was opening draft pull requests against a production debt-collection product while i slept, and an ios app they mostly wrote was sitting in the app store. that’s a steep six months. steep enough that the honest way to sort it is the sergio leone way.
the good
the headline is leverage, and it’s not hypothetical. most evenings i open my laptop to draft prs that weren’t there in the morning — refined, reviewed, qa’d, security-checked, each one closing a specific issue from my backlog. i’m still the only person on the product. what changed is that i kept the two decisions that need me — what’s worth building, what’s good enough to ship — and handed the structured middle to a pipeline of narrow personas. for one person running a real product in the margins, that’s the difference between a backlog that mocks you and a backlog that drains.
the app store release is the proof i’d point at. a native swiftui app, written almost entirely by the same team that runs the backend, live, with paying users checking money they’re owed on a phone. january me would not have believed june me.
the quieter wins were embarrassingly cheap. prompt caching took about twenty lines and cut roughly 80% off the input bill of my heaviest workload. spec-kit brought specs back by making them five minutes of conversation instead of an afternoon at a keyboard. and treating a tool call plus its guardrails as one unit meant most of my agent code became plain functions that test without a model in the loop. none of this is exotic. that’s the good news — the wins of 2026 so far have mostly been discipline that got cheap, not magic that got real.
the bad
i throw away agent work. still. weekly. prs that miss the point of the issue, prs that solve it in a way i don’t want, prs that trip a review gate and never reach me — which is the system working, but is still compute spent on a thing i delete. the one genuine upside of an agent team here: nobody sulks when i close their pr. as a manager, i notice that.
the models show their age at the edges. the swift implementer kept reaching for NavigationView and the old onChange(of:) — code that compiles, warns quietly, and behaves wrong on a current ios. training cutoffs are a supply chain, and fast-moving frameworks are where the shelf date shows.
and the homelab kept me humble. i asked a local qwen to explain proxmox to a docker user; it confidently invented a docker integration that doesn’t exist. llama, same prompt, took twice as long and omitted half of what proxmox actually does. between the two of them there was one correct answer, distributed unevenly. at 5 tokens a second on cpu, you have plenty of time to reflect on that.
the pattern in this whole column: nothing failed loudly. the deprecated api warns quietly. the hallucinated feature reads fluently. a generous tool description gets picked for everything, because of course it does. the bad in 2026 isn’t the model refusing or crashing. it’s the model coping.
the ugly
the ugly is the stuff where “the model coping” stops being an evening lost and starts being a liability.
when i put an oauth-protected mcp server in front of real debt-collection data, the security review found that my “read-only” integration was a ui promise, not a boundary — the backend couldn’t tell an oauth token from a session token, so a leaked token could write. it found that the mcp container and the backend shared an hmac secret, which meant the smaller, newer, less-hardened runtime could mint admin tokens for the big one. it found bearer tokens landing in logs because an sdk attached the request object to thrown errors. none of these were the model’s fault. all of them were mine. that’s what makes them ugly: agents multiplied how fast i could build attack surface, and did exactly nothing for the threat model.
agents collapsed the cost of the middle. the two ends — what's worth building, what's safe enough to ship — got more valuable, not less.
the other ugly is quieter. an llm’s arguments are user input, and the moment the host trusts a model-supplied userId you’ve built confused-deputy-as-a-service. a cache breakpoint after a timestamp caches nothing and tells no one. an output check nobody wrote lets a widened enum drift into production wearing a green checkmark. the ugly failures of this half-year all share a shape: they keep working. crashes turned out to be the good failures.
apple deserves an honorable mention here. my first app store submission was rejected by a human — no demo account in the review notes — which after six months of automated gates felt almost nostalgic. mobile added a third gate i can’t hand back: signing keys, a device in my hand, a reviewer on apple’s clock. the perimeter of a platform is untouched by all of this. worth knowing before you promise anyone a timeline.
the half-time score
tallying up: the leverage is real, the cheap wins are really cheap, and the failure modes have migrated — away from “the model can’t do it” and toward “the model did it fluently and nobody checked.” every good thing on this list came with a verification bill, and the bill always lands on the same desk. mine.
so the second half looks like more of the same, deliberately: the two human gates stay, the threat model stays next to the code, the evals get written before the prompts. maybe a gpu joins the rack so the homelab models can be wrong faster.
see you at the full-year one, where i find out which of these paragraphs aged badly.