
Duration:
3 years
Role:
Backend Owner
Stack:
Rails 8 · PostgreSQL · Rails Event Store · Kafka · Sidekiq · OpenAPI
The campaign engine behind a personal-finance platform's partner rewards: it defines campaigns and gifts, receives partner-reported applications, and answers the commercial question nobody could answer precisely before: is this campaign actually profitable?
Profitability lived in spreadsheets, with no shared data model, no lifecycle and no reconciliation against what was actually paid out. And because a downstream operations platform consumed this engine's APIs, every contract decision had long-range consequences.
What I Built
A P&L subsystem from first principles: provider-reported applications, lump-sum monetising events and expiring forecasts, with transactional CSV ingestion and row-level error reporting
An incentive-cost reconciliation service: an event-sourced reactor books an approximate cost the moment a gift is issued, and an async job later reconciles it against actual purchase-order costs, with a feature-flag rollback path that got used
Claims lifecycle automation, including fixes for the race conditions that emerge when events arrive out of order
External API contracts designed additive-only, documented in OpenAPI in the same branch, always keyed on stable UUIDs rather than editable names
A Rails 8 and Ruby 3.4 upgrade delivered through staged rollouts with disciplined production reverts, plus a scheduled end-to-end regression suite with a documented scenario matrix
Engineering Judgment
API surface area was treated as a liability to be minimized: the default test for any new capability is the design that adds no new endpoints, no new auth scopes and no new routes. Contracts were designed additive-only against stable identifiers, which later allowed a parallel next-generation system to adopt them as its compatibility target.
Outcome
Campaign profitability became a queryable system instead of a spreadsheet ritual, and the engine's external contracts now anchor a downstream fulfilment platform and a next-generation campaign system.