# Six things I check before trusting an AI-built app in production

<p class="meta-line">Mik Bry · 2026-07-22 · ~10 min read · AI · Security</p>

<div class="article-hero">

![A founder's hand hovers over a glowing deploy button; six warning triangles sit dimly in the background.](/assets/2026-07-22-six-red-flags-hero.png)

</div>

Your app works. Pages load, sign-up works, the payment screen appears, and the demo looks convincing.

That is useful. It is not the same as being ready for production.

The questions change as soon as real users, real money and real data enter the picture. I stop asking only whether the normal path works. I start asking what happens when someone changes a number in a URL, a payment fails halfway through, a script creates hundreds of accounts, a private key becomes visible to visitors, or the interface says “success” while nothing actually happened behind it.

AI did not invent these problems. I made some of the same mistakes years before AI wrote code for me. What AI changes is the speed: you can now reach a convincing product much faster, including the parts nobody has really challenged yet.

These are six things I check early.

<div class="article-toc">
<div class="article-toc-label">The six checks</div>

1. [Payments work — but only on the expected path](#s1)
2. [The server trusts the browser](#s2)
3. [Nothing slows abuse down](#s3)
4. [Secrets leak into places they should not](#s4)
5. [The database may not protect one customer from another](#s5)
6. [Failure looks like success](#s6)

</div>

<h2 id="s1"><span class="section-no">01 —</span> Payments work — but only on the expected path</h2>

A checkout can look finished while the payment system is not.

The obvious checks are still worth doing: are you using the payment provider's test setup or the real one? Does the provider's confirmation come back to your app? Does your app record the final result correctly?

But I do not stop at the Stripe screen.

I also look for every other way the app can mark an account as paid, create or upgrade a subscription, unlock access, or change someone's billing status. A clean Stripe integration does not help if another hidden route in the app can produce the same result without the same checks.

<div class="check-callout">
<div class="check-callout-label">A safe first check</div>

Open your payment provider dashboard and confirm whether the environment is test or live. In test mode, use the provider's published test cards and verify that the application records both successful and failed payments correctly. If you are already live, plan a small refundable transaction rather than experimenting with a real customer charge.

Passing this check does not prove the payment system is safe. It tells you whether the most obvious path is wired correctly.

</div>

<p class="refs"><strong>Reference:</strong> <a href="https://docs.stripe.com/get-started/checklist/go-live?locale=en-GB">Stripe — Go-live checklist</a> covers test/live configuration, production webhooks, duplicate or delayed events, API keys and edge cases.</p>

<h2 id="s2"><span class="section-no">02 —</span> The server trusts the browser</h2>

Your interface knows which invoice, project or account you are looking at. That does not mean the server has checked whether you are allowed to see it.

The classic version is simple: you open `/invoice/42`, change `42` to `41`, and the application returns someone else's invoice.

Your app should never assume that something is trustworthy just because it came from the visitor's browser. Visitors can change what their browser sends.

<div class="check-callout">
<div class="check-callout-label">A safe first check</div>

Create two test accounts that you control. Log in as one of them and try to open something belonging to the other by changing the number or code in the URL. A safe application refuses. If it returns the other account's data, stop there. You have already found the problem.

</div>

<p class="refs"><strong>Reference:</strong> <a href="https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html">OWASP — Insecure Direct Object Reference Prevention</a> recommends this same type of check: use two accounts and verify that one cannot access objects belonging to the other by changing an object reference.</p>

<h2 id="s3"><span class="section-no">03 —</span> Nothing slows abuse down</h2>

A form that works once can sometimes be called ten thousand times.

Sign-up is the obvious example, but the same problem applies to password reset, invitations, file uploads, sending emails, one-time login codes, and features connected to an AI provider.

The AI case deserves special attention. If your app sends requests to OpenAI, Anthropic, Mistral or another provider using your account, an attacker may be able to use your app as a relay and trigger paid requests at your expense. A badly protected AI feature can therefore turn abuse into a very real bill.

Without checks that slow repeated or suspicious use, a feature that looks harmless during a demo can become expensive very quickly.

<div class="check-callout">
<div class="check-callout-label">A safe first check</div>

Use only accounts and addresses you control and repeat the same action several times. Does anything slow you down, ask for confirmation or reject obvious repetition? If nothing ever pushes back, that is worth reviewing — especially on features that cost you money every time they are used.

</div>

<p class="refs"><strong>Reference:</strong> <a href="https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/">OWASP API Security — Unrestricted Resource Consumption</a> recommends rate limiting and provider spending limits because repeated API calls can consume paid resources and create business impact.</p>

<h2 id="s4"><span class="section-no">04 —</span> Secrets leak into places they should not</h2>

Private access keys copied into files that were accidentally shared with the source code. Database passwords showing up in technical logs. A live payment key sent to every visitor's browser. These mistakes are still common because the app can appear to work perfectly while they remain invisible.

AI tools can connect services very quickly. That does not mean they always put the private keys in the right place.

<div class="check-callout">
<div class="check-callout-label">A safe first check</div>

Ask where the private keys used for payments, the database, email and AI services are stored, and whether any of them are sent to a visitor's browser or included in code that other people can see.

You can also look at what your browser receives for obvious leaks, or ask a technical person to do it. Finding nothing does **not** prove your secrets are safe; this only catches the easiest mistakes.

</div>

<p class="refs"><strong>References:</strong> <a href="https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html">OWASP — Secrets Management</a> explains why API keys and database credentials should not be hard-coded or scattered through configuration files. <a href="https://docs.github.com/en/code-security/concepts/secret-security/secret-scanning">GitHub — Secret scanning</a> documents the risk of exposed API keys, passwords and tokens in repositories.</p>

<h2 id="s5"><span class="section-no">05 —</span> The database may not protect one customer from another</h2>

A polished interface can hide a serious problem: the screen only shows your own data, but the system behind it may still allow access to someone else's.

Services such as Supabase can enforce rules directly where the data is stored: for example, “this customer can only read their own records.” Other systems enforce the same rule elsewhere.

The technology is not the important part. What matters is that the protection exists somewhere the visitor cannot simply bypass.

<div class="check-callout">
<div class="check-callout-label">A safe first check</div>

Open the security section of your database or hosting service and look for warnings about customer-data access. If you use Supabase, its Security Advisor and row-level security checks are good places to start. Then test with two accounts you control: one account should never be able to read the other's data.

</div>

> **My tip:** tools such as Supabase are very convenient when you want to build quickly. They give you a database, login system, file storage and other services in one place.
>
> For a product that I expect to grow, I usually prefer to keep the database more independent from the rest of the stack. In practice, that means using a managed PostgreSQL service such as Neon or AWS and letting the application talk to it through its own server.
>
> Why? Because if the product succeeds, you may want to change how login works, move part of the application, add another service, or simply control database costs separately. Keeping the database independent gives you more freedom later.
>
> This does **not** mean Supabase is a bad choice or always more expensive. It is often an excellent way to build an MVP. My point is simply that convenience at the beginning can become dependence later, so I prefer to make that choice consciously.

<p class="refs"><strong>References:</strong> <a href="https://supabase.com/docs/guides/database/postgres/row-level-security">Supabase — Row Level Security</a> says RLS is enabled by default for tables created with the Dashboard Table Editor, while tables created with raw SQL or the SQL Editor need RLS to be enabled explicitly. Supabase also says RLS should be enabled on tables in exposed schemas. Its <a href="https://supabase.com/docs/guides/deployment/going-into-prod">Production Checklist</a> recommends reviewing Security Advisor findings and enabling RLS with appropriate policies before production.</p>

<h2 id="s6"><span class="section-no">06 —</span> Failure looks like success</h2>

This one is easy to miss because the interface can look perfect.

The payment screen says success, but nothing was recorded. The contact form turns green, but the message was never sent. The upload finishes, but the file is missing. The screen has finished its animation while the real operation behind it failed.

In AI-built applications I review, failures are often tested much less than the normal, successful path.

<div class="check-callout">
<div class="check-callout-label">A safe first check</div>

Use harmless invalid inputs and situations designed to fail. Submit an empty upload. Enter a negative value where only positive values make sense. Try a payment test that is supposed to be rejected. The app should refuse clearly, without leaving half-finished data behind.

A green message is not evidence that the operation succeeded.

</div>

<p class="refs"><strong>References:</strong> <a href="https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html">OWASP — Input Validation</a> recommends validating both the format and the business meaning of incoming values. <a href="https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html">OWASP — Error Handling</a> recommends explicitly handling failure cases rather than leaving the application in an unpredictable state.</p>

<aside class="lr-callout">
  <div class="lr-callout-icon" aria-hidden="true"></div>
  <div class="lr-callout-body">
    <p class="lr-callout-eyebrow">Automated first read</p>
    <p class="lr-callout-text">Before requesting a CTO review, you can start with the <a href="https://mikbry.com/launch-readiness/">Launch Readiness Scan</a>. It gives you an automated first read, but it doesn't see your production environment or business context.</p>
  </div>
</aside>

## Before you launch

Ask six simple questions:

- **Payments:** has the real payment state been verified, including failure cases and adjacent paths that grant paid access?
- **Access:** can one test account open data belonging to another?
- **Abuse:** what stops an automated script from repeating an expensive or sensitive action — especially one that calls an AI service you pay for?
- **Secrets:** do you know where your production keys are stored and who or what can read them?
- **Data:** is there a rule, outside the visitor's control, that keeps one customer's data separate from another's?
- **Failures:** when an operation fails, does the product actually fail safely instead of only changing the UI?

If one answer is “I don't know,” you have found something to investigate. You have not necessarily found a catastrophe.

That distinction matters.

<section class="cta-offers">
  <div class="cta-offers-head">
    <span class="eyebrow">If you want to go further</span>
    <p class="cta-offers-lead">I review AI-built and vibe-coded applications before they go into production.</p>
  </div>

  <div class="price-card price-card--featured price-card--solo">
    <span class="price-pill">Start here</span>
    <div class="price-card-body">
      <div class="price-name-row">
        <span class="price-icon" aria-hidden="true"></span>
        <span class="price-name">Trust Report</span>
      </div>
      <p class="price-desc">An automated review of your app and its code, using my analysis tools. You get the main risks, what deserves attention before launch, and what can wait. If you want to discuss the findings, a review with me is available as an option.</p>
    </div>
    <div class="price-card-right">
      <div class="price-row">
        <span class="price-amt">€349</span>
        <span class="price-note">one-time</span>
      </div>
      <a class="cta-btn cta-btn-accent" href="/launch-readiness/">Get yours</a>
    </div>
  </div>

  <div class="audit-strip">
    <div class="audit-icon-tile" aria-hidden="true"></div>
    <div class="audit-text">
      <div class="audit-label">Audit — need a deeper look?</div>
      <p class="audit-desc">Architecture, payments, access, customer data, deploy-key custody, third-party software + a documented launch recommendation from a human CTO.</p>
    </div>
    <span class="audit-price">from €1,200</span>
    <a class="cta-btn cta-btn-outline" href="mailto:hello@mikbry.com">Talk to Mik</a>
  </div>
</section>

The six checks above are a useful first pass. They can catch obvious problems, but they cannot show you what you do not yet know to test.

If the app is about to handle real users, real money or sensitive data, that is precisely where an experienced technical review becomes useful.
