The three-layer sync model
Most production Volunteer integrations have three distinct sync layers, running at different cadences and serving different purposes:
Each layer has a different failure mode and recovery story. The initial sync can be restarted from scratch (it’s idempotent). Polling can lose ground (which reconciliation catches). Reconciliation can run late (no immediate user-visible impact).
Why three layers, not one
It’s tempting to “just poll” — and rely on polling to catch everything. The reasons that doesn’t work:- Polling can miss deletions. Deleted records don’t appear in
updated_afterqueries. - Polling can miss failed processing. A record was returned and the checkpoint advanced, but processing failed silently.
- Polling can’t recover from a lost checkpoint. If the checkpoint is corrupted, you don’t know where to restart.
Pattern 1: pull architecture (the Volunteer default)
Volunteer has no webhooks. Every integration is pull-based — partner integrations query the API on a schedule. This shapes everything else:When pull is the wrong choice
Pull architecture works well when:- The data doesn’t change second-to-second
- Sub-minute freshness is a comfort goal, not a hard need
- The integration can tolerate brief data lag
- Real-time signal is essential (live check-in dashboards, e.g.)
- The data volume is too high for full polling at the needed cadence
Pattern 2: hub-and-spoke architecture
For partner integrations serving many customers, the typical architecture is hub-and-spoke: The hub holds:- Per-customer tokens (with proper isolation)
- Shared infrastructure (workers, queues, DBs)
- Per-customer state (checkpoints, mappings, DLQs)
- Per-customer cadence configuration
Why hub-and-spoke beats per-customer-deployment
Some partners deploy a separate instance per customer. This is conceptually simpler but operationally heavier:
For most B2B partner integrations, hub-and-spoke is the right shape. Per-customer-deployment is reserved for high-stakes integrations with strict isolation requirements (compliance, security).
Shared vs per-customer infrastructure
Within the hub, decide what’s shared and what’s per-customer:Pattern 3: per-resource worker decomposition
Within the polling layer, two main organizational shapes:Shape A: monolithic poll cycle
One worker handles all resources for a customer in sequence:JavaScript
Shape B: per-resource workers
Each resource has its own worker with its own cadence: Pros: Per-resource cadence tuning; isolated failures; scales independently. Cons: More schedules to manage; more state per customer.Choosing between them
For most partner integrations, start with Shape A (monolithic) and decompose to Shape B when you have evidence one resource needs different treatment. Premature decomposition adds complexity for hypothetical needs. Signals to decompose:- One resource’s poll takes much longer than others
- One resource genuinely needs a different cadence (e.g., Form Completions every 5 min, everything else hourly)
- One resource’s failures shouldn’t block the rest
Pattern 4: queue-based decoupling
For higher-scale or higher-reliability integrations, decouple polling from processing via a queue: The polling worker’s only job is to detect changes and publish them to the queue. The processing worker(s) consume the queue and do the actual destination writes.Why decouple
The cost is operational complexity (you now have a queue to monitor and reason about).
When the queue is worth it
For partner integrations serving 50+ customers with high-volume sync needs, the queue is worth its weight in operational burden.
Pattern 5: bidirectional sync (when needed)
Most Volunteer integrations are one-way (VOMO → external). But some need bidirectional sync — external system pushes changes back to VOMO:Bidirectional brings complications
Loop detection pattern
Track the direction of recent changes and skip writes that would re-trigger a change you just processed:JavaScript
Authority configuration
Per-field authority declared explicitly:JavaScript
Pattern 6: multi-tenant scheduling
For hub-and-spoke integrations, scheduling polling across many customers needs to avoid:- All customers polling at the same minute (thundering herd against VOMO)
- All customers using rate budget on the same worker
- One customer’s heavy polling starving others
Staggered scheduling
Spread polls across the polling interval:JavaScript
Per-customer rate budget
JavaScript
Priority lanes
For customers paying for premium tier vs. standard tier:JavaScript
Pattern 7: regional and geographic considerations
For partner integrations serving customers in multiple regions:
For most partner integrations, single-region is fine. Multi-region is a complication worth taking on only when data residency or DR requirements demand it.
Pattern 8: progressive delivery and feature flags
When changing a sync integration in production, feature-flag the change:JavaScript
- Internal test customer first
- One or two friendly customers (with notification)
- 10% of customers
- 50% of customers
- All customers
Why feature flags matter for sync
Sync changes are subtle. A “small change” to the polling logic might:- Skip records that should be processed
- Re-process records that already were processed (wasted work)
- Advance the checkpoint incorrectly
- Trigger spurious side effects (welcome emails, notifications)
Pattern 9: separation of “infrastructure work” from “business work”
The polling worker shouldn’t know about your business logic; the business processor shouldn’t know about polling mechanics. Separate them cleanly:JavaScript
Pattern 10: operations playbook
Production integrations need a documented playbook for common operational scenarios. A starter checklist:
Even a basic playbook ensures consistent response when problems arise. The detail matters less than having one.
Architecture maturity model
Where does your integration sit?
Most partner integrations land between Level 2 and Level 3. Level 5 is reserved for the most operationally critical integrations.
Don’t try to skip levels — each level’s practices build on the previous. A Level 1 integration that adds feature flags before adding multi-tenant scoping is over-engineered for its actual maturity.
Decision framework
When designing a new sync integration, walk through these questions:
Answering these explicitly produces clearer architectural choices than improvising as you build. Document the answers; revisit them when scale changes.
Where to go next
Security and Credential Management
The security patterns that protect this architecture.
Versioning and Backward Compatibility
The patterns for surviving API changes.
Data Modeling
The data model that supports this architecture.
Error Recovery Patterns
The error-handling patterns this architecture relies on.