· M Hammad Hassan

Six Things to Try on Your AI-Built App Before a Customer Does

AI builds the path you asked for. Customers find the paths you didn't. What we found auditing a Lovable-built app that passed every demo, and six checks you can run on yours today.

AI-Generated CodeLovableSupabaseProduction ReadinessSoftware AuditSecuritySaaSTechnical Debt
Six Things to Try on Your AI-Built App Before a Customer Does

You built it in weeks, not months. It has a login screen, dashboards, PDF exports, email notifications and a settings page with more toggles than you'll ever use. You've shown it to investors, to friends, to your first prospects. Every time, it works.

That's exactly the problem.

A demo is a performance with a script. You know which buttons to press, in which order, with which data. The app has been built, prompt by prompt, to get that script right. Your customers don't have the script. They'll press the buttons in a different order, leave a form half-finished, lose Wi-Fi, invite the wrong person, and type numbers the way people in their country type numbers. Then they'll wait a week for the report that was supposed to arrive every Monday.

AI is exceptionally good at producing the path you asked for. Customers are exceptionally good at finding the paths you didn't.

We recently audited an app that passed every demo it was ever given. This post walks through what we found underneath it, and then gives you six things you can try on your own app today to see whether yours has the same problems. No code reading required.

The app that passed every demo

The founder had built a B2B platform for dairy processors in Brazil using Lovable, with Supabase as the backend. It took about ten weeks. By the end, it had seven modules, five languages, user management, dashboards, PDF and Excel exports, scheduled email reports, push notifications and an integration with the customers' ERP system.

The customers weren't casual users. Dairy processors in Brazil operate under federal inspection (SIF) and go through food-safety audits like FSSC 22000 and ISO. They keep official records of production, losses, procedures and maintenance, and they can get into real trouble if those records are wrong. For them, the software isn't a convenience. It's evidence.

The founder's plan was the one we hear most often:

"It already works. Let's fix the bugs, tighten security and launch. We'll build a proper version 2.0 once revenue comes in."

Before agreeing, we read the whole thing. All of it: about 93,500 lines of code and 144 database migrations, line by line, with every finding traced to a specific file. Nothing was guessed and nothing was attacked in the live system.

We found 207 problems. That number sounds dramatic, but it isn't the important one. The important number is 5: five problems in how the system was built, which caused a large share of the other 202. You can't fix those with small changes.

What was underneath

The rules lived on the screen

In a well-built product, the system enforces the rules. If a regular user isn't allowed to approve a document, the server refuses, no matter how the request arrives. The screen just offers a friendly way to ask.

In this app, the rules lived almost entirely on the screen. The code checked the user's role and hid the "Approve" button. The database only checked one thing: is this person a member of this company? So any employee of a customer, including one with view-only access, could publish an official procedure, approve documents, close work orders or lock a finished production day. All they had to do was skip the screen.

It wasn't one shortcut in one place, either. We counted 192 places across 42 files where the browser wrote directly into the database, with no server in between checking whether the action was allowed or the data made sense. Every validation rule — "this must be positive", "this field is required", "this value must be in range" — existed only in code that runs on the user's own device.

A view-only user could even replace the file behind an approved official document. And a document could carry hidden code that ran in the browser of whoever opened it next.

Think of a shop with a locked glass cabinet and the front door wide open. In a demo, nobody tries the front door.

Any customer could make themselves the landlord

The platform let anyone sign up and create a company account. That's normal. What wasn't normal was what that account's admin could do next:

  • Turn on every paid module for free.
  • Lift a suspension the platform had placed on their own company.
  • Put their own name and logo on the platform's main login page, the one every other customer sees.
  • Make themselves the company owner, or demote the real one.

New sign-ups also started as active accounts, not trials. So the pricing model was more of a suggestion.

On top of that, a secret key for the notification system was written in plain text inside the code. And the link that triggered report emails for every company on the platform was protected by a key that is public by design: every visitor's browser already has it. In practice, anyone on the internet could make the system send those emails.

The walls between customers had doors in them

In a product that sells to many companies, one rule matters more than any other: one customer can never touch another customer's people or data.

Here, a company could add a user who belonged to a different company, without that person agreeing, and then see their name, phone number and email.

The platform's own support staff could see and change every customer's data without asking. There was a customer-approval step for support access, and it looked great in the UI. But support staff could already reach the data without it, so it protected nothing. If a customer's auditor asked who had access to their records, the honest answer would have been "we can't tell you."

Records that should have been final weren't

For audited customers, "signed", "published" and "closed" are promises that a record is final. That's usually the whole reason they buy software instead of using spreadsheets.

  • A signed field report could be switched back to draft, edited and signed again, with no record that it happened.
  • A published document version could be unlocked and rewritten.
  • Changing a report template changed old signed reports, because old reports were displayed using the current template.
  • A "signature" was any typed text of three or more letters. It wasn't tied to the person and didn't ask them to confirm who they were.
  • The audit trail was written by the screen after the action. If writing it failed, the error was ignored and the action still counted. And any company member could insert a history entry directly, with any name and any date.

An audit trail with gaps is incomplete. An audit trail that anyone can write, in anyone's name, backdated, is worse than having none, because it looks trustworthy.

The number customers were paying for was wrong

The product's main module calculated production losses: how much milk went in, how much product came out, and where material was lost along the way. The rules for these calculations had been written down and confirmed with the founder. The code didn't follow them.

  • The loss was stored with the wrong sign, and the dashboard read it assuming the opposite. So real losses showed as 0 kg, and gains showed up as losses.
  • The loss percentage used the wrong base, so a 3% loss displayed as 2%.
  • The dashboard never found the confirmed tolerance because the code used a Portuguese word in one place and an English word in another. It silently fell back to default thresholds nobody had agreed to.
  • Litres and kilograms were mixed: dropped from some totals, added together in others.
  • The official end-of-day closing record added percentages together, so 3.5% and 3.6% became "7.1%".
  • A closed, official day could still be changed.

Then there were the tests. The app had some, and some of them checked the wrong loss sign as if it were correct. They passed because they tested the bug.

The number problems weren't limited to one module:

  • The app was built for Brazil, but money fields didn't accept the Brazilian decimal comma. Typing "250000,00" into a budget field erased the planned value.
  • "Actual capex" was never saved, so the over-budget warning could never fire, and the project dossier always printed "actual: R$0".

None of this crashed, and nothing showed an error. Every one of these numbers looked authoritative. For a product whose customers make decisions (and pass audits) based on those numbers, that's worse than a crash.

The app didn't tell the truth about itself

This category is the one that should worry non-technical founders most.

  • In many places, the app showed "saved" when saving had failed. Actions that saved in several separate steps could leave half-saved data behind.
  • Scheduled report emails always failed. The schedule was saved without the information the sending system needed, so every run failed silently while the screen showed the schedule as active.
  • Preventive maintenance plans based on machine hours never created work orders. The screen saved the plan, but the machine ran past every service interval and nothing happened. That's the feature customers buy to avoid breakdowns.
  • Monthly reports set for the 29th, 30th or 31st never ran in shorter months.
  • A double click took spare parts out of stock twice.
  • A reopened task kept its 100% progress, so projects marked themselves "concluded" on their own.
  • Starting a new revision of a procedure took the approved version off the shop floor. Workers saw the unapproved draft instead.
  • And the opposite failure: an operator couldn't sign their own report. The people the module was built for got an error.

None of these show up in a demo, because nobody waits a week to see whether the preventive work order appears.

Six things to try on your app

You don't need to read code to find out whether your app has these problems. You need to stop behaving like the person who built it. Each check below takes minutes to an afternoon.

1. Do it the wrong way

Try this: fill in half a form and submit it. Paste nonsense into a field. Upload the wrong file type. Hit the back button in the middle of a multi-step flow. Double-click Save, Submit or Pay. Turn off Wi-Fi right after pressing Save, then turn it back on.

What you're looking for: whether the app tells the truth. A good app fails loudly and leaves your data consistent. A fragile one says "Saved!", creates the record twice, or leaves half of it behind.

In the audit: "saved" messages when saving had failed, half-saved records, and stock taken out twice on a double click.

2. Do two things at once

Try this: open the same record in two tabs, or log in as two different users in two browsers. Edit the same thing in both. Delete something in one tab while it's open in the other. Remove a user's permission in one session while they're still logged in on the other.

What you're looking for: duplicate records, changes that silently overwrite each other, and people who keep access after you took it away. Demos have one user doing one thing at a time. Your customers won't.

In the audit: duplicate records from repeated actions, and multi-step saves that could leave data in a half-finished state when something else happened in between.

3. Try to break the permissions

Try this: create the least-privileged user your app has, and try to do everything a manager can. Don't just check whether the button is hidden: try old links, bookmarked pages and URLs copied from an admin's session. Then sign up as a brand-new customer and see what you can reach that you'd normally charge for. Then create a second test customer and try to reach anything that belongs to the first. Finally, sign, approve or close a record, and try to change it.

What you're looking for: whether the rules are enforced by the system or only by the screen. If the only thing stopping a user is a hidden button, the rule doesn't really exist.

In the audit: rules checked only on screen, 192 unchecked writes from the browser, company admins unlocking paid modules and making themselves owners, one customer reaching another's users, support access that skipped customer approval, and signed records that could go back to draft.

4. Give it something real

Try this: stop testing with "John Test, Test Company, $100". Import real volumes: thousands of records, not ten. Use names with apostrophes and accents. Type numbers the way your customers type them (with decimal commas, thousands separators and their units). Set the device to a time zone you didn't build in. Then search, filter, sort, export and run reports on all of it.

What you're looking for: the point where the prototype's assumptions meet reality. AI builders tend to assume the formats and data you used while prompting.

In the audit: a Brazilian-format budget number that erased itself, litres and kilograms added together, and reports that only broke on certain days of the month.

5. Check your core number by hand

Try this: pick the one number your customers are paying for. It might be a loss percentage, an invoice total, a commission, a forecast or a score. Take a real example and work it out in a spreadsheet, step by step. Then compare it with what the app shows. Repeat with an awkward case, like a cancelled item, a refund, a partial period or a zero.

What you're looking for: whether the app actually does the maths you think it does. "It shows a number" and "it shows the right number" are different claims, and a demo only proves the first one.

In the audit: real losses showing as 0 kg, a 3% loss shown as 2%, percentages added together in an official record, a dossier that always printed R$0, and tests that confirmed the wrong answer.

6. Wait for what happens later

Try this: set up everything your app promises to do on its own: scheduled reports, reminders, notifications, renewals, recurring tasks, automated follow-ups. Include one scheduled for the end of the month. Then check the next day, and next week, whether each one actually happened, at the right time, for the right people.

What you're looking for: proof that the thing happens, not proof that it can be configured. A settings screen that saves a schedule tells you nothing about whether anything reads that schedule.

In the audit: report emails that always failed while showing as active, preventive maintenance that never created a single work order, and monthly reports that skipped every month shorter than 31 days.

If one of them failed

If any of these checks failed, you haven't found a bug. You've found a symptom. What you can't see from the outside is how many other places the same cause is sitting.

The natural next move is to open your AI builder and describe what you saw: "a normal user can approve documents." It'll fix it, and it'll tell you confidently that it's fixed. Very often it'll hide the button a little better, and the system underneath will accept the action exactly as before. The screen is now right, and the problem is still there.

Before you trust that, ask it where the rules actually live: "Where is this permission enforced? Show me the exact file. Where are the tests for sign-up and invitations? Are there any secret keys in the code?" Then look at what comes back.

The app we audited had been through that loop many times. Its 144 database migrations were written in ten weeks, and 74 of them replaced rules set by earlier ones. Each change made sense on its own. Together, they made it nearly impossible for anyone, human or AI, to say what the rules actually were. There were 33 test files for 93,500 lines of code, none of them covering sign-in, invitations, notifications, background jobs or the ERP integration, and some of the ones that existed tested the bug. Every fix was a new layer on top of the last one, with nothing underneath to catch it when something broke.

What happened next

The founder didn't launch. We took on the rebuild ourselves.

The product needs multi-step approval workflows, versioned signed records, mass-balance calculations and scheduled jobs that all have to agree with each other. That logic belongs in an application backend. Lovable's default setup gives you a Supabase database and the browser, with no real layer in between, so the logic ended up in the one place users can bypass: the screen. That's why we moved it into a dedicated backend (NestJS, Prisma, PostgreSQL, BullMQ and Redis) with a Next.js frontend. The prototype wasn't thrown away. It became the most detailed specification the project could have had: proof of what customers want, the screens they like and the workflows they need.

Before your customer does

None of this means AI app builders are bad. They got this founder from an idea to a product customers wanted to see in ten weeks, and that's valuable. But getting to a working demo and building software that survives real customers are two different engineering problems. Speed solved the first one. It didn't touch the second.

Your customers will run these six checks eventually, without meaning to, on a bad Wi-Fi connection, on the 31st of the month, or in front of their auditor. It's much better if you find out first.

Did one of the six checks fail?

Then you've found a symptom, not the bug. Before you ask your AI builder to fix it one more time, find out where your app's rules actually live, and how many other places the same cause is sitting.

Book a free intro call. Tell us what you built and who it's for, and we'll tell you honestly whether it needs an audit.

contact@graylining.com