What we find when we open other people's code.
A Purpledecks defect taxonomy. Drawn from fourteen years of takeovers, audits and rescues.
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.
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.
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.
These bands are our judgement, per the note above.
Security and data exposure
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.
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.
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
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.
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.
Fake substance
Something was sold as clever. An algorithm. A model. "AI." Automation. You open it and there is nothing there.
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.
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.
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.
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.
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.
Who built it, and the mess that follows
Not code problems. People problems. And they often decide whether a rescue can work at all.
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.
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.
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.
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.
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.