Mobile ticketing for transport networks
Instant System · since May 2023 · in production
I joined a team with no mobile developer at all, alone on the ticketing scope of the whole portfolio — for a year. Here are five workstreams, each with the problem that triggered it and what it produced.
01Authentication that logged people out for no reasonInherited code nobody wanted to touch — rebuilt on actors
Problem
When several requests received an unauthorised response at the same time, each one fired its own token refresh. The concurrent refreshes invalidated one another, and the user ended up signed out for no apparent reason. The authentication code dated back to 2019 and nobody wanted to touch it.
Decision
I took the initiative on the rebuild, moving to async/await with an actor holding the authentication state.
Why an actor rather than a lock: a lock protects state provided you remember to take it everywhere — nothing checks that, and a lock held across an await is a deadlock waiting to happen. With an actor, isolation becomes a property of the type: every access from outside is necessarily serialised, and forgetting is no longer possible.
The heart of the fix: the in-flight refresh task is remembered. Concurrent requests do not start a new one, they await the same one. A single refresh, however many simultaneous calls — plus a guard against retry loops on the same call.
Result
Authentication on TCL became markedly more stable. No more unexplained sign-outs — the kind you never reproduce in testing and only ever see in production.
02QR code fraudAn innovation-sprint initiative that became a paid feature
Problem
A digital ticket is displayed as a QR code. Fraudsters would take a screenshot and pass it on — and at inspection, nothing told the copy apart from the original.
Decision
During an innovation sprint, I took the initiative of designing an internal library that masks the screen content as soon as a capture or a recording is under way. The ticket stays readable to its holder, and disappears from anything that could be shared.
Result
Rolled out across every app, then adopted on the new platform. Clients are pleased with it — to the point that it became a feature they pay for. A sprint idea turned into a line of revenue.
03Card top-up on the Lyon networkFrom proof of concept to production, built almost single-handedly
Problem
The Lyon network had been waiting to let riders top up a transport card from their phone. On iOS, everything was still to be done: the ticketing vendor's SDK had never been integrated.
Decision
I first proved the integration with a POC — a POC should not show that it works, it should show what will break in production. Then I built almost the entire feature on iOS single-handedly, through to release.
Result
Shipped. I am now one of the technical leads on the TCL app, which I know inside out, and I am driving the m-ticket launch on it.
04Carrying clean architecture all the wayThe product owns the default, the vendor overrides only its difference
Problem
The per-vendor ticketing abstraction had been laid down by the tech lead. But implementations that were entirely vendor-independent still lived inside the vendor modules — duplicated once per supplier.
Decision
I created the module for a new vendor even though it had few specifics: keeping it outside would have been quicker, but would have left a hole in the architecture. End-to-end consistency was worth the extra code.
And I argued for — and obtained — lifting vendor-independent implementations up to the product. Vendors now override only what they actually mean to change.
Result
Less duplication between suppliers, and a clear rule that holds without arbitration: the product owns the default behaviour, the vendor overrides only its difference.
05Launching the new platformOùra and Tuscany, across every functional area
Problem
The new product platform, in clean architecture with dependency injection, had to carry the whole of ticketing — with no regression against a platform proven over years.
Decision
I worked across every functional area: purchase catalogue, cart, parameterised products, purchase history, beneficiary management, buying on someone's behalf. And I wrote the internal QR code generation library from an encoded payload — the building block behind ticket validation and inspection.
Result
Platform launched on Oùra (Auvergne-Rhône-Alpes) and in Tuscany. That is where I learned SwiftUI, dependency injection and this architecture — in production, on a shipped product.




