Prototype → production
Your vibe-coded app works. Now you need to be able to trust it.
By Louis Cassedanne — Project Lead & Data and AI Architect
Published on · 13 min read

You built a first version with Lovable, Bolt, Cursor, Replit, Claude Code or another coding agent. It works well enough to demonstrate the idea, convince a customer, or start serving real users.
At that point the question is no longer really how the code was produced. What matters is whether you understand the system well enough to trust it in production.
Where the code came from does not answer that on its own. AI-generated code can be correct; code written entirely by hand can be fragile. What vibe coding mainly changes is the speed at which your product can become functional without your understanding of it keeping pace.
You can end up with an application that is close to complete as a piece of product, and still a long way from the guarantees it needs to be operated: permissions, data integrity, error handling, observability, cost control, the ability for someone else to take it over.
That gap is the subject here. Moving to production is less about "cleaning up" the code than about turning a system you understand imperfectly into one whose risks you understand well enough.
The real subject: comprehension debt
Technical debt did not arrive with AI assistants. You can already inherit a poorly documented codebase, take over a contractor’s work, or discover decisions nobody fully owns any more.
What vibe coding sharpens is something more specific: you can produce far faster than you build a mental model of the system.
In more traditional development, part of that understanding is built by writing the code. When you ask an agent for authentication, a customer area and a payment system, you can get a convincing demo very quickly without having answered questions like:
- where are permissions actually checked?
- can a user reach another organisation’s data by editing a request?
- what happens if a webhook arrives twice?
- can a migration leave existing data inconsistent?
- which third-party services were added along the way?
- what happens if a model call takes thirty seconds, or costs a hundred times more than expected?
None of this means your prototype is bad. These questions were simply outside what it was built to answer.
GitHub makes the same point in its Copilot documentation: generated code can look valid while being incorrect or vulnerable, and must be reviewed and tested before it is merged (GitHub Docs).
A prototype proves a use. Production asks for guarantees.
A prototype sets out to check that a use is worth existing. In production you also have to guarantee the product keeps working as conditions get less favourable: more users, more data, a third-party service down, a mistake in an operation, a version change.
Those guarantees are not absolute. An internal tool used by five named people does not carry the risk profile of a public multi-tenant SaaS. The NIST Secure Software Development Framework works the same way: practices are meant to be adapted to context, risk tolerance, resources and cost.
| Dimension | Prototype | Product in production |
|---|---|---|
| Use | The main path works | The error cases that matter are handled |
| Access | A few known users | Permissions are explicit and tested |
| Data | It is stored | It is protected, migratable and recoverable |
| Third parties | They respond during the demo | Their outages and retries are planned for |
| Operations | You find out about problems | You can detect and diagnose them |
| Handover | The author knows the system | Someone else can understand and maintain it |
Prototype
Demonstrate
"Is this use worth existing?"
Product in production
Guarantee
"Does it hold when conditions degrade?"
In practice — Themis-X / MyTravelConnect
For Themis-X we designed and built MyTravelConnect end to end. A designed and prototyped first version shipped in April 2024, the full application was online in May, and the engagement ran until September, with a proof of concept built with Aéroports de Paris and then a recommendation layer.
The product was not open to the public. Known participants in the EONA-X ecosystem connected to it to exercise specific use cases. That exposure was real, and known in advance, which is what made it possible to calibrate the guarantees of the first deployment.
But "controlled exposure" does not mean "lighter guarantees everywhere", and that is the interesting part. What the context allowed us to defer was scale: connecting the whole travel industry, self-service onboarding, load. What it did not allow us to defer was consent and data protection — because that was precisely what the product had to demonstrate.
Before refactoring, rebuild the model of the system
When you take over an application you barely know, the first instinct is usually to open the repository and look for what seems messy. That is not the best starting point.
Start instead by understanding:
- who uses the system, and in which roles;
- where the sensitive data lives;
- which external services are involved;
- which work happens asynchronously;
- which actions are expensive, irreversible or hard to replay;
- where the level of trust changes.
That last one matters most. In threat modeling, OWASP recommends mapping data flows, external actors and trust boundaries — the points where data moves from somewhere you control what happens to somewhere you no longer do — before reasoning about threats at all.
Take a SaaS application with a frontend, an API, PostgreSQL, Stripe and an external LLM. The repository shows you modules. A risk map shows you something else: the points where data moves from the browser into the API, from a user to a tenant’s records, from your backend to Stripe, or from your database to an external model.
Outside your control
User
Role and organisation to establish, never to believe
Browser
Everything arriving from here is an assumption
Authentication
Your application
Frontend
No check can stop here
API
Where permissions must actually be verified
Workers
Asynchronous work, replayable
Per-tenant authorization
Your data
PostgreSQL
Personal data, migrations, backups
File storage
Direct access to control separately
Secrets
Keys to the services below
Data leaving
Third-party services
Payments
Inbound webhook: authenticate it, make it idempotent
LLM
What leaves does not come back; variable latency and cost
A tolerable outage, provided you know about it
Those boundaries are where it becomes worth asking which checks actually run and which assumptions are implicit. An 80-line function is rarely a risk in itself. An implicit authorization rule can be.
Secure the invariants that matter
Once you understand the system, reading the whole codebase at the same intensity is rarely the best use of the time available.
It is more useful to start from the invariants: the properties that must stay true.
In a B2B SaaS:
A user from company A must never be able to read or modify company B’s data.
In a payment system:
Receiving the same event twice must never produce the business effect twice.
For an external integration:
A third-party outage may degrade a feature, but must not make the whole application unusable.
From there you can build the controls that genuinely buy confidence. For tenant isolation, an explicit test matrix is usually more useful than an abstract coverage target:
| Actor | Resource | Action | Expected result |
|---|---|---|---|
| User A | Invoice A | Read | Allowed |
| User A | Invoice B | Read | Denied |
| Admin A | User B | Update | Denied |
| Anonymous | Invoice A | Read | Denied |
Broken access control is still the first category in the OWASP Top 10 2025, and OWASP publishes a dedicated guide to authorization regression testing.
The same logic applies to idempotence: Stripe documents explicitly that the same webhook event can be delivered more than once, and recommends recording the events you have already processed (Stripe Docs).
So the question is not "do we have enough tests?" but which important properties are we able to defend?
Prioritise risk, not code tidiness
A codebase can be imperfect without being dangerous. Conversely, a very tidy application can hide a critical business assumption that has never been tested.
For each sensitive area, look at least at five things:
| Question | Example |
|---|---|
| How likely is failure? | An unstable external service, a migration run by hand |
| What would the impact be? | A duplicated payment, a data leak |
| How far would it reach? | One user, one tenant, every customer |
| How fast would you know? | An immediate alert, or a customer complaint |
| Can you recover? | A simple retry, or corruption that is hard to reverse |
Likelihood counts, but it does not decide on its own: on a security invariant, reach usually wins, because a small but non-zero likelihood applied to every one of your customers is still unacceptable.
A cross-tenant permission is checked incorrectly
Fix immediately
- Impact
- Another customer’s data leaks
- Reach
- Potentially every customer
- Detection
- Hard — nothing flags a read that was wrongly allowed
A payment webhook runs twice
High priority
- Impact
- Double charge, or double fulfilment
- Reach
- One customer at a time
- Detection
- Medium — usually via the complaint
A UI component is duplicated
Can wait
- Impact
- Visual inconsistency
- Reach
- One screen
- Detection
- Easy — visible straight away
Before production we almost always look at authorization, tenant isolation, secrets, migrations, data integrity, idempotence, external dependencies, backups, variable costs and observability.
Non-critical duplication, inconsistent conventions, mediocre naming or purely cosmetic refactors can usually wait.
The OWASP Top 10 2025 also puts Software Supply Chain Failures in third place, which is reason enough to take dependencies and the build chain seriously on a quickly assembled prototype.
Put three kinds of guardrail in place: prevent, see, recover
Once the priority risks are identified, choosing guardrails becomes much simpler.
Prevent
- Tests on the invariants
- CI and type checking
- Explicit access rules
- Versioned migrations
See
- Structured logs
- Error tracking
- Metrics
- Actionable alerts
Recover
- Rollback
- Tested backups
- Feature flags
- Retries and kill switches
Prevent
CI, type checking, tests on the invariants, static analysis, secret scanning, versioned migrations, explicit access rules.
The aim is not to prevent every bug, but to keep out the mistakes that are already predictable enough.
See
Structured logs, error tracking, metrics and alerts someone can actually act on.
Google SRE offers four useful signals for user-facing systems: latency, traffic, errors and saturation (Google SRE). On an AI product you can add timeouts, retries, or the average cost of a journey.
Recover
Rollback, feature flags, tested backups, retries, and the ability to switch an integration off quickly.
Rewrite or stabilise? Start with whether the original assumptions still hold.
The rewrite question comes up almost every time you take over a prototype. Our position is not "never rewrite".
Two Vezero engagements show the two possible answers.
| Criterion | Kyutai | Generous |
|---|---|---|
| Product assumptions in the existing code | Still valid | Superseded |
| What had changed | The intended use: demos, then open source | The business goal and the user experience |
| Decision | Keep and extend | Restart on a fitting base |
Kyutai: keeping what already had value
On Invincible Voice the existing codebase encoded the right assumptions: the functional core matched the product Kyutai wanted to release. We had no reason to rebuild it.
The work was to extend it towards two deadlines: demos in the short term, then an open-source release — which moves the bar considerably, because published code has to be picked up by developers outside the team. Security and authentication, tests, handover readiness and clearing out dependencies on the original setup all belonged to that second goal.

A concrete example of the kind of decision that involves: audio streams were loaded by prefetch, which suited a research prototype perfectly but weighed on how responsive the interface felt. We moved them to streaming. That trade-off does not show up when you read code looking for what is messy — it comes from the intended use.
Two months after the engagement started, the project could be published.
Generous: restarting because the product had changed
On Generous the situation was different. A first prototype existed, but the business goal and the intended user experience had moved on since it was built.
Keeping it would partly have meant keeping assumptions that no longer held — which is exactly the case where a rewrite is justified. We restarted on a base that fit the product actually being aimed at: two months to a demonstrable V1, then two more to full production support.
Keep using AI, but change the decisions it gets to make
Moving to production does not mean dropping Cursor, Claude Code or other agents. What changes is the nature of the instructions you give them.
While prototyping:
"Build me a customer area with authentication and organisation management."
Once the system is critical:
"Here is the current data model. Here is the tenant isolation invariant. Here are the integration tests that encode it. Change only the authorization module. List the migrations needed and the missing tests before writing any code."
In the first case you are asking the tool for a feature. In the second you are asking it for a change under constraints.
GitHub recommends a similar approach: use tests and automated analysis, check that the change respects the project’s architecture, and give the AI sources of truth such as documentation or the repository’s conventions (GitHub Docs).
| Vibe coding | AI-assisted engineering |
|---|---|
| "Make this feature work" | "Change this behaviour under these constraints" |
| Partial context | Explicit architecture and sources of truth |
| Mostly visual validation | Explicit tests and invariants |
| Wide latitude given to the tool | Limited surface of change |
Three questions to know where you stand
There is no universal production readiness score. But for a given scope, you should be able to answer three questions:
What can break? How will you know? What will you do when it happens?
They map exactly onto the three guardrails above: prevent, see, recover.
If you can answer them for your critical areas, and those answers are tested rather than assumed, your product can be ready even if the code still has rough edges. Conversely, a spotless repository is not enough if nobody can answer them.
Frequently asked questions
Do you have to audit every line of AI-generated code before production?
No. Start by understanding the architecture, identifying the critical flows and writing down the invariants that must never break.
Code review and analysis tooling can then be concentrated where a mistake would have real consequences.
Is AI-generated code less safe than code written by hand?
Where it came from does not answer the question. Generative tools can produce incorrect or vulnerable code, but code written by hand can have exactly the same defects.
For a production decision, it is more useful to assess the controls, the dependencies and the actual behaviour of the system.
How do you know whether to rewrite or keep a prototype?
Look first at the assumptions it embodies. If the business need, the UX and the fundamental constraints still hold, stabilising what exists is usually the rational choice.
If those assumptions have changed deeply, keeping the code can cost more than restarting on a base that fits.
How long does it take to move a prototype into production?
There is no universal duration, but the product’s real exposure changes the problem substantially.
In our own projects we had two months to make Invincible Voice publishable as open source at Kyutai, four months to take Generous from an inherited prototype to full production support, and around two months to put the complete MyTravelConnect application online in a controlled production context.
These are not benchmarks: they mostly show why you have to understand the level of guarantees expected before putting a number on the work.
Method and sources
This article draws on our experience taking over and productionising existing products — Kyutai, Generous and Themis-X / MyTravelConnect in particular — as well as on several external technical frameworks and references.
- GitHub — Responsible use of Copilot agents
- GitHub — Review AI-generated code
- NIST — Secure Software Development Framework (SSDF)
- OWASP — Threat Modeling Cheat Sheet
- OWASP — Top 10:2025
- OWASP — Broken Access Control
- OWASP — Authorization Regression Testing
- Stripe — Webhook best practices
- Google SRE — Monitoring Distributed Systems
- Martin Fowler — Strangler Fig Application
Your prototype works. The question is what it still needs to hold in production.
We take over what exists, identify the risks that genuinely matter, and work out what has to be stabilised, reinforced or replaced.
The goal is not to rebuild your product "properly" on principle, but to keep what still rests on sound assumptions and replace only what warrants it.
The expertise behind this article
Product architecture and engineeringWhat we have built