Back to portfolio
ENESFRPT
Data

How I Migrated a Production Business from Google Sheets to PostgreSQL

12 min read · 2026

After a catastrophic flood destroyed our warehouse and forced us to rebuild from zero, I decided to do it right this time — migrate from spreadsheets to a real database, build a proper ERP, and never lose control of our data again. This is the story of that migration.

Context: The Flood That Changed Everything

In April 2024, we moved into an industrial warehouse in Alfafar, Valencia, to scale operations. Six months later, in October 2024, the DANA — a catastrophic weather event — hit Valencia. Our warehouse was in one of the hardest-hit areas. Floodwater destroyed inventory, equipment, and months of operational momentum.

What followed was two months of crisis management that had nothing to do with code. In the weeks after the flood, volunteers and military personnel came to help us recover the warehouse operations — inventory, equipment, cleanup, and logistics. Then came the insurance claims, emergency budgets with no revenue, and the mental reset of seeing everything you built underwater. It was the hardest period of my life.

But it was also a reset. When we rebuilt — and we did rebuild — I made a decision: we would do it right this time. No more duct tape. No more "good enough." We rebuilt the team from 2 to 5 employees, and I committed to building proper infrastructure: a real database, a real ERP, real documentation. The Google Sheets era was over.

The Starting Point: Why Google Sheets Had to Go

When I originally founded the company, Google Sheets was the obvious choice for managing inventory. It's collaborative, visual, everyone knows how to use it, and it's free. My warehouse team could update stock locations. I could write formulas. Everyone could see everything in real time.

The problem is that Google Sheets was never designed to be a database. And when you're processing thousands of Amazon liquidation SKUs across multiple marketplace accounts, it starts to break in ways that are hard to predict:

I had to migrate. But I couldn't stop the business to do it.

The Constraint That Changed Everything

This wasn't a weekend side project migration. This was a production business with real constraints:

I couldn't flip a switch. I needed a migration strategy that would let me move incrementally, validate at every step, and roll back if something went wrong.

The 5-Phase Pipeline

I designed the migration as a 5-phase pipeline, where each phase could be run independently in either dry-run or execute mode:

Phase 1: EXTRACT & ANALYZE
  └─ Pull all data from 3 Google Sheets tabs → CSVs
  └─ Generate analysis report (row counts, data types, anomalies)

Phase 2: VALIDATE
  └─ Parse every row against the target PostgreSQL schema
  └─ Type conversions (dates, decimals, booleans, enums)
  └─ Flag invalid rows with specific error reasons
  └─ Generate validation report

Phase 3: MIGRATE
  └─ Load validated data into PostgreSQL
  └─ Respect foreign key ordering (items → listings → sales)
  └─ Dry-run mode: simulate without writing
  └─ Execute mode: actual INSERT with transaction safety

Phase 4: SYNC OPERATIONAL DATA
  └─ Pull "Prep de Pedidos" (order preparation) tab
  └─ Match warehouse status to database records
  └─ Update pick/pack states

Phase 5: CONTINUOUS SYNC
  └─ Daily: Sheet → DB delta sync
  └─ Handles new rows, updates, and edge cases
  └─ Runs alongside the database as source of truth transitions

The Hard Parts Nobody Talks About

1. Data Quality Is Never What You Think

Google Sheets is "flexible" — which means your data is a mess. Here's what I found when I tried to parse 8,000+ inventory rows:

I spent more time writing data cleaning and normalization code than the actual migration logic. Every edge case I fixed revealed two more.

2. Financial Reconciliation Per Truckload

We buy Amazon liquidation by the truckload. Each truckload has a code (A2Z33838, A2Z34102, etc.) and a cost. For tax purposes, I need to know: for each truckload, how much did we pay, how much did we sell, what's still in stock, and what's the margin?

In Google Sheets, this was a tab with SUMIFS formulas. In PostgreSQL, I needed to rebuild this as a view (v_resumen_a2z) and validate that the numbers matched to the cent.

Truckload  | Sheet Total | DB Total  | Diff    | Status
A2Z33838   | €14,230.50  | €14,230.50| €0.00   | ✓ MATCH
A2Z34102   | €8,445.00   | €8,445.00 | €0.00   | ✓ MATCH
A2Z34567   | €11,200.75  | €11,198.25| €2.50   | ✗ INVESTIGATE
           |             |           |         |   → 1 sale missing
           |             |           |         |   → LPN XU-20240312

I generated hundreds of these reconciliation reports. Every discrepancy had to be investigated. Most were caused by timing issues (a sale recorded in the sheet but not yet extracted from Wallapop's API) or by my employees making manual corrections directly in the sheet.

3. The Parallel Operation Period

For about 3 weeks, I ran both systems simultaneously. Google Sheets remained the source of truth for my employees, while the database was being populated and validated in the background.

Every morning:

  1. Run the sync script to pull changes from Sheets into PostgreSQL
  2. Check the reconciliation report for discrepancies
  3. Investigate and fix any issues (usually 2-5 per day)
  4. Test Retool dashboards against real data
  5. Process the day's orders (the actual business operations)

Every evening:

  1. Review what employees changed in Sheets during the day
  2. Run the sync again
  3. Verify financial totals still match

This was exhausting. I was essentially running two systems, doing my job as business operator, AND debugging migration edge cases. But it was the only way to ensure confidence before the cutover.

4. The Fix Scripts

No migration is clean. I wrote 15+ specialized fix scripts for issues discovered during or after the parallel period:

Each fix script followed the same pattern: diagnose → dry-run → review → execute → verify. No blind fixes.

5. Explaining to the Team

The hardest part wasn't technical. It was explaining to 4 non-technical employees why we were changing how they worked.

"The spreadsheet works fine" is a perfectly reasonable thing for a warehouse worker to say. And from their perspective, it did work fine. They didn't see the API rate limits, the data corruption risks, or the 3 AM scripts crashing because of a formula recalculation.

I had to:

The Cutover

After 3 weeks of parallel operation, 200+ reconciliation reports, and 15 fix scripts, I documented the cutover plan:

CUTOVER CHECKLIST
─────────────────
[✓] All truckloads reconciled to €0.00 difference
[✓] All active listings matched between Sheet and DB
[✓] All pending orders present in both systems
[✓] Retool modules tested: orders, items, sales, invoicing
[✓] Employees trained on new workflow
[✓] Sheet set to read-only with banner: "USE RETOOL"
[✓] Automated extractors pointed to DB (not Sheet)
[✓] Backup snapshot of all table counts taken
[✓] Rollback procedure documented (Sheet restore)

The cutover itself was anticlimactic — which is exactly what you want. I switched the extractors to write directly to PostgreSQL, set the Google Sheets to read-only, and told my team "starting today, use Retool for everything."

What I Built (The Technical Stack)

The migration wasn't just scripts. It required building the entire target infrastructure:

Results

6 months after the migration:

What I'd Do Differently

If I did this again:

  1. Start with the database from day one. The "we'll migrate later" tax is real and expensive. Even a simple PostgreSQL + custom ERP frontend on Retool setup from the start would have saved hundreds of hours
  2. Automate the reconciliation checks instead of running them manually every morning. A CI-style pipeline that emails me only when discrepancies are found
  3. Build the Retool UI first, then migrate. I built them in parallel, which meant employees saw half-finished interfaces during the transition period. If the UI had been polished before the cutover, adoption would have been smoother

The Takeaway

Migrating a production system isn't a technical problem. It's an operational problem that requires technical solutions. The code is maybe 30% of the work. The other 70% is:

I did all of this as a solo engineer running a business with 5 employees. No team of DBAs. No dedicated QA. Just systematic engineering and a lot of coffee.

The migration pipeline is open-source: github.com/AspiranteD/sheets-to-postgres-migration