Building an app is as much about deciding what not to support as it is about writing code.
When starting out on Bill2CRM, the initial architecture felt ambitious and capable:
IndexedDB on the client for local-first speed and offline availability.
WordPress as a headless backend for content and custom endpoints.
Supabase for real-time Postgres capability and modern auth.
On paper, it felt like getting the best of all worlds. In reality, juggling three different database paradigms simultaneously created an architectural nightmare that almost stalled development entirely.
The Triple-Database Trap πΈοΈ
Trying to keep three separate data stores in sync led to a cascade of painful edge cases:
Three-Way State Drift & Sync Hell: Keeping local IndexedDB state in sync with both a WordPress MySQL setup and a Supabase Postgres instance required a massive custom sync engine. If a user made an edit offline, pushing that change across two separate cloud backends with different schema models was a constant battle.
Backend Trapped in Local: Whenever network conditions dipped or a sync queue failed, data frequently stayed stranded inside the user's local browser storage, creating phantom bugs where local data didn't match either cloud backend.
Fragmented Auth & Permissions: Managing WordPress user roles alongside Supabase Row Level Security (RLS) while validating permissions against local browser tokens meant doubleβand sometimes tripleβhandling authentication logic.
Feature Velocity Killed by Plumbing: Instead of building actual CRM features for users, 80% of development time was spent writing glue code, handling retry logic, and resolving data conflicts between three backends.
The Pivot: Consolidating into 1 Territory π―
Eventually, a hard call had to be made: stop fighting three backends at once and retreat into one single territory.
We cut out the middle ground and migrated the entire architecture purely to Supabase. The relief was immediate:
Single Source of Truth: A unified PostgreSQL database eliminated schema translation layers and duplicate object stores.
Native Real-time & Auth: Out-of-the-box JWT authentication and real-time listeners replaced hundreds of lines of custom polling and sync logic.
Clean Architecture: Local state became simple UI caching rather than a full competing offline database trying to talk to multiple cloud endpoints.
π‘ Key Takeaway for Builders
If you are shipping an MVP, don't split your backend state across multiple competing platforms unless your core product absolutely demands it.
A hybrid local/multi-cloud setup sounds flexible on paper, but picking one solid, modern ecosystem first lets you stop fighting your infrastructure and focus on what actually matters: delivering value to your users.
Have you ever caught yourself building custom sync plumbing for too many backends at once? How do you handle local vs. cloud state in your builds? Drop your thoughts below! π
Top comments (0)