Synccos | Checkwriter
A live, multi-tenant financial platform modernized incrementally — evolving from a tightly coupled single-tenant system into a scalable architecture while remaining in continuous production with active financial users throughout.
Ongoing · Production modernization · Active financial users throughout
At a Glance
Connection instances
30+→1
MongoDB connections consolidated to singleton
State management
Redux→TQ
server state migrated to TanStack Query
Multi-tenancy
Full
users + orgs sharing financial entities
Downtime
Zero
all changes shipped without taking system offline
* All changes were delivered incrementally under continuous production constraints with no downtime.
The Problem
Synccos Checkwriter was already live and handling real financial operations when new requirements arrived — multi-tenant support, expanded payment workflows, and organization-level banking entities. The system worked, but it had grown into a tightly coupled architecture that made every new feature a risk.
Redux was handling both client and server state, the MongoDB schema embedded financial entities directly inside user documents under a single-tenant assumption, and 30+ MongoDB connection instances had accumulated across modules — a hidden infrastructure fault that only surfaced once the schema was touched. The core constraint was non-negotiable: the system could not go offline, could not be rewritten, and new features had to ship continuously while the architecture underneath was being rebuilt.
Constraints
Architecture Overview
Frontend
Redux (client state) + TanStack Query (server state)
Backend
Express + normalized MongoDB collections
Ownership
entityType / entityId unified model
Auth
MFA — email, Twilio OTP, authenticator app
Payments
Stripe + QuickBooks sync
Infra
Singleton DB connection + compound indexes
Key Engineering Decisions
Separating client state from server state
Redux was doing double duty — managing both UI state and remote data fetching. This created deeply coupled reducer logic, redundant synchronization patterns, and brittle loading states scattered across the codebase. I drew a clear boundary: Redux retained ownership of client-only state, while all server interactions moved to TanStack Query. Data-fetching logic became declarative, caching became automatic, and the Redux surface area shrank to only what belonged there.
Eliminated redundant state sync patterns; API interactions became predictable and independently cacheableNormalizing the MongoDB schema for multi-tenancy
The original schema embedded bank accounts and payees directly within user documents — a clean design for a single-tenant system, but impossible to extend to organizations. I decomposed the schema into separate collections and introduced a unified ownership model using explicit entityType and entityId fields. A single bank account could now be owned by either a user or an organization without duplicating schemas or forking query logic. Compound indexes on these fields kept tenant-scoped queries efficient as data volume grew.
Shared financial entities (bank accounts, payees) work across both user and org contexts from a single schemaEnforcing a singleton database connection
After schema normalization introduced cross-collection dependencies, a previously hidden issue surfaced: 30+ MongoDB connection instances had accumulated across modules, causing inconsistent Mongoose model registration behavior where certain collections weren't being resolved correctly depending on connection context. The embedded single-collection design had masked this entirely. I enforced a singleton connection pattern — all models registered against one shared instance — which immediately stabilized schema resolution across the entire system.
Eliminated model registration inconsistencies; cross-collection queries became reliableSequencing refactoring around continuous feature delivery
The hardest part of this project wasn't any single technical decision — it was the ordering. Every architectural change had to be backward compatible with the code still running in production. TypeScript was introduced incrementally: new modules in TypeScript first, legacy modules migrated opportunistically. The API abstraction layer was extracted gradually. Schema normalization happened collection by collection, with dual-read logic bridging old and new shapes during the transition window.
Zero production incidents during the full modernization; system remained stable and shippable throughoutOutcomes
- Migrated server state from Redux to TanStack Query, eliminating redundant sync logic across the frontend
- Normalized MongoDB schema from embedded user documents to multi-tenant collections with a unified ownership model
- Resolved a 30+ connection accumulation bug that had been hidden by the single-tenant schema design
- Introduced TypeScript incrementally — new modules typed from day one, legacy modules migrated during ongoing development
- Built and extracted a centralized API abstraction layer, later released as the open-source axios-auth-client npm package
- Delivered Stripe payment flows, QuickBooks sync, Excel bulk import, and MFA (email + Twilio OTP + authenticator app)
- Built a check register system maintaining running balances per bank account across both user and org contexts
- Shipped all of the above with zero downtime under continuous production constraints
Takeaway
“Production systems evolve safely through incremental, constraint-aware refactoring. The most impactful engineering decisions are often about sequencing, ownership modeling, and tradeoff management — not isolated technical implementations. The goal was never to build the perfect architecture. It was to move from the architecture we had to the one we needed, without breaking what was already working.”