Half of all internal "systems" are a person copying information from emails into a spreadsheet. A form that writes directly into the sheet removes the copying, and everyone keeps the spreadsheet they already understand.

1. Keep the spreadsheet

This is the point. Do not migrate anyone to a database. The sheet is where the pivot tables, the formulas and the muscle memory live. You are replacing the entry, not the storage.

2. Build the form

form-build.txt
Build a form collecting: <list the fields and types>

- One page, no framework. Works on a phone.
- Every input has a real <label>. Required fields marked, and the
  browser told about them.
- Validation messages next to the field, not an alert, and the typed
  values survive an error.
- A submit button that disables while sending, so a double tap doesn't
  create two rows.
- A clear success state on the same page.
- A honeypot field to catch bots. No captcha.

3. Wire it to the sheet

Two reasonable routes. A form service — Tally, Formspree, Google Forms — which is zero code and fine if you don't need custom logic. Or a small serverless function that appends a row via the sheet's API, which is more work and lets you validate, transform and notify.

sheet-append.txt
Write a serverless function that appends a row to <Google Sheets /
Airtable / Excel Online> from a form POST.

- Credentials in environment variables, never in the code.
- Validate every field server-side before writing. Reject rather than
  writing partial rows.
- Append only — never update or delete existing rows.
- Return a clear error the form can display if the sheet API fails,
  and log enough to work out why.
- Rate limit by IP.

4. Tell someone it happened

An email or a Slack message on each submission turns this from a data store into a workflow. Send it in the background, and make sure a failed notification never fails the submission — the row being saved is what matters.

Add a timestamp column the form fills automatically. It's the first thing anybody asks for and it's free.