Why the Virya CRM stayed in Google Sheets
My backend instinct wanted PostgreSQL and an admin panel. The band needed a table it could edit, so I kept Sheets and added structure around it.
- Engineering
- Automation
- Data
- n8n
My first instinct was obvious: move the whole Virya CRM into PostgreSQL and build a proper admin panel. Clean schema, relations, constraints, a nice architecture diagram. Very developer. :)
Then I looked at the people who actually need to correct the data. Musicians and collaborators want filters, comments, colours and a table they already understand. Nobody wanted to wait for me to ship version two of an internal product before changing a festival deadline.
So Google Sheets stayed.
Separate tabs
What changed was the discipline around it. I split organisations, people, opportunities, Meta targets, collected items, workflow runs and outbound communication into separate tabs. One giant sheet holding a venue, its contact person, a submission deadline and three workflow logs looks convenient at first, but soon nobody knows which cell describes what.
Stable IDs instead of row numbers
Row numbers had to go as identifiers. Sorting a sheet shouldn't change the identity of a festival. Important records get stable IDs, and records created from public links use deterministic keys wherever possible. That became the basis for duplicate detection and idempotency.
Explicit status fields
Colours are still useful for people, but they no longer mean state. Yellow can mean waiting, important, reviewed or simply "Jakub liked this shade". The workflows read explicit fields such as status, approval, verification and next step.
Current state and run history
I also separate current state from execution history. A target says what's true now: when it was last checked, whether the API is available and what the latest bounded error was. A run says what happened during one execution: how many rows came in, how many succeeded, what failed and what was new. Without that split, today's successful run would hide the fact that the same integration had been broken for three days.
Sheets will accept almost anything, so I added a small schema around it: stable headers, ISO dates, normalised booleans, one value per machine-readable cell, raw data separated from normalised data, and a clear place for every automation to report an error.
Manual correction
The biggest advantage is still manual correction. When a workflow misclassifies a festival, somebody changes one cell and the next run uses the corrected value. No deployment, no migration, no custom UI release.
When to move to a database
I'll move more operational state into a database when concurrent writes, scale or access control require it. I may still keep Sheets on top as the human-facing layer.
For now the spreadsheet works, as long as I treat it as structured data rather than a loose collection of colourful rows.