DEV Community

Cover image for Stop Treating Google Forms Responses as Rows
Lovanaut
Lovanaut

Posted on

Stop Treating Google Forms Responses as Rows

Google Forms responses often start as rows.

That is fine.

The first version is simple:

Google Form
-> Google Sheet
-> rows of responses
Enter fullscreen mode Exit fullscreen mode

For a small survey, that may be enough.

But the moment someone asks this question, the row has changed:

Who owns this response?
Enter fullscreen mode Exit fullscreen mode

Now it is no longer just collected data.

It is a workflow record.

I have been building FORMLOVA, a form operations product where responses can be searched, filtered, assigned a status, routed into notifications, excluded from analytics, and operated through chat and MCP clients.

One design lesson keeps showing up:

A spreadsheet row becomes a workflow record the moment someone asks, "Who owns this response?"

This post is about the smallest schema I would add when a Google Forms response Sheet starts becoming an operations surface.

It is not a replacement for Google Forms.

It is a way to keep a lightweight Google Forms + Sheets workflow understandable for longer.

The Symptom: The Team Uses the Sheet, But the State Lives Elsewhere

The first response Sheet usually looks like this:

Timestamp
Name
Email
Inquiry type
Message
Enter fullscreen mode Exit fullscreen mode

Then the team adds Slack notifications.

Someone reacts with an emoji.

Someone replies in a thread.

Someone changes the row color.

Someone adds a comment.

Someone says, "I already handled that one."

That works until you need to answer basic operational questions:

Which responses are still open?
Who owns each one?
Which ones were excluded from reporting?
Which responses have not changed in 7 days?
Did Slack get notified, or is the response actually done?
Enter fullscreen mode Exit fullscreen mode

If those answers live in Slack threads, row colors, memory, and scattered comments, the Sheet is no longer the source of truth.

It is only the place where the data arrived.

Add Workflow Fields Explicitly

The minimal fix is not a large system.

Add explicit workflow fields.

status
owner
last_event_at
next_action
exclusion_reason
notification_state
Enter fullscreen mode Exit fullscreen mode

That turns the row into a small response record.

field purpose
status whether the response is new, in progress, done, or excluded
owner who is responsible for the next action
last_event_at when the workflow state last changed
next_action what should happen next
exclusion_reason why this response should not count in normal reporting
notification_state whether Slack/email notification was sent, skipped, or failed

These fields are boring.

That is the point.

Operational systems get easier when the boring facts are visible.

Keep Status Small

Do not start with 12 statuses.

Start with four.

type ResponseStatus =
  | "new"
  | "in_progress"
  | "done"
  | "excluded";
Enter fullscreen mode Exit fullscreen mode

That maps well to a Sheet:

new
in_progress
done
excluded
Enter fullscreen mode Exit fullscreen mode

You can add waiting later if the workflow really needs it.

But in the first version, extra statuses often create ambiguity:

pending
waiting
checking
reviewing
assigned
open
Enter fullscreen mode Exit fullscreen mode

Are those different states, or different words for the same state?

If the team cannot explain the difference, do not add the status yet.

Owner Is Not Status

Owner and status should not share one field.

This is a common mistake:

Status: Tanaka
Status: Sales team
Status: Done by Sato
Enter fullscreen mode Exit fullscreen mode

That makes filtering harder and semantics weaker.

Use two fields.

owner: Tanaka
status: in_progress
Enter fullscreen mode Exit fullscreen mode

Then define simple rules:

new:
  owner can be blank

in_progress:
  owner should be set

done:
  last_event_at should be updated

excluded:
  exclusion_reason should be set
Enter fullscreen mode Exit fullscreen mode

This is enough to catch many workflow gaps.

An in_progress response with no owner is suspicious.

An excluded response with no reason is suspicious.

A new response that has been untouched for 7 days is suspicious.

You do not need a complex app to see those problems.

You need fields with stable meanings.

Row Color Is Not a Data Model

Color is useful.

Color is not a contract.

This is fragile:

yellow row = in progress
green row = done
gray row = excluded
Enter fullscreen mode Exit fullscreen mode

It looks good until someone copies rows, filters data, exports CSV, changes a theme, or forgets what the color means.

The value should be the source of truth.

status = done
row color = green
Enter fullscreen mode Exit fullscreen mode

In that order.

Use conditional formatting if you want visual scanning.

But let the column value drive the color, not the other way around.

Notification State Is Separate

If you add Slack or email, avoid this shortcut:

status = notified
Enter fullscreen mode Exit fullscreen mode

Notified is not a response status.

It is a notification state.

I would model it separately:

type NotificationState =
  | "not_required"
  | "pending"
  | "sent"
  | "failed"
  | "skipped";
Enter fullscreen mode Exit fullscreen mode

Then the response can say:

status: new
notification_state: sent
owner: blank
Enter fullscreen mode Exit fullscreen mode

That means the team was notified, but nobody owns the response yet.

That is a very different state from:

status: done
notification_state: sent
owner: Tanaka
Enter fullscreen mode Exit fullscreen mode

When these facts share one column, the workflow becomes hard to reason about.

Initialize Status With Apps Script

If you are already using Apps Script, the first automation can be very small.

On form submit, initialize the operational fields.

function onFormSubmit(e) {
  const sheet = e.range.getSheet();
  const row = e.range.getRow();
  const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];

  setCellByHeader(sheet, headers, row, "status", "new");
  setCellByHeader(sheet, headers, row, "last_event_at", new Date());
  setCellByHeader(sheet, headers, row, "notification_state", "pending");
}

function setCellByHeader(sheet, headers, row, headerName, value) {
  const index = headers.indexOf(headerName);
  if (index === -1) return;
  sheet.getRange(row, index + 1).setValue(value);
}
Enter fullscreen mode Exit fullscreen mode

This does not route ownership.

It does not send Slack.

It does not classify spam.

It only makes the response state explicit from the first moment.

That is a useful starting point.

After that, you can decide whether to add notification logic, owner assignment, exclusion rules, or a separate workflow table.

Protect the Contract

Once Apps Script reads these columns, the Sheet has a contract.

Column names matter.

Enum values matter.

Manual edits matter.

At minimum:

  • read by header name, not column number
  • use dropdowns for status and notification_state
  • protect workflow columns from casual edits if needed
  • keep raw response fields separate from operational fields
  • write errors to a predictable last_error column or log sheet

This is the moment many teams accidentally build a backend inside a spreadsheet.

That is not always wrong.

It just means the spreadsheet deserves backend-like care.

Where FORMLOVA Fits

FORMLOVA is built around the idea that form responses become operations after submission.

One real workflow I use in product examples is:

List response names.
Filter responses where participant count is 3.
Show the full details for those people.
Update those two responses to in_progress.
Enter fullscreen mode Exit fullscreen mode

The important part is not the specific event-registration example.

The important part is that listing, filtering, inspecting, and updating status stay in one operational context.

The response record has state.

The state can change.

The change is part of the workflow, not a side note in Slack.

If you are staying with Google Forms + Sheets, you can still apply the same rule:

Put status and ownership on the response record.
Treat notifications as side effects.
Keep exclusion explainable.
Enter fullscreen mode Exit fullscreen mode

That mental model scales further than "send it to Slack and hope someone handles it."

Checklist

If your Google Forms responses land in Sheets, check this:

[ ] Does every response have a status field?
[ ] Are status values fixed, not free text?
[ ] Is owner separate from status?
[ ] Can you filter open responses?
[ ] Can you find responses untouched for several days?
[ ] Can you explain excluded responses later?
[ ] Is Slack notification state separate from response status?
[ ] Are row colors driven by values, not memory?
[ ] Are Apps Script column names treated as a contract?
Enter fullscreen mode Exit fullscreen mode

If most answers are no, the workflow is probably living in people's heads.

That is fine for a very small process.

It is risky once the form starts receiving real business work.

References

Related FORMLOVA Notes

Try FORMLOVA | MCP setup guide

Top comments (0)