Should I vibe code
Build a polished form that writes to a user-owned database and supports simple branching
You never decided to store health data. A dropdown someone added to an event signup decided it for you.
?
Their verdict, the Starter price and the build-time estimate come from their entry, MIT-licensed. Checked 2026-08-04.
?
Our verdict, the regret score and everything below it. Editorial and unsponsored — nobody can pay to be moved.
The honest answer
why the verdict is what it is
The form is an afternoon: a public endpoint, a JSON column, an admin page. That build works, which is the problem, because what you have shipped is not a form — it is an open-ended store for whatever anyone decides to ask strangers for. You are not in the room the day a colleague adds a “any medical conditions or dietary requirements?” box to an event signup, and your schema cannot tell that answer apart from a favourite colour. Nothing in the code decided to hold health data. A dropdown in the builder did.
What actually breaks
not "if". the specific failures.
- The submission endpoint, which is public by definition and therefore a spam target, a probe target and a free way to fill your database
- File upload — the single most reliably broken feature in generated code: no type check, no size cap, and the bytes served back from a path the uploader chose
- The listing route, which is one missing ownership check away from public, and where the bug is invisible in testing because your own session always passes
- Data classification, because there is none: `data jsonb` holds a favourite colour and a passport number in the same column with the same retention and the same encryption, which is to say none
- Deletion, when a respondent asks you to erase their answers and those answers also exist in the CSV export in Slack, the notification email, and last night’s backup
- Your definition of the project, the first time someone asks for a payment field
The form was for a company offsite. Name, dietary requirements, and a free-text box asking whether there was anything the organisers should know. Two hundred and eleven people answered it honestly and nine of them typed a medical condition into a box on a server you set up in an afternoon, because the only form that existed when you built it was a newsletter signup. You learn that the listing route takes a form id and never checks who is asking when a colleague sends you a screenshot of somebody else’s answers, reached by changing one number in the URL. The submissions are unencrypted, there is no retention rule, and the honest answer to “how long have you kept this?” is “since the beginning, and also in every backup”.
Is that you?
the verdict is a default, not a law
- You are the only person who can create a form, and you approve field by field what it asks for
- It writes into a store somebody else secures — a Sheet, an Airtable base, a database with access control you did not write
- No file uploads, no payments, no signatures, and a standing rule against free-text boxes that invite people to explain their situation
- The form asks for an email address and nothing more
- Other people can create forms on it, because you have just stopped controlling what it collects
- It accepts file uploads, which means it accepts passports, payslips and letters from a doctor whether or not you asked for them
- Nobody has written down a retention period, which means the answer is “forever”
- You cannot erase one person’s submission from the database, the exports and the notification emails in a single tested operation
- A payment field is anywhere on the roadmap
If you build it anyway
the checklist, then the prompt that enforces it
- Write the deletion path before the collection path. If you cannot erase one person from the table, the exports and the sent notifications in one operation, you are not ready to collect anything.
- Require a retention period per form before that form can go live, and enforce it with a scheduled job you have actually watched delete a row.
- Authorise every read of submissions by form ownership, and write the test where user B requests user A’s form id. Your own session will always pass — that test is the only thing that will not.
- Rate limit and challenge public submissions from day one. The endpoint is public by design, which makes it a target by design.
- No file uploads until you have decided where the bytes live, who can read them, whether they are scanned, and when they are deleted. “S3 bucket, default settings” is not an answer to any of those.
- Take payments only by redirecting to a hosted checkout. If a card number can reach your server, delete that code and start again.
- Refuse special-category questions in the builder itself — health, ethnicity, religion, biometrics, government identifiers — or accept that you are now running a system that stores them.
I am building a form builder: I create forms, strangers fill them in, the answers land in my database. Apply these in this order and refuse the ones I try to skip.
1. Tell me first that a generic form builder ends up storing whatever anyone
types into it — health details, ID numbers, financial information — and
that my schema cannot tell those apart from a favourite colour.
2. Deletion first. Build and test "erase everything belonging to this
respondent": the row, uploaded files, generated PDFs, cached exports, and
which notification emails already went out. No collection code until it
passes.
3. Retention second. A form cannot be published without a retention period,
enforced by a scheduled job. Show me the job deleting a real row.
4. Authorisation third. Scope every read by form ownership, and write the test
where user B requests user A's form id and gets a 404. My own session always
passes, so manual testing will never catch this one.
5. Only then the public submission endpoint: rate limit per IP, add a
challenge, cap the payload, reject unknown fields rather than storing them.
6. File uploads are off by default. If I ask for them, make me answer where the
bytes live, who can read them, whether they are scanned and when they are
deleted. Never serve a file from a path the uploader controlled, and never
infer type from a filename.
7. Payments are off. If I insist, redirect to a hosted checkout and refuse to
write any code that can see a card number.
8. Warn me inside the builder when I add a field about health, ethnicity,
religion, biometrics or a government ID, and make me confirm I intend to
store special-category data.
9. Encrypt submissions at rest and log every export with who triggered it.
10. Out of scope on purpose: signatures, scheduling, login-gated forms, partial
response capture — each adds a category of data I have not planned for.
11. Finish by telling me that being the data controller, not building the form,
is the expensive half of this project, and that $15 a month buys a company
that has already answered every question above in writing.That one keeps you out of trouble. For the prompt that actually builds it, canivibecodeit.com has one.
their build prompt ↗Or don’t build it
the boring option, and the way back out
Almost always, and at $15 a month it is not close. Fillout’s entry tier already includes the exact parts that are dangerous to homebrew — file storage, hosted payments, signatures, CAPTCHA, a login wall — from a company that has written down where the uploads live and when they are deleted. Building the form is the cheap half. Being the data controller for whatever strangers typed into it is the half that has a legal definition.
$15/mo is cheaper than your weekend.
Export submissions as JSON with their form definitions, field types and timestamps, and keep uploaded files in a bucket you can hand over wholesale — a submissions table without the schema that produced it is unreadable within a year. Then do the part everyone skips: before you shut anything down, run the retention job to completion, because an abandoned form backend is a personal-data breach waiting for someone to find the bucket.
Active open-source survey and experience-management platform.
Open-source form builder you can self-host; closer in shape to Fillout, and someone else has already written the upload and submission handling.
Questions
It is just a contact form. Is this not wildly overwrought?
For one contact form asking name and email, yes — that is the SHIP IT version and it is in the list above. This entry is about the builder: the moment anyone can create a form, you no longer control the question set, and the honest scope of your database becomes “whatever a colleague thinks of”. The risk arrived with the second form, not the first.
Why is file upload singled out so hard?
Because it changes what you are storing without changing your code. A text field collects what you asked for; an upload field collects whatever the respondent had to hand, which in practice is passports, payslips, bank letters and medical notes. It is also the feature generated code gets wrong most consistently — unchecked type, uncapped size, and the file served back from a path an attacker chose.
Does GDPR really apply to a side project?
There is no hobbyist exemption. If you determine why and how personal data is processed you are the controller, and Art. 5 wants minimisation and a retention limit while Art. 32 wants security appropriate to the risk. None of that is exotic — it is roughly the guardrails list above, written down by someone with enforcement powers.
- GDPR Art. 5 — principles relating to processing of personal data (EU)
- GDPR Art. 32 — security of processing (EU)
Every week, someone ships something they shouldn’t have.
New verdicts, the worst thing that landed in the trap, and the occasional incident report. No other email, ever.
Survey answers are other people’s opinions with their identity attached. Store them like it.
A total computed in the browser is a discount code for anyone who can open devtools.
A form is easy. A form that receives strangers’ data and does not get spammed is less easy.
last reviewed 2026-08-04 · verdict is editorial and unsponsored · shared entry data from canivibecodeit under MIT · not legal advice