A prototype exists to reduce uncertainty quickly. It is built without tests, without error handling, and without the structure production code needs, precisely because those things would slow down the learning it exists to produce.
That is entirely correct, right up until the prototype is demonstrated to someone who asks how soon it can go live.
The demonstration problem
A working prototype looks finished. The absent parts — the failure handling, the edge cases, the security, the scale — are invisible in a demonstration, and the remaining effort is therefore systematically underestimated by everyone watching.
Say clearly what has been skipped, at the moment of showing it. Afterwards it reads as excuse-making.
Prototype code carries its shortcuts forward
Promoting a prototype means every decision made for speed is now permanent, and none of them were made with production in mind.
The shortcuts are also invisible to whoever maintains it next, because they look like ordinary code rather than deliberate omissions.
Keep the learning, discard the artefact
Write down what the prototype established: what works, what does not, what surprised you, what the real constraints turned out to be. That is the deliverable.
Rebuilding with that knowledge is fast, because the uncertainty that made the work slow has already been removed.
Make it obviously temporary
Keep prototypes in a separate repository, label them clearly, and use visibly non-production styling. Anything that makes promotion feel like a decision rather than a default is worth doing.
Where the pressure to ship a prototype is irresistible, at least treat it as a rewrite that happens to have a working reference implementation.
The question a prototype answers is worth a great deal. The code that answered it is usually worth nothing, and pretending otherwise is how a fortnight of learning becomes three years of maintenance.

