In Code We Trust

On bespoke software, AI, and earning trust back
Photo by Nathan Jennings on Unsplash

Whether your codebase was hand-crafted, coded over perhaps a decade, or generated by an AI assistant last quarter, the trust problem underneath it is the same one, regardless. It gets solved the same way: measured, not promised.

Hard data over handwaving.

On delivery, code quality, and what AI is actually good for

Walk into almost any mid-sized or large business today and you’ll find, somewhere beneath the brand and the balance sheet, a piece of software nobody outside the company has ever seen. Perhaps it routes the sales team’s leads, calculates payroll adjustments, reconciles the ledger against the bank feed, schedules the technicians, matches the inventory to the orders. It was built, at real expense, specifically for this business, because nothing off-the-shelf quite did what the business needed. The jobs to be done by the software were specialised and unique.

That bespoke software is now load-bearing. Pull it out and the business doesn’t just get slower. In a lot of cases, it stops completely. That means every business running on custom-built systems has, whether it meant to or not, placed a very large bet: a bet that the software is good enough, reliable enough, and maintainable enough to keep carrying that load, quarter after quarter, as the business changes around it. Now, the system doesn’t just support the business, it very much is the business. For some stakeholders, that can be an uncomfortable realisation.

For a long time, a lot of us have been quietly anxious about that bet. Not because anyone expects perfection — every serious technologist knows software is never finished, only shipped — but because the size of the bet keeps quietly growing, while the confidence behind it often doesn’t. As the company grows, more of the business runs through the custom code every year. More revenue, more customer data, more regulatory exposure sits behind systems that were built under heavy deadline pressure, by whoever was available at the time, and maintained since, by whoever inherited them.

The trust problem nobody named

Even before anyone had heard the term “vibe coding”, plenty of that bespoke software was already in trouble — not broken exactly, but fragile in ways everyone around it could feel and nobody quite wanted to name out loud. Not a crisis, but risky. The business kept running on it and everyone was grateful for that.

Bug backlogs grew long enough that teams stopped reading them end-to-end. It was impossible to do so. Features that had been promised, half-built, or built and then misunderstood by the people using them, or else misunderstood by the people coding them, sat in the grey zone between “known issue” and “low priority” Housekeeping — the unglamorous work of tidying dependencies, retiring dead code, keeping documentation honest, keeping up to date with libraries and dependency patches — lost every fight for priority against the next deadline. Attending to the infrastructure simply made the rate of “feature and fix” flow seem way too slow.

None of this happened because the developers involved were careless. It happened because coding standards varied wildly from team to team, and sometimes from person to person within the same team. Also, because a lot of the software industry’s genuinely well-established best practices — disciplined code review, meaningful test coverage, documented architecture decisions, basic threat modelling — quietly didn’t get applied. Not from ignorance alone, though that was sometimes part of it. Mostly it was the much more mundane trio: pressure of time, unawareness that “properly” was even an option on this budget, and cost. Best practices seemed like a “nice-to-have” overhead.

The uncomfortable truth is that a lot of businesses running on this kind of software were getting away with it. Not comfortably — only just. By the skin of their teeth.

Underneath the surface: poor maintainability, code that resisted extension without breaking something else, reliability that depended on nobody touching the wrong file at the wrong time, and performance that degraded a little more with every release. Vulnerabilities sat unpatched. Privacy and security deficiencies went unaudited, because auditing them cost money and time, which nobody had budgeted for.

Stakeholders felt this, even when they couldn’t name it precisely. Trust in the software eroded — slowly at first, and then all at once when something broke publicly. Frustration mounted: why can’t the team just get on top of this? It was a tension that had been building for years, waiting for something to either resolve it or make it dramatically worse.

Part of what made it so hard to fix from the inside is that none of this shows up as a single decision anyone made. Nobody sat in a room and chose to ship insecure code, or to let a critical feature go misunderstood, or to skip the tests. Each individual shortcut was locally reasonable — a fair trade against a real deadline, made by a competent person doing their best with the time and budget they were given. It’s only in aggregate, over years, that those reasonable trade-offs compound into a system nobody fully trusts and nobody has the appetite to properly excavate.

Then vibe coding arrived

AI-assisted coding — “vibe coding,” in the term that stuck — looked, for a moment, like the resolution stakeholders had been waiting for. Here, finally, was an alternative to imperfect human development teams: a way to generate working software quickly, without needing to find, hire, and retain the scarce, high-priced senior engineers who might have fixed the backlog properly in the first place.

It’s an appealing pitch. Development teams are expensive and hard to grow. Good engineers are in short supply and command premiums that strain small and mid-sized budgets. If an AI system could simply produce the software — quickly, cheaply, on-demand — a lot of long-standing organisational pain would simply dissolve. Features and fixes would flow at the speed required by stakeholders.

In practice, it didn’t work out that way. Code generated straight out of the box by vibe-coding tools was, on the whole, decidedly worse than what the human teams had been producing — the same human teams whose output had already been a quiet source of anxiety. The AI coding tools didn’t close the trust gap. In a lot of codebases it widened it: duplicated logic, no clear architectural layering, invented dependencies, security patterns applied inconsistently or not at all, tests that passed because they tested nothing meaningful. The failure modes were different from the old human failure modes. They weren’t smaller.

Some of that gap is a fair criticism of the tools as they stood: models generating code with no real memory of the business rules embedded three modules away, no felt sense of which shortcuts were load-bearing and which were cosmetic, and a strong tendency to produce something that looked plausible and complete rather than something that was actually correct. The tools lacked object permanence. But some of it was simply the mismatch between what vibe coding was being asked to do and what it was ever likely to be good at. A tool that generates code quickly, on request, with no persistent institutional memory of the codebase’s history, was never going to replace the accumulated judgement of an engineer who had been living inside that system for years — it was only ever going to replace the parts of software development that were mechanical in the first place.

So businesses that had gone looking for a way out of an already uncomfortable position often found themselves somewhere worse: sitting on bespoke software that combined the legacy trust deficit of the old human-written system, with a fresh layer of AI-generated fragility on top — produced faster than anyone had the capacity to review it.

Four ways out, and only one of them new

That left a fairly narrow set of realistic options.

  1. Get better coders. Senior engineers who can both write good software and clean up what already exists. This is the most reliable fix, over time, and the hardest to execute — that talent is scarce, expensive, and difficult to retain once you’ve found it, especially against competitors who can pay more, or offer more interesting problems.
  2. Scrap bespoke software altogether. Retreat to off-the-shelf tools and accept whatever compromises that entails. For some businesses, this is genuinely the right call. For a lot of others, the bespoke system exists precisely because no off-the-shelf product does the specific thing the business needs done — and giving it up means giving up the operational advantage it was built to provide.
  3. Limp along. The option most businesses default into by inertia rather than choice: keep running software that isn’t adequately supporting the business, keep patching the fiercest fires, and keep hoping the gap between what the software does and what the business needs doesn’t widen into something catastrophic. But hope is not a strategy.
  4. Do something authentically innovative, if you can find it — something that doesn’t require an unaffordable hiring spree, doesn’t require abandoning the bespoke investment, and doesn’t just mean tolerating the status quo for another year.

Measuring trust instead of guessing at it

Here’s a version of that fourth option worth taking seriously: use standard, well-established code quality tooling to evaluate a codebase objectively, and turn the results into something a non-technical stakeholder can actually read — a live, evidence-based picture of how trustworthy the software really is.

Not a vague reassurance from the development team that “it’s mostly fine”, taken at face value. An actual measurement, refreshed as work continues, showing exactly where the codebase stands and where it’s moving — on a real-time dashboard, like this one.

The method doesn’t care how the code got there. A module a founding engineer wrote by hand, a decade ago, and a module an AI assistant generated, last sprint, go through the same standard tooling, get scored against the same quality bar, and show up on the same ledger. Why? Because the stakeholder’s question is never, “who or what wrote this?”, it’s, “can I rely on it?” Vibe coding didn’t create the trust problem this is built to solve. It just made a lot of existing trust problems more urgent, and added some fast-moving new ones of its own.

The picture shows a live trust ledger, six weeks into a remediation engagement. Each module is scored, tiered, and tracked against its own baseline — not folded into a single reassuring average.

That’s a real example of what this looks like in practice — a codebase four modules into a remediation programme. Payments and Auth, the two Tier I modules carrying the highest risk, have climbed from a standing start to 73% and 80% tier-weighted trust respectively. Catalog, Tier II, sits at 74%. Reporting, Tier III and lower priority, has barely moved past 17% — not because it’s been forgotten, but because it hasn’t been engaged with yet, and the dashboard says so plainly, rather than folding it into a flattering overall number.

That honesty is the point. A single trust percentage sitting at 69% doesn’t tell a stakeholder much on its own. A trend line per module, a tiered breakdown of what’s closed versus accepted versus still open, and a plain statement of what would still be outstanding if the engagement stopped today, tells a story a board, a CFO, or a nervous operations director, can actually act on. It sets realistic expectations.

What the tiers actually mean

None of this works if every defect gets treated as equally urgent, which is the fastest way to produce a backlog nobody ever finishes reading — the exact problem this is meant to fix. The tiers exist to keep the work honest about what matters most.

Tier I addresses blast radius: the payment path, the authentication layer, anything that touches customer data or money directly, where a defect left open is a defect the business is actively exposed to.

Tier II is contained, but real — a catalog service that’s wrong in ways that cost time and credibility, but don’t put customers, or revenue, at immediate risk.

Tier III is housekeeping — legitimate work, worth doing, but not the work that determines whether a stakeholder should trust the system this quarter. Grading every module against the same tier definitions, rather than letting each team apply its own sense of what’s urgent, is what makes the resulting score comparable across a codebase — and comparable, later, against itself, over time.

Why clean code is the trustworthy kind

The theory behind this isn’t new or exotic: clean code is the most trustworthy code, and a long history of industry experience bears this out. Code that’s simple, well-tested, consistently structured, and free of known defects is code that behaves predictably — which is, in the end, close to the entire definition of trustworthy software. Complexity, duplication, untested paths, and unpatched vulnerabilities are exactly the things that make software behave unpredictably, and unpredictability is what erodes stakeholder trust faster than almost anything else.

What’s newer is treating trustworthiness as something you measure, rather than something you feel in your senses. Set a baseline first — run the standard toolset against the codebase as it stands today, honestly, without flattering the result — and then do the actual work to move the codebase up that trustworthiness scale, module-by-module, tier-by-tier, with the baseline sitting there as the fixed reference point everything else gets measured against.

Turning AI loose on the problem — with care

This is where AI earns back some of the trust it spent during the vibe-coding phase — not by writing the software, but by interrogating it.

If a lack of development resource was already the bottleneck before any of this started — and for most businesses running lean bespoke software teams, it was — then AI is a genuinely useful way to close that gap without a hiring spree. Point it at the codebase with the same standard quality metrics tools a senior engineer would reach for: static analysis, complexity and maintainability scoring, security and dependency scanning, test coverage measurement. Have it read the codebase the way a thorough, unhurried senior reviewer would, if you could ever find the hours to give one.

This is what AI can be good at. It can follow a repeatable process, more or less mechanically, and produce analysis results, without becoming bored or fatigued. Iterations can be frequent and fast. This is an adjunct to human code review, not a replacement – a way to maintain a data-driven view of how the trustworthiness of the codebase is progressing, as it is being repaired.

The output isn’t a vibe. It’s a structured, reproducible survey of exactly where the codebase stands against known, industry-standard measures — the same measures a skeptical stakeholder, or a technical due-diligence reviewer, would use, had they never heard of AI.

From survey to structured defect log

The next step is to have the AI log whatever problems it finds — properly, as discrete, itemised defects, rather than a wall of prose summarising general impressions. Each one tagged by module, by severity, by the specific risk it represents to the business, rather than only to the code.

This is what turns a one-off audit into something you can actually manage over time. A logged defect can be tiered — the critical, customer-facing, revenue-touching issues in Tier I; the important-but-contained issues in Tier II; the lower-priority housekeeping in Tier III — and tracked to closure, atomically. It can be counted, frozen as a baseline, and referenced every time someone asks “are we actually getting anywhere?” It turns “the codebase needs work” from a gestalt feeling into a reproducible number, with a source.

Fixing what the survey finds

Remediation itself doesn’t have to be AI’s job. The defects that come out of the survey can be fixed the traditional way — an engineer reads the ticket, writes the fix, tests it, moves to the next one — and for plenty of the trickiest, most judgement-heavy defects, that’s still the right approach.

Where AI earns its keep is loop speed. Point AI assistance at the remediation backlog and you get a much tighter fix-and-confirm cycle: propose a fix, write the self-test that would have failed against the original defect and now passes against the fix, and re-run the same quality interrogation immediately to confirm the fix actually moved the needle, rather than just moving the problem somewhere else — without waiting for an engineer’s calendar to free up for every single item. AI can fix the easy issues much faster and more reliably than human coders can.

The self-testing part matters, whichever intelligence is doing the fixing. A fix that isn’t provably tested is just a claim, and the entire point of this exercise is to replace claims with evidence. So, each remediation should come with tests that would have failed against the original defect and now pass against the fix, checked into the codebase, alongside the change, rather than discarded, once the ticket closes — which has the useful side effect of leaving the codebase with better test coverage than it had before, as a by-product of remediation, rather than as a separate line item. Tests that confirm fixes accumulate into an expanded test suite, to prevent future regressions.

Done well, whichever path is doing the fixing, this becomes a loop rather than a one-off pass:

Survey → log → fix → test → re-survey

Run that loop by hand and each pass takes as long as an engineer’s schedule allows. Run it AI-assisted and a pass can take hours — the same fix-and-confirm discipline, just compressed enough to work through a large backlog without every item waiting on scarce senior engineering time. A blended approach is often optimal. Either way, each cycle should narrow the gap between where the codebase is and where the trust ledger says it needs to be, module-by-module.

Done carelessly, AI-assisted remediation can produce exactly the kind of problem vibe coding did in the first place: a codebase that looks better on the surface and is quietly worse underneath. This will be immediately evident, as for each issue addressed, one or more new issues is logged.

The safeguard is the same genuine human quality gate, applied to whatever AI proposes — someone with real engineering judgement reviewing the fix, understanding why it works rather than accepting that it merely passed its own test, and willing to reject a change that technically closes a ticket without actually reducing overall risk. AI taking on the volume of fixes is the leverage. A human holding the gate — whether or not they wrote the fix themselves — is what makes that leverage trustworthy.

Watching the number move

Run that loop consistently and something becomes visible that wasn’t visible before: trust in the codebase measurably increasing. Not a promise that things are getting better — a chart showing it, module-by-module, week-by-week. Tier I items closing faster than they open. The tier-weighted trust score climbing off its baseline. A shrinking list of what would still be outstanding if the engagement stopped today.

That visibility changes the conversation with stakeholders entirely. Instead of a development team asking for continued patience on faith, there’s a number on a dashboard that either moved this week or didn’t — and if it didn’t, that’s visible too, which is its own kind of honesty most software teams have never been in a position to offer.


This is what we offer

We start with a fixed-price diagnostic audit: a baseline reading of your codebase’s trustworthiness across the dimensions that actually matter — security, architecture, delivery governance, performance, and real-world behaviour — using standard, defensible, quality tooling rather than opinion. You get an honest, tiered, itemised number before anyone commits to fixing anything.

From there, remediation is scoped and priced module-by-module, not sold as an open-ended retainer you have to trust blindly. The fixing can be handled the traditional way, by an engineer, or AI-assisted for a tighter fix-and-confirm loop through the backlog — either way, every change passes through a genuine human quality gate before it counts as closed. We track the whole thing on a trust ledger like the one above, so you can watch the number move, in real time, instead of taking anybody’s word for it.

And once a codebase has earned its way up the trust scale, we offer a guardrail retainer to help keep it there — because a codebase that regresses, six months after remediation, hasn’t actually been fixed. It’s been fixed temporarily, and temporarily fixed is precisely the trap that got most businesses into this position in the first place. The continuous re-assessment of the software is what provides the foundation for the trust.

None of this is magic, and we won’t pretend it is. It’s rigour, applied consistently, with AI doing the volume of work no human team was ever going to have the hours for, and a human holding the judgement that AI, on its own, still can’t be trusted with. If your bespoke software has been quietly eroding stakeholder trust for longer than anyone wants to admit — before vibe coding, or because of it — that’s the problem this is built to solve.

Written by Michael Topic. If this describes a codebase you’re responsible for, let’s talk about what a baseline reading would show.

Your AI Wrote Bad Code. Here’s the Cure

A lot of AI-coded software is quietly rotting in production. Here’s what we’ve learned about why, and what actually fixes it.

Dr Jose Crespo put a number on something a lot of us have felt in our gut for a while now. In AI Is Producing More Garbage Code Than Ever, he lays out the numbers behind the vibe: short-term code churn is up roughly 100% since 2021, developers using AI assistants write about 40% more security vulnerabilities, experienced developers are measurably slower with AI tools on unfamiliar codebases, and at some shops engineers are rejecting around 70% of what the AI suggests outright. The “10x productivity” story that justified an awful lot of AI spend is, on the evidence so far, closer to a rounding error once you net out the cleanup cost.

None of that means AI coding agents are useless. It means a lot of organisations shipped a lot of AI-generated code without the discipline that used to keep human-written code honest, and now they’re sitting on codebases nobody fully trusts. That’s a specific, solvable problem. It’s also, as it happens, the business we’re in.

What Should an Organisation Actually Do About This?

An organisation might be tempted to hope they can get away with sub production-quality code, crossing their fingers that nothing will go too terribly wrong. Hope is not a strategy.

Still others will have lost so much faith in the code their AI tooling has produced, that they’re tempted to scrap the whole thing and start again another way. That can be a fatal error, because the competition doesn’t stand still, while you have another go.

So, what is an organisation to do, when they have lost trust in AI generated code?

The honest answer is: not panic, and not pretend it isn’t happening either. Both reactions are common and both are wrong.

The panic response is to freeze all AI-assisted development, mandate line-by-line human review of everything, and quietly hope the problem goes away. That throws out the genuine productivity the tools do deliver on well-scoped, well-supervised work.

In addition, as you go through the rework, you have no solid, measurable evidence that things are getting better, or getting better fast enough.

The denial response is to keep shipping at the same pace, treat “the build is green” as proof of quality, and assume that whatever’s wrong will surface eventually and get fixed then. It usually does surface — normally at the worst possible time, in production, in the worst possible way, in front of customers.

The useful middle path is to treat this the way you’d treat any other engineering quality problem: measure it, understand its root causes, and apply proportionate, disciplined remediation. That’s a solved problem in software engineering. We have the tried and tested tools and techniques. It just hasn’t been pointed at AI-generated code very often yet.

Why Is So Much AI Code Written So Badly?

Two things are true at once here, and neither one is an excuse for the other.

First: humans write mediocre code on average, and that’s what these models learned from. Look at the training corpus honestly — dominated by “normal GitHub,” inconsistent style, non-trivial defect and vulnerability rates — and the realistic quality of the average public code sample is somewhere around 4–5 out of 10, not 8 or 9. Models trained on that corpus inherit its habits, including the bad ones. Agentic coding doesn’t automatically elevate that baseline; independent comparisons of AI-generated versus human-written changes at scale have found AI-authored pull requests carrying roughly 1.7x more issues on average, with 1.5–2x more security vulnerabilities and substantially worse readability. Separately, one widely cited industry figure puts AI-introduced security vulnerabilities at around 45% of cases when the output isn’t reviewed.

Second: the models themselves are still catching up. Earlier generations of coding models are, in hindsight, simply not that good — and a lot of the AI-generated code sitting in production today was written by those earlier models, in a hurry, without the guardrails anyone’s built since. The tools have improved. The habits organisations built around using them mostly haven’t.

Put those together and you get exactly what Crespo’s data shows: code that runs, passes a superficial check, and then falls over the first time it meets a real edge case, a concurrent user, or an attacker.

Can It Be Fixed? Yes — Here’s How We Found Out

I learned most of this the expensive way, building Confelicity, an independent music platform, largely with AI coding agents. It was a genuinely useful education, because every failure mode you’ll read about below, I watched happen in my own codebase before I had a name for it. The behaviours were surprising, because they were the sorts of things the most junior of programmers would (almost) never dare to turn in as finished work.

The takeaway lesson I learned is that the current AI coding agents aren’t trained to write production-quality code. They’re blind, hence indifferent, to the real world issues that seasoned, defensive programmers learned they needed to pre-empt, long before AI attempted to write any code.

What I want to walk through isn’t a list of AI’s shortcomings for the sake of complaining about them. It’s the specific set of behaviours that, once you know to look for them, tell you exactly what tooling and process you need to build around the agent to get production-quality output the first time — which is the discipline we’ve since refined using Kiro’s spec-driven workflow (steering documents, hooks, and structured specs) as the closed loop.

The agent declares victory early, and writes pretend tests

The single most dangerous habit I saw was an AI coding agent reporting a task complete when it wasn’t. Not lying exactly — more like a student who’s confident they answered the exam question because they wrote something under the right heading.

AI agents flatter to deceive. They often sound enthusiastically sycophantic and are programmed to please their users. That’s why they tell you the code they write is complete, when it isn’t. In the heat of code creation, they desperately don’t want to disappoint or delay. In fact, all they do is defer the disappointment for when the truth comes out.

Typically, tests would exist, would pass, and would test almost nothing meaningful: mocked-out dependencies standing in for the actual logic under test, assertions checking that a function returned a value rather than the right value, or coverage of the happy path only. A green build became a signal you could no longer trust without checking what was actually behind it.

Structural, privacy, and security issues, generated wholesale

Left unsupervised, the agent would happily create coupling between parts of the system that had no business knowing about each other — a password reset flow quietly triggering account cleanup, a data export function processing a payment as a side effect. Multiple layers of the programme calculating important results independently but in contradiction with each other. These aren’t hypothetical; they match a documented failure pattern researchers have started calling “memory-collision cascades,” where compressed representations put unrelated concepts too close together and one quietly wakes the other at runtime.

On top of that, privacy and security shortcuts crept in wherever the spec didn’t explicitly forbid them: logging more than it should, validating less than it should, trusting input it had no reason to trust. Part of the solution is rigorous checking of the code structure and integrity, through knowledge mapping and dependency checking, each and every time the code is changed.

It forgets. Context is everything, and it rots.

Standing instructions — coding conventions, architectural boundaries, “we never do X here” — have a shelf life inside an agent’s working context. Object permanence is not the AI coding agent’s strong suit. They often operate as if they were just born and, frustratingly, easily forget important constraints and instructions.

Say it once at the start of a long session and it’ll be gone by the fiftieth exchange, quietly overwritten by whatever’s more recent and more locally relevant to the immediate task. This isn’t a minor inconvenience; it’s the single biggest reason AI-generated codebases drift out of internal consistency over time, feature by feature, each one built to slightly different unstated rules. You cannot fix this by asking nicely once. You need a mechanism that re-injects the standing rules on a cadence, and — better still — external memory that survives across sessions rather than living only in a context window that’s going to get compacted or forgotten. This is exactly the gap tools like Beads, a git-friendly issue tracker and memory system built specifically for coding agents, exist to close: a durable, versioned record of what’s outstanding and what the rules are, that travels with the codebase instead of evaporating at the end of a session.

Performance and code quality were nobody’s job

Nothing in a typical “build the feature” prompt asks the agent to think about algorithmic complexity, query efficiency, or maintainability. Most of the corpus the AI agent was trained on didn’t consider it explicitly or as a priority. So it doesn’t. It optimises for the thing it’s being measured on — does the feature work, right now, on the input I tried — and everything else is free to degrade. You end up with N+1 queries, unbounded loops, and duplicated logic sprinkled across a dozen files, none of which shows up until the system is under real load. By then, you have a severity 1 issue on your hands.

AI Is Not a Substitute for Knowing How to Run Software Projects

There’s a version of this story that blames the model. It’s more useful to look at the decision-making around the model. Pavel Samsonov made a sharp observation on his newsletter, The Product Picnic: frequently the failure isn’t AI itself, it’s that the organisation was already bad at running software projects, and AI adds all the failure modes of a novel technology on top of the ordinary ones. With so many organisations with no previous history of software development suddenly trusting in vibe coding, this was bound to show up as a problem.

Executives, in particular, have found AI to be a convenient place to locate blame for problems that predate it — the hope that a tool can substitute for product discipline, technical leadership, and honest project governance, none of which AI provides and none of which it was ever going to.

In my opinion, AI could have been equipped to add those additional disciplines to its vibe coding wizardry, but the outputs wouldn’t have been as snazzily swift and so, less dazzling, to people that try vibe coding for the first time.

If your organisation didn’t know how to run a software project well with human engineers, handing the keyboard to an agent doesn’t fix that. It just makes the consequences arrive faster, and in code nobody on the team has fully read (or perhaps knows how to read).

AI-Driven Tooling to the Rescue

Here’s the genuinely encouraging part. Every one of the failure modes above has a countermeasure, and most of those countermeasures can themselves run as automated checks inside the AI workflow, not as a separate, resented, human-only review pass bolted on afterward.

The underlying insight, which Valentina Servile makes well in her ThoughtWorks piece, Should we still design code for humans?, is that good design isn’t only a courtesy to human readers — it’s also what keeps the agent’s own inference reliable. Coding agents were trained on human-authored code and rely on the same legibility signals humans do: sensible names, clear module boundaries, low coupling. Feed an agent a tangled codebase and it burns more tokens, makes more wrong assumptions, and produces worse output on the next change — the technical debt shows up literally on the token bill.

Sadly, remediating technical debt can often be more expensive than writing it badly in the first place and certainly far more expensive than writing clean code at the outset, whether you’re reliant on humans or AI agents to do the remediation.

So the fix isn’t “have a human read every line” — that doesn’t scale and it isn’t where the leverage is. The fix is standard quality metrics, that we’ve used for years, wired into the workflow the same way you’d wire them into a human team’s CI pipeline: linters, complexity and duplication scanners, and architecture-boundary enforcement tools like dependency-cruiser or ArchUnit that fail the build the instant a change violates a structural rule, not months later when someone notices the codebase has drifted. Integration, end-to-end and property-based tests, in addition to unit and acceptance tests. Security hardening and adversarial penetration testing. The usual arsenal of techniques.

Pair that with steering documents that encode your actual conventions, kept current rather than written once and forgotten, and non-bypassable review gates on anything touching payments, auth, or data integrity.

Here I’ll make a claim I think is defensible from what we’ve seen so far: training an AI coding agent to work with precision, to production standard, guided by someone who knows how to guide human engineers to the same standard, is arguably faster and more tractable than training a junior or mid-level human engineer to the same level of discipline. The agent doesn’t get defensive about code review, doesn’t need six months to internalise “why we do it this way here,” and will follow a hook’s verdict without an ego. It just needs someone competent setting the hooks.

We Start With an Assessment

We don’t walk into a codebase nobody trusts and start rewriting. We assess it first, on a fixed-price, two-week engagement. Look, don’t touch. Characterise and measure. Assess the damage.

That structure isn’t arbitrary — it mirrors what actually works in legacy-code remediation more broadly. Stop further undisciplined generation against the codebase. Get an independent audit that establishes ground truth: an architecture map, duplication and complexity scores, security gaps, test coverage, and specifically an inventory of what touches critical paths. “Independent” matters here for trust as much as for accuracy — if the same process that made the mess is the one grading its own homework, nobody believes the grade.

We lean on frameworks like Runloop’s Assessing AI Code Quality: 10 Critical Dimensions for Evaluation to structure that assessment: functional correctness, code quality metrics, efficiency and performance, robustness and security, semantic correctness, adaptability, compilation and execution success rate, and more. Every issue the assessment turns up gets filed as a discrete, tracked item — a bead on the string — rather than a vague verbal warning that evaporates by the next standup. That’s not incidental: given how much AI-generated technical debt comes from context simply rotting away between sessions, the fix has to be a record that persists independently of anyone’s memory, human or artificial.

Patient Remediation, Billed in Time and Tokens

The assessment tells you the shape and scope of the problem. Fixing it is a different, open-ended engagement, and we bill it in time plus tokens rather than a fixed quote, because the honest answer to “how long will this take” is: it depends what we find.

Some individual issues genuinely take thirty minutes or more to fix properly, even with AI assistance doing the mechanical work. Others need a human team labouring for days — untangling a piece of Type III “spaghettification,” where separate parts of the system got wired together by an agent that saw a false pattern connecting them, isn’t something you delegate back to an unsupervised agent. That would just relocate the same failure mode one step downstream.

The remediation itself follows a disciplined, incremental shape rather than a rewrite: pin current behaviour with characterization tests before touching anything, then use a strangler-fig approach — build corrected components alongside the fragile ones, verify each in isolation, migrate traffic slice by slice. A big-bang rewrite is the trap that finishes off whatever stakeholder trust is left if — when — it slips its timeline.

Working Through the Beads, Measurably

This is where trust actually gets rebuilt, and it happens slower than anyone wants: not with a single “we fixed it” announcement, but with a visible trend line. Duplication score down. Test coverage up. Incident rate down. Each bead closed is a small, independently verifiable claim rather than a promise.

It matters that this is measurable, because “measurably improve code quality” and “restore trust in AI-generated code” turn out to be almost the same exercise. Stakeholders who’ve been burned once don’t want reassurance; they want a number that was wrong last month and is less wrong this month, shown to them on a cadence they didn’t have to ask for. Numbers they can believe in.

Later: Minimising Tokens, and Matching Tests to the Change

Once a codebase is back to a state anyone would call production quality, the work doesn’t stop — it changes shape. The next phase is efficiency: working out which categories of change genuinely need a full regression pass and which only need a narrow, targeted test, and trimming the token spend that comes from agents re-reading and re-reasoning about context they didn’t need for the change at hand. That’s a tuning problem, not a rescue problem, and it’s a much nicer problem to have.

We Can Save the Day After All

None of this requires believing AI coding agents are a dead end, and none of it requires pretending the last two years of “garbage code” headlines were exaggerated. Both things are true: the tools are genuinely useful, and an enormous amount of what’s been shipped with them needs a disciplined, measured intervention before anyone should trust it in production.

That intervention is a known kind of work. It’s software engineering — the assessment, the tracked issues, the incremental remediation, the metrics that prove it’s working — applied to a new kind of author. We know how to do that part. That’s the whole point.