The 13-layer production check
By Aakshar Garg, Founder, Fixier · Last reviewed
the short answer
Thirteen layers decide whether an AI-built app survives real users, and an audit walks them in stack order: frontend foundations, APIs and backend logic, database and storage, auth and permissions, hosting and deployment, cloud and compute, CI/CD and version control, security and RLS, rate limiting, caching and CDN, load balancing and scaling, error tracking and logs, and availability and recovery. The failures that end companies cluster in four of them: data, auth, security, and anything touching money.
This is the framework a developer walks in every audit, in this order, and it is the same thirteen layers whichever tool built the app. Each layer below is a question you can answer about your own app today, a test you can run without buying anything, and the fix if the answer comes back wrong. The order is the stack rather than a ranking of severity — it starts at the screen your customer touches and ends at whether you could get the data back — so if you have one afternoon rather than one week, layers 2, 3, 4 and 8 are where the expensive answers live.
What skipping each layer actually costs
The reason each layer is on the list: these are failures that end companies or cost real money, rather than the ones that annoy engineers.
| What breaks | What you'd notice |
|---|---|
| Frontend foundations skippedmedium | One component throws and the whole page goes white. The app is not down — it just looks down to the customer, with nothing on screen telling them what to do next. |
| APIs & backend logic skippedcritical | Your server believes whatever it is sent: a price, a quantity, someone else's id. Or a payment provider's retry is treated as a second sale and a card is charged twice. |
| Database & storage skippedcritical | A query returns rows belonging to someone else. This is the one that becomes a notifiable breach and an email you do not want to write. |
| Auth & permissions skippedcritical | A page or an action that was only ever meant for signed-in people turns out to work for anyone who knows the address. |
| Hosting & deployment skippedhigh | A bad deploy reaches customers and there is no way back except fixing forward, live, while it is broken — which takes as long as understanding the bug takes. |
| Cloud & compute skippedhigh | The bill arrives and it scales with requests rather than with revenue. A loose key or an unbounded job turns that into five figures overnight. |
| CI/CD & version control skippedmedium | Nothing checks a change before customers do, and anyone can push straight to what deploys. Breakage ships on a Friday and is found on a Monday. |
| Security & RLS skippedcritical | A key readable in the browser, or an ownership rule that only exists in the interface, lets a stranger read your database directly without touching your app at all. |
| Rate limiting skippedhigh | Someone runs a password list against your login overnight, or calls the endpoint that costs you money per request as fast as your server will answer. |
| Caching & CDN skippedhigh | One customer's page is stored by the CDN and handed to the next person who asks for that address. Their name, their orders, their data. |
| Load balancing & scaling skippedmedium | The app that was instant in testing takes eight seconds on the day you finally get traffic, and the database runs out of connections before the servers run out of capacity. |
| Error tracking & logs skippedmedium | The app has been broken for a segment of users for a week and the first you hear of it is a cancellation. |
| Availability & recovery skippedhigh | A bad migration or a wrong delete, and no verified path back to yesterday. This is the only one on the list that can be unrecoverable. |
Layer 1 — Frontend foundations: does it break in front of the customer?
The front end is the only layer your customer actually sees, so its failures are the ones they describe as "the site is broken" even when everything behind it is fine. One unhandled error in one component can blank an entire page, and an unoptimised image can cost more load time than every query behind it combined.
The test: open your live site on a phone, on a real connection, with the browser console open. Anything red is something a customer is already hitting. Then click through the parts people use most and watch for a screen that goes blank rather than showing an error.
The fix: an error boundary around each route, so a failure degrades to a message instead of a white page; images served through your framework's image component rather than raw; and the bundle split so the first screen does not wait for the whole app to arrive.
Why it is first and not last: not because it is the most dangerous — it is the least — but because you can check it yourself in ten minutes, and how it was handled tells you a great deal about how carefully everything underneath was built.
Layer 2 — APIs & backend logic: does your server trust whatever it's sent?
Every endpoint your app exposes can be called directly, with any values, by anyone who can read the network tab — the form on the screen is a suggestion, not a constraint. Two failures here cost real money: input nobody validated, and events processed twice.
The test: take one endpoint that changes something — a checkout, an update, an invite — and call it with a price, a quantity or an id you could never send from the interface. Then, in your payment provider's dashboard, resend a webhook you have already processed and check whether you now have two of something.
The fix: validate on the server at the edge of every endpoint, against a schema rather than by hand. Verify webhook signatures before acting on the payload. Make writes idempotent — give each attempt an identifier so replaying it is a no-op rather than a second order — and return structured errors instead of stack traces.
What we find: the failure is almost never in taking the payment. It is in what the app records afterwards, which is why it survives testing and surfaces in reconciliation weeks later.
Layer 3 — Database & storage: can a query be made to return someone else's rows?
This is where the data lives, so it is where the worst day starts. Two questions decide it: whether a value from a user can change the shape of a query rather than just its parameters, and whether the database itself knows which rows belong to whom.
The test: two accounts, two browsers. Create a record in the first, note the id in the URL, and try to open, edit and delete it from the second. Test all three — it is common to find reading locked down and deleting wide open.
The fix: parameterised queries everywhere and never string-concatenated SQL, plus the ownership rule in the database rather than in each screen, so it applies to screens that do not exist yet. In Postgres and Supabase that is row-level security; elsewhere it is a scoping clause every query inherits.
Two more things belong here: what your ORM and database driver versions are exposed to, and whether backups are configured at all. Configuring them is this layer; proving one restores is layer 13. This is the layer we are asked about most, and it has its own page — why one user can see another user's data, including what to do in the first hour if it is already happening.
Layer 4 — Auth & permissions: can a stranger reach something they shouldn't?
This layer is about whether the door is locked at all. Every page and every endpoint has to check the session on the server, and "the button is only visible to signed-in users" is not the same thing — the button is not what gets called. The address is.
The test: sign out entirely, then paste the URL of a page that should be private into a fresh browser window. Then do the same for anything your app calls in the background — if you can find an address in your browser's network tab, try it signed out.
The fix: check the session on the server, inside the thing being called, rather than deciding in the interface whether to show a link. Then add the flows that get skipped when an app is built fast: password reset, email verification, a lockout after repeated failed attempts, and a re-authentication step before anything destructive.
What we find: admin screens reachable by URL, and background actions that were never protected because the only way to reach them in the interface was already behind a login.
Layer 5 — Hosting & deployment: if today's deploy is wrong, can you undo it?
Everything ships eventually and some of it will be wrong. This layer decides whether that is a five-minute event or an evening: whether there is somewhere to try a change before customers see it, and a way back that does not involve writing new code under pressure.
The test: say out loud what you would do if the version you deployed an hour ago were corrupting data right now. If the answer starts with finding the bug, you do not have a rollback — you have a fix-forward, and it takes as long as understanding the bug takes.
The fix: a preview or staging environment with its own database and its own environment variables, one-click rollback to the previous deploy, and HTTPS enforced with HSTS so no request ever starts in plaintext. Every serious host gives you the first two — they are usually switched off rather than absent.
The trap: one set of environment variables shared across environments. It works right up until a staging test writes to the production database, or a preview deploy sends real email to real customers.
Layer 6 — Cloud & compute: what's the bill at a hundred times this traffic?
Usage-based pricing is a good deal until something loops. The surprise bill is rarely caused by success — it is an unbounded job, a paid API called inside a render, or a key someone found — and the platform will serve all of it happily and invoice you afterwards.
The test: open your last bill and mark each line as growing with customers or growing with requests. Only the first is paid for by revenue. Then check whether a spend alert exists and at what number — if you cannot say the number, there isn't one.
The fix: budget alerts at a figure that would genuinely worry you, hard caps on anything metered per call (AI and inference spend above all), and a ceiling on any job that runs for as long as its input tells it to.
Worth knowing: platform limits are as important as price. Free and entry tiers cap per-invocation CPU and memory as well as monthly totals, and hitting those looks like a broken app rather than a bill.
Layer 7 — CI/CD & version control: what stops a bad change reaching customers?
This layer is the difference between a mistake being caught by a machine in ninety seconds and by a customer in ninety minutes. It is free on every host worth using, and an app can run for months without it — which is exactly why it is worth checking rather than assuming.
The test: push a change that fails type-checking and open a pull request. If it can be merged, nothing is checking anything. Then check whether anyone can push straight to the branch you deploy from.
The fix: a pipeline that runs lint, type-check, tests and a real build on every push; branch protection on the branch that deploys; and an automated dependency scan that fails the build on a known-vulnerable package rather than emailing someone about it. OSV, Dependabot and Snyk all do the last one.
Also check the versions themselves. Pre-release, canary and release-candidate packages are fine in a prototype and a liability in something customers pay for. Read your lockfile, not your intentions.
Layer 8 — Security & RLS: can one customer see another customer's data?
Layer 4 asks whether someone is signed in; this layer asks what a signed-in person is allowed to open, and what your app ships to the browser that it shouldn't. Broken access control is the top entry in the OWASP Top 10:2025, and it is the failure that produces a breach notification rather than a bug report.
The test: open your deployed site, open developer tools, and search the built bundle for a fragment of each key you use — the built output, not your source code, because prefixes like VITE_ and NEXT_PUBLIC_ mean exactly "put this in the browser". Then look at your response headers for a content security policy and a framing header.
The fix: the ownership rule enforced in the database as well as the app, so a screen nobody has written yet inherits it; secrets moved behind code you control, and rotated — deleting a key from the source does not retract a value that has already shipped. Then a scan of your git history, because a key committed once is in the history forever.
Worth knowing: some keys are designed to be public, and finding one is not automatically a problem. A Supabase publishable key is safe in a browser precisely because the database rules are what protect the data. If those rules are wrong, that same key is the way in.
Layer 9 — Rate limiting: can one person hammer sign-in, or checkout, all night?
Without a limit, one script can try a hundred thousand passwords against your login, or call the endpoint that costs you money as fast as your server will answer. None of that requires a vulnerability or any skill — it is just your app, used at speed.
The test: submit your own login form wrong twenty times in a row, quickly. If the twentieth attempt behaves exactly like the first, there is no limit. Repeat on anything that sends an email, creates an account, or costs you money per call.
The fix: a limit per IP and per account on sign-in and sign-up, a limit on payment and checkout routes, and a default limit on every endpoint that writes. Most hosts and edge platforms provide this as configuration rather than code.
The trap: limiting the page rather than the endpoint. A login screen that is slow to resubmit does nothing if the endpoint behind it will answer a thousand times a minute.
Layer 10 — Caching & CDN: could one customer be served a page cached for another?
Caching is how a fast app stays fast, and it is also the quietest way to leak data: a response containing one person's details, stored by a shared cache, handed to the next person who asks for that address. The cache is doing exactly what it was told, which is what makes it easy to miss.
The test: sign in, load a page carrying your own data, and read its Cache-Control header. If an authenticated response is storable by a shared cache, that is the bug. no-store means no cache of any kind keeps it; private means only the visitor's own browser may.
The fix: no-store on anything authenticated, cache keys that include who the response is for, and a deliberate written decision about what is static, what revalidates and how often.
Worth checking: not only pages. An API response, a generated image or a server-rendered fragment is cached by the same rules, and none of them look like a page to whoever set them.
Layer 11 — Load balancing & scaling: what happens when everyone arrives at once?
Most early scaling failures are database shape rather than server size. A page that runs one query per row instead of one query total is instant with ten rows and unusable at ten thousand — and an app that scales to many short-lived connections exhausts the database long before it exhausts the platform.
The test: find your slowest page and count how many database calls one load makes. Then check whether the columns you filter and sort by are indexed. Then ask whether anyone has ever put a hundred simultaneous users through it — if not, what you believe about capacity is a guess.
The fix: fetch related records in one query rather than per row, add the missing index, put a pooler in front of the database if your platform opens a connection per request, and run a load test once so the number stops being a guess.
This is the cheapest layer to audit and often the most satisfying, because the fix is frequently caching or indexing something that never needed to be recalculated at all.
Layer 12 — Error tracking & logs: would you know it broke before a customer told you?
This is the layer that decides how long every other failure lasts. An app without error tracking is not more reliable than one with it — it is equally broken and silent about it, and the silence is what turns a two-hour incident into a two-week one.
The test: deliberately break something small in production — a bad value in a form nobody uses — and see whether anything tells you. If nothing arrives, you are relying on customers as your monitoring, and most of them will churn rather than write in.
The fix: error tracking that captures failures in your users' browsers as well as on your server, structured logs with levels so they can be searched rather than read, and an alert on the two or three paths that matter — signup, checkout, and whatever your app is actually for.
The trap: alerting on everything is the same as alerting on nothing. Alerts that fire constantly get muted within a fortnight, and then you are back where you started while believing you are covered.
Layer 13 — Availability & recovery: if the data went wrong today, could you get it back?
Every other layer on this list describes a bad day. This one decides whether the bad day is permanent. A backup that has never been restored is not a backup — it is a file whose usefulness is untested, and the moment you need it is the worst possible moment to find out.
The test: restore yesterday's backup into a scratch environment and open the app against it. Time it. That number is your actual recovery time, and it is usually the first time anyone has measured it.
The fix: a health endpoint, an uptime monitor that checks it from outside your own infrastructure, migrations that can be reversed, a deploy you can roll back without a rebuild, and a restore you have personally completed at least once.
What we find: backups switched on and never exercised, and no answer at all to "what would you do if a bad migration deleted a column of customer data an hour ago?"
Who runs the audit — a person or a tool?
An experienced software developer runs it end to end and is accountable for every finding. AI is used inside the audit to cover ground fast — reading a whole repository, cross-referencing package versions against known CVEs, drafting the layer-by-layer sweep — and nothing reaches your report that the developer has not confirmed against your actual code. The tests are run rather than inferred: the app is penetration-tested, stress-tested, and put under simulated traffic at the volume you expect.
What AI is genuinely good at here: breadth. Every file rather than a sample of them, a dependency tree checked version by version against published vulnerabilities, thirteen layers held in mind at once without the tenth getting less attention than the first. Coverage is the thing that suffers first when a person alone runs out of hours, and it is the thing a founder can least afford to lose.
What it cannot do: know what your app is supposed to allow. A tool can confirm that a row-level security policy exists. It cannot know that orders should be visible to the customer who placed them and the two admins on your team, because that is a fact about your business rather than your schema — and getting it wrong is the failure that ends companies. That judgement is the audit; the rest is groundwork for it.
What the tests are: penetration testing against the auth, access and injection paths; a load test at the traffic you expect rather than the traffic you have today; and a stress test past that point, so you know where it breaks instead of assuming it doesn't. Active testing runs only against an environment you have authorised in writing.
And then the fixes, if you want them. Our specialists implement them in your repo, and migrate the app to a platform that can carry the load where the hosting rather than the code is the ceiling. Re-audited at the end, so every fix is verified rather than assumed — and the codebase stays yours either way.
Do I have to do all thirteen before launching?
No, and treating it as a gate is how people never launch. Four of them decide whether launching is safe at all — layers 2, 3, 4 and 8, where money moves and data is exposed — so those are worth being confident about before real customers arrive. The other nine are what stop a small problem becoming a long one, and they can follow.
If you only have an afternoon, spend it on the two-account test from layer 3 and a search of your deployed bundle for keys from layer 8. Both take minutes, both are checkable without changing any code, and both are far more expensive to discover after launch than before it.
What happened to the seven-layer check?
It became this. The seven layers — access, tenancy, secrets, money paths, load and cost, observability, recovery — were the founder-facing subset of the same audit. The thirteen here are the layers the audit walks and the report is organised by, so what you read before buying is what you get back afterwards. Nothing was dropped.
Where each one went: access is layer 4; tenancy is layers 3 and 8; secrets is layer 8; money paths is layer 2, with the limits on payment routes in layer 9; load and cost split into layers 6 and 11; observability is layer 12; recovery is layer 13. The old address redirects here, and any link to it still works.
Why thirteen layers and not the OWASP Top 10?
They answer different questions. The OWASP Top 10 is the reference standard for classes of web application vulnerability and we use it — broken access control is its top entry for 2025, which is layers 4 and 8 here. What it is not is a launch checklist for a founder: it does not cover whether your bill scales, whether a payment can be taken twice, or whether you could restore yesterday's data.
So the thirteen layers are deliberately broader than security, and deliberately shaped like a stack rather than a threat list. They are the questions that decide whether an app survives its first real users, walked in the order the app is actually built in — and each one is phrased so that a non-technical founder can answer it about their own app without a translator.
Sources
Every claim above about a third-party tool was checked against that tool's own documentation on August 17, 2026. These products change fast — if you spot something out of date, tell us.
Related guides
Want someone to actually check?
We audit AI-built apps against all thirteen layers and hand back a report in plain English — with the technical detail underneath for whoever fixes it. Startup-friendly pricing, scoped to your project.
Book an Audit