When a friend asked if we could build a tracker for his fantasy cricket league, the natural instinct was to reach for a database. Postgres. An ORM. A migrations folder. A docker-compose for local dev.
We didn't do any of that. The entire backend is a Google Sheet, and it's probably the best decision we made.
The organiser already had a spreadsheet
Every fantasy cricket league has a spreadsheet somewhere. Rosters, draft picks, scores from the match — it lives in Google Sheets because that's what normal people use. Asking the organiser to switch to an admin UI we'd have to design, build, and maintain just to do what Sheets already does well would be absurd.
So we wrote an ingestion pipeline that reads the sheet, parses each scorecard, and computes fantasy points. The organiser keeps working in Sheets. The pipeline keeps reading it. The site stays up.
What we gave up
A few things, to be honest:
- Strong schemas. A typo in a column header will silently break parsing. We mitigated this by logging parse failures loudly in a raw-scorecards tab.
- Referential integrity.If a player name in the scorecard doesn't match the player name in the roster, points don't count. We normalise aggressively and keep a small alias table.
- Concurrent writes.Not a real problem when there's one organiser and no concurrent editors.
What we got back
- Zero database ops. No migrations, no backups, no alerts.
- A fully auditable paper trail: every score is one row away from its source cell.
- Free multi-admin support. Anyone with sheet access is an admin.
- Easy disaster recovery: revert the sheet, re-run the pipeline, done.
If the league grew 100× we'd probably move to a real database. Until then, this is the right shape.