We Inherited an AI-Generated Codebase. Here Is What We Found

Underneath, almost nothing that holds a multi-tenant product together was in place. Authorization was broken. Organizations could see each other's data. Uploads and transcription, the core of the product, didn't work reliably. There was no deploy process. Secrets were hardcoded. There were no tests.
None of this came from bad engineering in any single file. The code an AI agent writes is usually plausible on its own. What breaks is everything between the pieces. Here's what we found when we took over Oria, why AI-built codebases fail in these exact places, and six checks you can run on yours this week.
The product
Oria is an oral history platform. Josiah Faison started it after losing his grandmother before her stories could be recorded. Families, libraries, museums and public agencies use it to capture life stories while the people who lived them can still tell them.
An institution opens a campaign and shares a link or a QR code. Community members answer guided prompts in the iOS and Android app, in text, audio, photos or video. Every story comes back transcribed and searchable, and it belongs to the institution.
The institution platform was built fast, with AI coding agents doing most of the implementation. That's a reasonable way to find out whether institutions want the product, and they did. It's a poor way to run a system that holds other people's family stories across a dozen organizations.
1. Authorization was broken
Logging in worked. Deciding what a logged-in user was allowed to do didn't.
Generated code practically ships with this one. Each feature gets generated in its own session, and each session makes its own assumption about who the user is and what they may touch. One screen checks a role, the next checks nothing, a third checks only on the client. Every change looks fine in review, because each one works for the person testing it.
The fix is deliberately boring: one authorization layer on the server that every route goes through, with the rules in one place. Client-side checks are for display. They never grant access.
2. Organizations could see each other's data
For a platform that holds family stories and consent records, this was the finding that mattered most.
Multi-tenancy is a property of the whole system, not of a feature. When the organization filter has to be added to each query by hand, the agent adds it where the prompt mentioned it and forgets it where the prompt didn't. One missed query is enough.
So tenant scoping can't depend on anyone remembering. It has to be enforced below the application code, in a data-access layer or with database row-level security, so a query without a tenant can't run at all. Then every build gets a test that logs in as organization A and tries to read organization B's stories.
3. Uploads and transcription were broken
Audio and video in, transcripts out: that's the product. And it failed where real users live, which is long recordings on phone connections.
Generated code handles the happy path it was shown. A small file, a fast network, a transcription service that answers on the first try. Large files, dropped connections, retries and jobs that take minutes all need a design, and nobody had asked for one.
What holds up is uploads that go straight to storage and survive a dropped connection, transcription as a background job with retries, and a visible status for every story. A storyteller should never have to wonder whether their grandmother's interview was saved.
Run the six checks on your codebase
Tell ChatGPT which tools built your product and on which stack. It will map each finding to where it usually hides in that stack, with a quick check for each.
“|”
4. There was no deploy process
Code reached production by hand, and nothing described how to do it again.
That's not surprising. Agents write application code. The path from a commit to a versioned release you can roll back is infrastructure work that sits outside the prompt, so it rarely exists unless someone builds it on purpose.
It needs a pipeline that builds, tests and deploys every change the same way, separate staging and production, migrations that run as part of the deploy, and a one-step rollback.
5. Secrets were hardcoded
Pasting the key right where the code needs it is the fastest way to make a generated integration work. The key then lives in the repository history, even after someone moves it into an environment variable.
Our rule: every key that was ever in the repository is leaked. Rotate all of them first. Then move them into a secrets manager and scan the history, so the old values stop mattering.
6. There were no tests
Agents write tests when you ask them to. The catch is that tests generated alongside the code tend to confirm what the code does, not what it should do.
Start where a failure costs trust: login, tenant isolation, upload to transcript. A few dozen tests there are worth more than hundreds of generated ones.
The pattern behind all six
None of these is a bad function. Every finding lives between features: who the user is, which organization they belong to, where a file goes after upload, how code reaches production. An AI agent sees the file it's editing. The system is what nobody was looking at.
That's also why these codebases pass a demo. A demo exercises one path, by one user, in one organization, on a good connection. Production is every path at once.
Built most of your product with AI agents?
Rescue audit and plan from $500: a written audit in 48 hours covering the six places above, then a flat-price plan for what to keep, fix or replace. Send your email and we'll set up repository access within 24 hours.
Keep, fix or replace
Throwing the product away is almost never the answer. Twelve institutions had already validated what Oria does. The job is to sort the codebase into three piles:
- Keep: what works and is safe, usually the screens, the flows users know and the product decisions behind them.
- Fix: what works but is unsafe or fragile, such as hardcoded keys or a missing check on an otherwise sound route.
- Replace: what is wrong at the foundation.
At Oria, the foundation went in the replace column: authorization, tenant isolation, the upload and transcription pipeline, and the deploy path. We rebuilt those so the product on top could keep doing what institutions were paying for.
Where Oria is now
Oria runs with 12 partner institutions, from local libraries to the Maryland-National Capital Park and Planning Commission, and has been covered by Black Enterprise and AfroTech. Every new institution now lands on a system where its stories stay its own.
The six-question check
Two or more "no" or "not sure" answers mean you're standing on the same foundation Oria had.
- Has someone tested whether a user in one organization can read or change another organization's data?
- Is every route checked by one server-side authorization layer?
- What happens to a long video upload on a bad phone connection, and to a background job that fails halfway?
- Can you deploy the current version from scratch, and roll it back, without someone's laptop?
- Is the repository history free of API keys and passwords?
- Is there at least one automated test for login, tenant isolation and your core flow?
Related reading
- How we use Claude Code in production: how we build with AI agents without handing them the architecture
- Getting your code back: what to demand before you walk away: the access you need before anyone can take over a codebase
- Your AI prototype works. Here is why it will fail in production: the same gaps, for AI features
Built with a specific tool? Cursor, Claude Code, Lovable, Bolt, Replit Agent. Weighing a rebuild: agency transfer vs rebuild. The service: Vibe Code Rescue.
Enjoyed this article? Share it with others
Related Posts

One Profile, Two Products: What an Identity Layer Has to Survive
SportsID gives an athlete one permanent record that travels from camp to college. CoachID hires coaches off the same idea. Two products, one identity underneath, and that underneath part is where the engineering actually lives.

Why We Moved Idlecorp Off WordPress and Onto Saleor
WooCommerce got Idlecorp's store live in a weekend. It couldn't get them past a plugin-stacked checkout and recommendations that didn't actually personalize anything. Here's why we moved the whole platform to Saleor.

We Finished Ryan Johns's Product When He Couldn't Pay for It Yet
Midway through building Sales Leverage, the founder's personal situation made it impossible to keep funding the build on schedule. We shipped it anyway. He paid in full once he could.