Selected case studies
The interesting part is usually the decision that came before the code.
These are simplified versions of real production work. Partner details and proprietary implementation details are intentionally omitted; the problems, decisions and outcomes are the useful part of the story.
01
Performance40 → 200 RPS: improving the existing platform before scaling out.
The cashback platform was getting busier, and scaling out looked like the obvious answer. Before scaling out, the first step was to understand what the existing instances were actually spending time on.
InvestigationJVM and memory behavior, cache utilization, database work, asynchronous paths, threads and logging.
Engineering changesJVM tuning, Caffeine + Redis multi-level caching, query optimization and asynchronous processing.
ResultThroughput improved from 40 to 200 requests/sec per instance, while production instances dropped from 14 to 8.
Engineering takeawayMeasure first. It is easy to buy more hardware; it is harder to find the constraint. In this case, the constraint was solvable in the application.
02
Platform architectureConfiguration became a product feature.
Cashback, lending, NPS and referral programmes kept asking for slightly different behavior. Releasing code for every variation would have made the platform harder to change over time.
NeedBusiness rules and campaign behavior had to change frequently without turning every change into another application release.
ApproachReusable business-rule and campaign-management capabilities, with the right variability represented as configuration.
Scale110+ concurrent campaigns and approximately 8M eligibility evaluations per day.
Engineering takeawayGood configuration has a boundary. The goal is to make predictable change cheap, not to turn every business decision into a giant rules engine.
03
Integration architectureOne framework, 35 banking partners.
Partner integrations had common steps but partner-specific differences around certificates, encryption, validation and communication. That variation was placed behind a reusable framework.
ProblemEvery new bank could otherwise create another round of custom integration and serviceability work.
ApproachA common integration and onboarding flow with partner variation handled through configuration and reusable execution components.
ResultThe framework supported 35 banking partners and multiple TPAP integrations.
Engineering takeawayWhen integrations share a shape, a good framework should remove repetition without pretending that every partner behaves identically.
04
Production troubleshootingThe application was slow. The database wasn’t the problem.
A slowdown showed up during peak traffic. There were no obvious exceptions or database alarms, and CPU and memory looked normal. The clue came from looking at thread dumps and Grafana together.
SignalRequest threads were waiting in the logging pipeline while Grafana showed elevated disk I/O wait.
Root causeThe primary Logback appender used neverBlock=false. Under higher traffic, the logging queue could fill and request threads could block while writing logs.
FixLogging configuration was changed and application performance recovered.
Engineering takeawayProduction performance is a system problem. The bottleneck can sit in a subsystem that was never part of the original business flow.
05
Operational automationStop asking someone to remember the campaign schedule.
A campaign that was meant to run during a recurring time window initially needed someone to activate and deactivate it manually.
ProblemManual activation and deactivation created recurring operational work around the start and end of each campaign window.
ApproachFive parameters captured recurring-active state, start time, end time, frequency and frequency calculation.
ResultDaily, weekly and monthly recurrence patterns could be expressed through configuration, removing the manual step.
Engineering takeawaySometimes the best engineering improvement is not another service. It is making the system smart enough to remove a task that people keep repeating.
06
Preventive engineeringA cache miss we wanted to avoid.
Offer constructs lived in Redis with an approximately two-hour TTL. The concern was what could happen if the cache expired for many requests at roughly the same time during a busy period.
RiskMany requests could miss Redis together and hit the database at once when the TTL expired.
OptionsDistributed locking was considered, but slow database responses could leave application threads waiting.
DecisionA simple hourly cron on one server triggered an API that refreshed offers into Redis with a two-hour TTL.
Important contextThis was preventive work, not a confirmed production incident. The choice favored simple, predictable operations over adding distributed locking complexity.
Internal endpoints, certificates, keys, partner details and proprietary architecture are intentionally omitted.