Skip to main content
NinjaNext
MVP and product

Taking a Half-Built Product to Production

Whether it came from an AI builder, a freelancer who stopped replying or an agency that ran out of road, the work of finishing it is the same shape. This is that shape, in the order it should be done.

By NinjaNext TechnologiesPublished 8 min read

Key takeaways

  • Almost nothing about finishing a product depends on how the half-built version was made.
  • The first week is reading and testing, not building; starting to fix is how the only advantage gets destroyed.
  • Two checks outrank everything else: whether one customer can reach another's data, and whether any secret is public.
  • The expensive decisions are ownership and data structure, both of which get harder every week you wait.

There is a particular kind of project that arrives already underway. Something exists, it mostly works, and the person responsible for it is no longer the person who built it. Sometimes that is because a contractor left. Sometimes an agency delivered something and the relationship ended. Increasingly it is because a founder built the first version themselves by prompting an AI tool, got further than anyone expected, and then hit a wall that is not about features.

Those three stories feel completely different to the people living them, and produce remarkably similar work. This is the shape of that work, with the parts that do differ marked clearly.

Why the origin matters less than it feels

What makes a product unfinished is rarely missing features. It is that the version which exists was built to be shown, and the version you need is one that strangers can use without supervision.

Those are different artefacts. A demonstration is allowed to assume a cooperative user, a working connection, one person at a time and nobody hostile. A production system is not allowed to assume any of that. Everything in the gap between them is the same regardless of who built the first one or what they built it with: access control, secrets, payments, failure handling, backups, the ability for somebody new to pick it up.

This is worth saying plainly because the explanations people arrive with are usually too specific. The code is not bad because an AI wrote it, and it is not bad because the previous developer was careless. It is incomplete in the places that only matter once it is real, which is exactly what you would expect.

The first week is reading, not building

There is heavy pressure at the start to show movement, and the quickest way to look busy is to begin fixing things. It is also the quickest way to destroy the one advantage you have, which is that the current behaviour is known, including the bugs.

So the first pass is description. What does it do, what does it talk to, where does the data live, what happens if it stops. In most inherited projects that produces the first written account of the system that has ever existed, and every piece of work afterwards is cheaper because it exists. What a code audit covers sets out that pass in detail, and it is deliberately bounded: a few days, ending in findings and a plan rather than in a backlog.

If the handover was abrupt, the reading is not even the first thing. Recovering accounts comes first, because software can be read by anyone competent while an account nobody can open is at the mercy of a support process. The order to work in when a developer disappears covers that, and the two items that cause lasting damage are a domain on a personal card and an app store listing under somebody else's developer account.

The two checks that outrank the backlog

Most findings add work. Two change what happens next, and both can be answered in an afternoon.

The first is whether one customer can reach another's data. You do not reason about this from the code; you make two accounts and try it, changing an identifier in a request to see whether the system objects. Products built with AI tools are especially prone to this, because the application talks to the database directly from the browser, which means the database is the only place access can genuinely be enforced. Where protections were added by prompting, later tables often arrived without the rules the earlier ones received, so the app looks consistent while the enforcement is not. Why one user can read another user's data explains the mechanism.

The second is whether any secret is public. The obvious version is a key committed to the repository, and the version history matters as much as the current files, because a key deleted last year is still in the history and still valid unless someone replaced it. The less obvious version is a key that was never in the source at all and reached users anyway, because build tools copy configuration into the files a browser downloads. The key that ignores your security rules is the one to understand first, since it is designed to bypass every access rule you wrote.

If either of these is wrong, it is not an item on a list. It is the thing that happens before anything else ships, and anything already exposed is replaced rather than hidden.

Ownership, and why waiting makes it worse

The next question is whose name everything is in. Repository, domain, hosting, database, payment processing, store listings. This is tedious and it is the part that quietly decides whether you have a business or a dependency.

Where a managed backend came bundled with the building tool, there is a further decision. It is excellent until credits run out or the data needs to sit under your own billing, and the move carries less than people expect: schema and rules rebuild from migration files, but rows, uploaded files, accounts and keys are carried by hand in an order that matters. Passwords usually decide the timing, because if they cannot travel then every existing account needs a reset email. What moves and what breaks walks the cutover.

The reason to do this early is simply that it gets harder every week. A migration with a handful of testers is routine work. The same migration with a real customer base is a rehearsed cutover with a window and a message to send, and nothing about the technology changed in between.

The part that genuinely is tool-specific

Having said the origin matters less than it feels, there is a real exception. Each AI builder concentrates its risk somewhere different, and knowing where saves days of looking.

  • Lovable: the managed backend and the move off it, which is an ownership and timing problem rather than a code problem.
  • Bolt: configuration, where a naming prefix decides whether a value ships to the browser, and adding it is the fastest way to make a development error disappear.
  • Replit: the deployment choice, where the default scales down to zero and quietly stops background work, timers and long-lived connections that assume something stays awake.

Each has its own page with the specifics: Lovable, Bolt and Replit. If you have not yet chosen, or want the comparison in one place, which of them survives real users sets the three side by side.

There is also a question of when to stop prompting altogether. The signal is not a feature you cannot build; it is fixes that undo each other, because nothing is testing the behaviour you already had. The useful answer is rarely to abandon the tool, but to put tests around the flows that matter and move the decisions that matter to a server. The signals that change the answer covers it.

Anywhere money changes hands

If the product takes payments, there is one rule that is worth more than the rest of this section combined: the browser is not allowed to be the authority on whether a payment succeeded.

A browser can be closed, refreshed, interrupted or tampered with. Deciding that an order is paid because a screen in somebody's hand said so produces the two failures a business cannot absorb, which are goods released without payment and customers charged for nothing. The confirmation has to arrive at your own server from the payment provider, and it has to be able to arrive twice without charging twice. Payment webhooks as the source of truth is the detail.

Being found, and working where people are

Two things are routinely discovered late and are much cheaper to decide early.

The first is whether anything can read your pages. Apps built by prompting are usually single-page applications, where one near-empty file serves every address and the content appears afterwards. Anything reading without running scripts therefore sees nothing, which is why a working product can be absent from search results and unknown to assistants at the same time. Usually only the public pages need fixing; nobody needs your dashboard indexed. Why an AI-built app is invisible has the test that takes thirty seconds.

The second is working without a connection, if the product is used anywhere a connection is unreliable. This is cheap as a design constraint and expensive as a retrofit, because it changes how state is stored throughout. Offline sync is where that cost actually lives, and it is not in the offline part.

A sensible order, and what it costs

Gathering the above into a sequence: recover access, read and test, fix anything that exposes data, settle ownership of the backend, then put tests around the flows that touch money and customer records. Only then features.

That order is deliberately front-loaded with work that produces nothing visible, which is uncomfortable when there is pressure to launch. The argument for it is that every item in it becomes more expensive the more customers you have, and none of the feature work does.

On cost: nobody can price this from a description, and anyone who does is pricing their uncertainty into your invoice. The honest structure is a bounded first stage that produces findings, then a fixed quote once the unknowns are known. What it costs to take over a codebase sets out what moves the number, and what finishing a half-built AI app costs does the same for products that came from a builder.

What finished actually looks like

It is worth having a definition, because without one this work has no end. A product is ready for strangers when a new developer can run it from the repository alone, when the flows that touch money and customer data fail a test rather than a customer, when no secret is public, when one customer cannot reach another's records, when a restore from backup has actually been performed rather than assumed, and when the accounts are all in your name.

Notice that none of those is a feature. That is the point. The half-built product you have is usually closer to finished than it feels, and further away than it looks, and the distance is made up almost entirely of things nobody demonstrates.

It is also worth saying what this work is not. It is not a rewrite, and a rewrite proposed in the first week should be treated with suspicion, because reading somebody else's code is slow and unrewarding while starting again is neither. There are honest reasons to rebuild, and they can always be stated in one sentence that a non-technical owner understands. If nobody can state it that way, the answer is almost always to finish what exists.

Let's build it

Want help putting this into practice?

Tell us what you are building and where you are stuck. We will reply with questions, a suggested approach and next steps.