Writing · Defect taxonomy

What we find when we open other people's code.

A Purpledecks defect taxonomy. Drawn from fourteen years of takeovers, audits and rescues.

Barry Gough, Chief Technology Officer · 7 July 2026 · 40+ codebases opened
What this is

We build software. We have since 2012. A good part of the work is not building from scratch. It is being handed a system someone else built and asked to take it over, fix it, or judge whether it can be saved at all.

Since 2012 we have opened and assessed more than forty codebases other people built. Apps, portals, backends, legacy systems, and lately code written mostly by AI. This document catalogues what we keep finding inside them.

It is written for someone who has been burned once and wants to know we have seen their problem before. It is a reference, not a story. Read the entry that matches your worry, or read the lot.

About the numbers, read this first

The frequency figures in this document are our own estimates. They are the honest read of a senior team that has done this work more than forty times over fourteen years. They are not counted from a spreadsheet, and we will not pretend they are. If you ask us whether a number came from our records or our judgement, the answer is judgement, and we are happy to say so.

We have used rough bands, not exact figures, on purpose. A band like "a quarter to a third" is a considered estimate. It is not a measurement, and it is not dressed up to look like one.

Everything in here is ours. Every example is a real system we opened. We have changed or held back details where a client or a partner has a right to that. There are no borrowed war stories in this document and no industry survey figures. This is what we have seen with our own eyes.

Where the bad code comes from

A word on this, because most people get it wrong.

At the small end, cheap is a real risk. A lot of what lands on our desk came from low-cost outsourcing paid to hit a spec and nothing more, from first-time builds by people out of their depth, and lately from AI-built code that ran in a demo and was never made fit for real use. That pattern is real and we see it often.

But the price tag was never the actual tell. We have opened expensive systems that were just as dangerous. The thing that separates a safe system from a rotten one is not what was paid for it. It is whether the basics were there. Version control. Testing. Someone who understood what they shipped. Clear ownership of the keys, the accounts and the domain. When those are missing, the system is a problem, whatever it cost.

One more thing worth saying plainly. You are often not just taking on bad code. You are taking on the client's own bad habits too. Passwords shared around in chat and email. Codebases mailed about as attachments. Customer data left open on the internet. The software is only half the job. Sometimes the bigger job is fixing how the whole place handled its own keys and its own data.

How to read the frequency column
Nearly always
Roughly 75% and up. We expect to find this. If it is not there, we are pleasantly surprised.
Very common
Roughly 50% to 75%. More often than not.
Common
Roughly 30% to 50%. A regular finding. Not a given.
Now and again
Roughly 15% to 30%. Not the norm, but no shock either.
Occasional
Under 15%. The rarer stuff. When it turns up, it tends to be memorable.

These bands are our judgement, per the note above.

01

Security and data exposure

What we findHow oftenFair enough?What it means
Hardcoded secrets. Passwords, keys and database logins written straight into the source, and committed into the history where they live forever. Very common Never fair on anything real. A hobby project, maybe. A live system, no. Regulated: a leaked key to health or financial data is a breach on its own. Non-regulated: attackers scan for these in seconds and walk straight in.
Keys passed around the business. Credentials shared in chat, email and shared docs. The client's own habit, not just the builder's. Common Not fair, but usually ignorance rather than laziness. You are securing the organisation, not just the code. Every copy is a live risk.
Data left open on the internet. Storage set to public that should be locked, with real data behind it. Now and again Never fair. Regulated: with health or financial data behind it, this is a reportable breach and a matter for a solicitor. Non-regulated: a data breach waiting to be found.
Broken access control. Parts of a system with no login check. Anyone who reaches the server can do the thing. Common Never fair on anything holding real data. Regulated: unauthorised access to records you are legally bound to protect. Non-regulated: account takeover and data theft.
Weak or hardcoded fallback keys. A login system with a guessable fallback secret baked into the source, so anyone who reads the code can forge access. Now and again Never fair. Anyone with the code holds the master key. Full rotation and a rebuild of the auth path.
One we opened

On one system we took over, a messaging token and a cloud key were both sitting in the code and baked into the version history. Private files, including private messages between users, had been left in storage open to the public internet. Left as it was, it was a breach waiting to be found. We rotated the keys, locked the storage down, and wrote the data-handling paperwork that had never existed.

A note on confidentiality

Every example here is altered or combined so no client, system or project can be recognised, and we never name clients. What we find in yours stays between us.

02

AI-built code

This section is newer than the rest, and it is growing fast. We use AI tools heavily ourselves, every day. So this is not a case against AI. It is a case against AI in the hands of someone who cannot tell when the output is wrong.

How to tell if code is AI generated

The quickest tell is inconsistency. Every part of the system is built a different way, as if a different person wrote each piece. One API done one way, the next done another. That is the classic signature of AI code shipped by someone who could not tell good output from bad.

Alongside it: happy-path-only code, and long stretches with no handling for failure. These are the code smells we look for first, and they are usually the fastest way to date a build to a tool rather than a developer.

What AI-generated code actually looks like when you open it

What we findIn AI-built workFair enough?What it means
Runs in a demo, falls apart in production. Code that looks grand on screen and comes apart under real use, because nobody who understood production ever looked at it. Nearly always Depends. Fine for a prototype. Not fine once it is live and taking money or data. Regulated: an AI build with no record of how it was checked is close to impossible to certify. Non-regulated: it ships, then it breaks under real load.
Inconsistent patterns throughout. Every part built a different way, as if a different person wrote each piece. Very common Not fair when sold as the work of an experienced developer. A nightmare to maintain and reason about. It usually means the person shipping it could not tell good output from bad.
Missing failure handling. Happy path only. No handling for the third-party call that fails, the payment that times out, the request that half-completes. Very common Never fair on anything handling money or critical data. Silent failures. Payments that vanish. Data that quietly goes wrong with no error to catch it.
Regression by prompt. A later change silently drops an earlier safeguard, because the tool rewrote the file and lost the bit that mattered. The screen still looks fine. Now and again Never fair, and hard to catch without a trained eye. Safety removed while the demo still works. This is exactly why AI code needs a senior reviewer, not just a prompt.

Is AI-built code safe to ship

It depends what you are shipping. For a prototype, fine. Once it is live and taking money or regulated data, not without a senior pass over it first.

The dangerous one is regression by prompt: a later change quietly drops an earlier safeguard because the tool rewrote the file and lost the bit that mattered. The screen still looks fine. That is exactly why AI-built code needs a senior reviewer, not just another prompt. We sell the engineering, not the AI.

One we opened

A company came to us after a developer, who had claimed to be far more experienced than he was, built their system using AI he did not understand. Bugs were showing up everywhere, worst of all around billing. Customers were being charged twice. People who had cancelled were still being billed. When we opened the code, the AI signs were plain. No two parts were built the same way, each API done differently, as if a different coder had written every piece. Nobody had understood what they were shipping. We rebuilt the billing path, put in the tests that were never there, and stopped the double-charging.

03

Fake substance

Something was sold as clever. An algorithm. A model. "AI." Automation. You open it and there is nothing there.

What we findHow oftenFair enough?What it means
Hardcoded pretending to be clever. An "algorithm" or a "model" that turns out to be hardcoded values, a lookup table, or a pile of if-statements. Now and again Never fair. This is selling something that does not exist. Regulated: selling a hardcoded script as a validated clinical or financial algorithm will not survive an audit, and it exposes you badly. Non-regulated: you paid a premium for a clever system and got a fragile script.
People behind the curtain. "Automation" that is really staff doing the work by hand, dressed up as software. Occasional Never fair. The business does not own what it thinks it owns. It owns an expensive illusion held up by people.
Data baked into the app. Content or data hardcoded into the build, so every change needs a whole new release pushed to the store. Now and again Rarely fair. Occasionally a defensible shortcut on a tiny app. No live system behind it. Every small change is a slow, heavy release. It does not scale and it was never meant to.
One we opened

One company came to us believing they had a working system. What they actually had was people going into the databases by hand, at night, changing records so the software looked like it was doing something. A machine with people hidden inside it. We built the working system underneath, and retired the hand-editing for good.

04

Maintainability

Security is what gets you sued. This is what quietly bleeds you. It is also the damage that does not show at handover. A takeover often turns into a rescue only once you are inside and see what you actually took on.

What we findHow oftenFair enough?What it means
Little or no testing. No automated safety net, so every fix risks breaking something you cannot see. Very common Often fair on a small early build on a tight budget. Not fair on a system handling money or health data. The judgement is the whole point. On a small app, a reasonable trade. On a serious system, dangerous.
Giant tangled files. Single files running to thousands of lines. No line between screen, business rules and database. No comments, no documentation. Common Rarely fair. Usually a sign nobody senior was involved. A change that should take three hours takes three weeks. The owner pays engineers to fight the code instead of improving the product.
The wrong tool for the job. A content system running a bank's sign-up. A website scraper standing in for a proper interface, so it breaks every time the site changes. Common Never fair. These are decisions, made wrong and made early. You cannot patch your way out of it. The part that was wrong has to be rebuilt.
Performance that rots as it grows. Grand at launch, buckles as the data and users pile up. Queries pulling everything into memory. Missing indexes that only bite at volume. No caching. Common Partly fair. Easy to miss early. Not fair to leave once it bites. Logins that crawl. Reports that time out. A cloud bill that climbs while the system does less.
One we opened

One estate we assessed had files past eleven thousand lines, and single functions past three thousand. No comments. No documentation. The only way to understand it was to read all of it. Another had gone from a two-second login to twenty, and reports that once took thirty seconds taking five to ten minutes. We broke the giant files apart along clear lines, added the missing indexes and caching, and brought the login back under two seconds.

05

The basics that were never there

Below the code sits the plumbing. In bad builds it is missing, and it is often invisible until you go to do something ordinary and find you cannot.

What we findHow oftenFair enough?What it means
No version control, or no access to it. No history at all, or the owner cannot get into their own. Common Never fair. You spend the opening days of a takeover just chasing access instead of fixing anything.
Lost signing keys. The key to update an app sitting on a personal laptop, or gone entirely. Now and again Never fair. Basic hygiene. On Android in particular, lose the key and you can no longer update your own app in the store.
Nowhere safe to test. No separate testing space, so every change is tested live, on real users. Common Rarely fair. Every change is a gamble taken in front of the customer.
Backups nobody has ever tried. A backup that has never been restored. Or none at all. Common Never fair. A backup you have not tested is a hope, not a plan. In one case, data was deleted and simply gone.
The vanished developer. One person built it, held it all in their head, and left. Knowledge, passwords, reasons, all gone. Now and again A risk, not always a fault. But a fault to have allowed it. Nobody left who knows how it works or how to build it.
The hostage. A soured relationship where a builder sits on the code, the accounts or the domain. Now and again Never fair on the client's side. We have met owners who did not hold their own cloud root account or their own repository.
One we opened

We inherited an app where the signing key was gone, and no firm had a way to update it in the store. We have spent the first days of more than one takeover just chasing signing keys, cloud accounts, developer accounts and third-party access before we could safely touch a line. Where a key is truly lost we say so plainly. What we can do, and did, is get the client back in control of every account and credential that could be recovered, and set up the hygiene so it cannot happen again.

06

Who built it, and the mess that follows

Not code problems. People problems. And they often decide whether a rescue can work at all.

What we findHow oftenFair enough?What it means
Out of their depth. Work-experience students or juniors with no oversight, code shipped live to a paying customer. Now and again Not fair to have shipped it without a senior eye on it. We inherit the mess. The people who built it were never the problem. The absence of oversight was.
Oversold experience. Someone who claimed to be far more senior than they were, propped up by AI they did not understand. Now and again Never fair. The damage shows up in production, often in billing, and it is expensive to unwind.
Not ready to face the depth of it. Wants to band-aid it, and band-aid it again, rather than look at the real state. Common Understandable, but it has to be faced. We take on a rescue only if the real state of it is faced. If the ask is just another plaster, we are not the right firm, and we say so up front.
The grudge at handover. The person who built the mess holds the knowledge and will not share it. Sometimes quiet sabotage. Now and again Never fair. One reason getting full control of accounts and access early is not paperwork. It is protection.
07

Data and privacy, where it stops being technical

For some clients a defect is a bug. For others it is a licence, a fine, or a patient. The same missing lock means very different things depending on who is behind the door.

What we findHow oftenFair enough?What it means
Personal data logged in the clear. Names, messages, health or financial detail written to logs in plain text. Common Never fair on sensitive data. Regulated: a serious problem on special-category data. Non-regulated: still a breach risk and a trust problem.
No paperwork behind the data. No lawful basis written down. No impact assessment for high-risk data. No agreement with the people processing it. No retention rule. No breach plan. Very common in small-vendor builds Fair to lack it on a hobby project. Not fair on anything holding real personal data. On a regulated build, that missing paperwork decides whether the thing is even legal to run.
One we opened

On that same system, personal data was sitting in storage open to the public internet, and none of the governing paperwork existed. Under GDPR, this kind of personal data is the most protected category there is. If real users' data was exposed while that door was open, that is not a bug you quietly fix. We closed the door, then did the harder part: writing the lawful basis, the retention rules and the breach plan that should have been there from the start.

The verdict

Harden, rebuild, or leave it.

Every audit ends with one of three answers. Harden it as it stands. Rebuild it, in part or in full. Or leave it be, it is sound.

Leave it be is rare. Nobody calls us about a healthy system. By the time we are opening the code, something has already gone wrong, so the ones we see are the worse ones by definition.

Across the work it comes out close to an even split between hardening and rebuilding, with hardening slightly ahead. But that split is nearly the least useful thing we can tell you, because the real answer depends on the system in front of us. Here is what actually decides it.

How big and how critical it is. You cannot rewrite a bank. The more the business depends on it running, the less you touch in one go.
How tangled it is. Well-separated code you can replace a piece at a time. A tangled monolith fights every change, so more of it has to go.
How many users, and how spread out. A handful of users is a low-risk rewrite. Millions of users, across regions, pushes you toward caution and phases.
What has to keep working while you change it. Rules about staying backwards compatible can force you to keep the old thing alive while you build the new one underneath.
Sometimes something as plain as the language. We once held a large app together because it was written in Objective-C and no other firm had, or wanted, a developer who still wrote it. It was too big to rewrite and the client was not ready. So we kept it running, because a rushed rewrite would have been worse than the problem.

For anything large, the real answer is almost never one big leap. It is harden the dangerous parts now, then rebuild in phases over a planned timeline, protecting the running business the whole way. Full ground-up rebuilds are for the small, closed systems, where starting again is genuinely cheaper than untangling.

If a firm opens your system and straight away tells you to throw it all out and start again, be careful. Sometimes that is the honest answer. Often it is just the easy one, and it is not the same thing.

How to read all of this

The defects are the same everywhere. A codebase rots the same way in London, Berlin or San Diego. What changes is the consequence, depending on who the client is and what the system holds.

And the honest frame, which is also the true one. The systems that reach us are the worse ones, because we are the call people make when it has already gone wrong. We are not telling you all software is broken. We are telling you what fourteen years of opening other people's code has actually shown us.

Have us open yours See the Readiness Audit → Back to Purpledecks →