How to finish an AI-built app that’s stuck at 80%.

The last 20% of an AI-built app is integrations, edge cases and deployment. Take an honest inventory, wire real payments and auth, and get it live.

By UmaizPublished 9 min read

The way to finish an AI-built app is to stop estimating and start inventorying. “80% done” is almost always a measure of the visible parts — the screens exist, the flows demo well. The remaining 20% is integrations, edge cases and deployment, which is where AI tools stop being useful because the answers are specific to you. Write down what’s actually finished, what’s stubbed and what only appears to work, and the real number reveals itself. It’s usually smaller than the dread and larger than the estimate.

Why AI stalls at the end

Cursor, Lovable, Bolt and the rest are extraordinary at the first 80% because it has a clear shape: screens, models, obvious flows, the things every app has. The last 20% is integration with things that are only yours — your Stripe account, your domain, your data, your users’ actual behaviour. There’s no generic right answer for how your refund flow should work, so nothing can be generated confidently. It has to be decided, then built.

Recognising this stops you blaming the tool, or yourself. The stall is structural.

Step 1: The honest inventory

Open the app and go through every screen and every action. For each, mark one of four states:

  • Done — works with real data, handles failure, you’d show it to a customer
  • Demo — works on the happy path with the data you’ve been testing with
  • Stubbed — the button exists, the handler is a console.log or a TODO
  • Missing — it isn’t there
# Stubs the model left behind
grep -rniE "TODO|FIXME|placeholder|not implemented|coming soon" src/ | wc -l

Most “80% done” apps come out around 40% Done, 30% Demo, 20% Stubbed, 10% Missing. That’s not bad news — it’s the first accurate map you’ve had.

Step 2: Decide what version one actually is

Look at the Stubbed and Missing columns and ask, for each: does a paying user need this on day one? Be ruthless. Settings screens, admin dashboards, export features and “nice to have” integrations almost always move to version two.

What’s left is the real scope. Write it down as a list of no more than ten items. If it’s longer, you haven’t been ruthless enough.

Step 3: Turn Demo into Done

The Demo column is where most of the remaining work hides, because each item looks finished. For each one, test the paths the AI never tried:

  • Empty — a new user with no data. Does the dashboard render, or crash on undefined.length?
  • Failure — the API returns 500. Does the user see something, or a spinner forever?
  • Slow — throttle the network. Does a double-click submit twice?
  • Expired — log in, wait for the session to expire, act. Where does the user end up?
  • Wrong — enter nonsense in every field. Is it rejected on the server, not just the form?

Each of these is usually a small fix. Together they’re the difference between a demo and a product.

Step 4: Wire the real integrations

Payments, auth and email are where prototypes are most often fake without looking fake.

Payments. A test-mode key and a redirect back to a success page is not a payment integration. Confirm success from a signed webhook, store the provider’s ID against your order, and handle the refund path — it will be needed.

// A redirect proves nothing. The webhook is the truth.
app.post('/webhooks/stripe', verifyStripeSignature, async (req, res) => {
  if (req.body.type === 'checkout.session.completed') {
    await markOrderPaid(req.body.data.object.id)
  }
  res.sendStatus(200)
})

Auth. Signup works. Test reset, expiry, logout from another device, and what happens when someone signs up twice with the same email.

Email. The sandbox sender that only delivers to your own inbox has to become a verified domain. This is a DNS task, and it takes longer than expected because of propagation.

Step 5: Get it actually live

“Live” means on your domain, reachable by strangers, with the environment configured correctly — not localhost and not a preview URL with a warning banner.

Work through the deployment as its own task, because it’s the step most likely to surface problems the app has been hiding: environment variables that only existed on your machine, a database that requires SSL in production, file paths that resolve differently on Linux. There’s a full checklist for that.

Then the four things that make it safe to leave running: a backup you’ve restored once, error tracking that reaches you, an uptime check, and a way to roll back.

Mistakes to avoid

  1. Estimating instead of inventorying. The estimate is a feeling. The inventory is a list.
  2. Building version two’s features before version one ships. The settings page can wait.
  3. Trusting the happy path. Every Demo item has an empty state, a failure state and a double-submit waiting.
  4. Asking the AI to “finish it.” It will generate more screens. The gap isn’t screens.
  5. Treating deployment as the last hour. It’s a day, and it finds things.

Checklist

  • Every screen and action classified: Done, Demo, Stubbed, Missing
  • Version one scoped to ten items or fewer
  • Every Demo item tested empty, failing, slow, expired and wrong
  • Payments confirmed by webhook, not redirect; refund path exists
  • Auth tested for reset, expiry and duplicate signup
  • Email sending from a verified domain
  • Deployed on your domain with backups, error tracking, uptime and rollback

If the inventory comes back longer than you can face, that’s the point where having someone finish it starts to make sense — the inventory itself is the first day of that.

← All insights

Show me what’s stuck.

Don’t spend another six hours fighting the same bug. Send me what you’ve got — the messy version is usually the useful version.

Both open with a short template already filled in.