case-studies / ev-charging-infrastructure
Case Study 01 · EV Charging Infrastructure
Untangling a national charging network
Every team was wiring directly into the same third-party APIs. One change could silently break three others.
Domain
EV Charging Infrastructure
Timeline
12 months
Role
Lead Architect & Engineer
Scale
1 → 7 sales channels
A national EV charging operator had outgrown its own architecture. What started as a single product had sprawled into multiple teams shipping against a shared monolith and a tangle of direct third-party integrations. The platform worked — until it didn't, in ways nobody could trace. The mandate was to make the system safe to change again, and to do it without pausing the business.
Multiple teams integrated independently against the same external systems — CRM, payments, identity, analytics. There was no single owner of any integration, so a contract change from one provider could break unrelated features with no warning and no clear blame surface. Observability was thin: when something failed in production, tracing the cause meant reading logs across services that didn't share a correlation model. The monolith underneath couldn't absorb the pace the business now demanded.
- ×Zero downtime — the charging network is live infrastructure people depend on
- ×Multiple autonomous teams already mid-flight on their own roadmaps
- ×Existing third-party contracts and rate limits could not be renegotiated
- ×Migration had to be incremental — no big-bang rewrite was acceptable
The core move was to stop letting every service talk to the outside world directly. I introduced a proxy service layer that owned each third-party relationship, exposed a stable internal contract, and absorbed provider-side churn. Services stopped integrating with vendors and started integrating with us. Asynchronous, event-driven communication via a message bus decoupled the services from each other so a slow or failing downstream couldn't cascade.
Consumer App
B2B Portal
Partner Sites
Onboarding Flow
API Management
Auth · routing · rate limiting
Domain Services
Async via Service Bus
Integration Proxy
Owns every 3rd-party contract
CRM
Payments
Identity
Analytics
Fig. 01 — highlighted nodes mark the components I designed and owned
Runtime
Messaging
Edge
Data
Ops
I owned the architecture end to end and wrote a large share of the code myself. That combination mattered: the design decisions were tested against the reality of implementing them, not handed off as a diagram. I also worked across the autonomous teams to migrate them onto the proxy layer one integration at a time, which was as much a coordination problem as a technical one.
Migrating live integrations without a maintenance window
Each third-party integration had to move from direct-access to proxy-mediated while traffic kept flowing. I used a strangler approach — the proxy shadowed real traffic first, then took over reads, then writes, with the old path kept warm as a fallback until confidence was high. No single switch was ever flipped on the whole system.
Designing for partial failure
In a distributed system, the question isn't whether a downstream fails but what happens when it does. I made failure modes explicit: idempotent message handling so retries were safe, dead-letter queues for messages that couldn't be processed, and circuit breakers so a struggling provider degraded one feature instead of taking down the platform.
Making the invisible traceable
A request might cross four services before touching an external system. I introduced a shared correlation model so a single identifier followed a request across every hop, turning 'something is slow' into a question that could actually be answered.
Centralizing a relationship — not just code — is the real win. The proxy's value wasn't technical elegance; it was giving every third-party integration a single owner.
Incremental migration is slower and almost always correct. The strangler pattern cost more upfront and saved the project from the risk no one could afford.
Observability isn't a feature you add later. If you can't trace a request end to end, you don't really understand your own system.