Fueled
A multi-tenant event-activation platform built to keep commerce data moving when traffic spikes and vendors fail.
What it had to do
Browser-side tracking had become unreliable, but merchants still needed complete conversion signals in their ad, email, analytics, and warehouse tools. The platform moved those signals server-side: collecting storefront behavior and transactions, normalising them, enriching them with identity and consent, and delivering them in near-real-time.
Six source services and twelve destination services had to evolve independently without silently losing or corrupting an event. Bursts needed to become queue depth, a failed vendor needed to remain its own problem, and every service needed to agree on one canonical contract across roughly twenty-eight repositories.
- events ingested on an average day
- ~14M
- source services to destination services
- 6 → 12
- independently deployed repositories
- ~28
EventBridge routes; SQS absorbs bursts and isolates failure. Every consumer validates the same versioned contract, and every delivery service owns its vendor's retries, rate limits, and dead-letter queue.
How it moves
- 01
Accept
Storefront and commerce events reach a protected edge. The synchronous path does only the validation and routing work needed to accept them safely.
- 02
Buffer
A durable queue turns a traffic burst into backlog instead of letting it become pressure on every service behind it.
- 03
Normalise
The owning source service hydrates when necessary, then translates its platform-specific payload into the versioned event contract.
- 04
Enrich
Identity, consent, and enabled-channel configuration are applied before the core emits one independently routed event per intended destination.
- 05
Isolate
Each destination receives work through its own queue, so one slow or unavailable provider cannot block the rest of the pipeline.
- 06
Deliver & recover
A destination consumer maps the canonical event to the external API. Retries are bounded, and failed work is retained for controlled replay.
Failure was part of the shape
Queues absorb. Throttles pace. Isolation contains. Replay recovers.
Traffic surges
Bursts become visible queue depth. A deliberate throughput control lets the system drain at a predictable rate without stampeding downstream services.
A provider goes dark
Only that destination backs up. Every other integration keeps delivering, while retained failures can be replayed after the provider recovers.
A release goes wrong
Replaceable compute is kept apart from durable routing and recovery state, so a service can roll back without erasing accepted work.
What I actually did
Kept the hot path boring
Deferred execution and connection reuse so a cold Lambda does not go looking for a database handle it will only use once, plus caching in front of the reads that repeat. Cheaper to run and much less exciting at 2am.
Made integrations look like one
One canonical JSON Schema generated both the TypeScript types and the runtime validation every service used. Sources and destinations depended on that contract, never on each other — so adding the next integration was a bounded change, not an archaeology project.
Embedded it in Shopify without the sharp edges
Pages Router with getServerSideProps for App Bridge embedding, OAuth redirects, CSP hardening, and HMAC-verified compliance webhooks. A thin proxy route decodes the Shopify session token, exchanges it for a backend JWT, and passes the request on.
Assumed the API would be poked at
Tenant-aware guards and feature permissions on every operation, a locked-down production surface, limits on how expensive a single query is allowed to be, and errors sanitised before they leave the building.
Watched the bill as closely as the latency
Infrastructure lived in the Serverless Framework and AWS CDK, which meant cost was a thing you could read in a diff rather than discover at the end of the month. Most of the savings came from the unglamorous side: caching that removed redundant compute, and read patterns that stopped asking the database the same question twice.
What changed because I was there
Project and provider details are intentionally generalised; the responsibilities, trade-offs, and scale are representative.
- 01
Made the whole pipeline runnable locally
Built a containerised workspace with cloud-service emulation and live event inspection, turning cross-service debugging into a local feedback loop.
- 02
Made cross-repository work reproducible
Created a manifest-driven workspace with commit-pinned snapshots, so the team could reconstruct what shipped together without turning independent services into a monolith.
- 03
Carried an auth migration across service boundaries
Coordinated the application flow, configuration lifecycle, shared token handling, and delivery fallback while keeping old and new credentials interoperable during rollout.
- 04
Stopped configuration reads stampeding
Unified cache keys and coalesced concurrent reads, so overlapping serverless invocations shared one in-flight request instead of multiplying pressure upstream.
- 05
Proved the reporting path
Evaluated the event stream against near-real-time reporting needs and built a columnar analytics proof of concept that exposed the changes the pipeline needed.
- 06
Shipped contract changes end to end
Moved shared event fields and parsing fixes through multiple producers and consumers, placing reusable behavior in the contract layer rather than copying it service by service.