Problem
ChargedUp ran Europe's largest phone-charging network, with kiosks in pubs, bars and clubs across the UK. When COVID-19 closed hospitality overnight in March 2020, the revenue behind those kiosks went to zero. The business goal was blunt: stand up a new revenue line before venues reopened, or run out of runway.
The company had two assets venues still valued: relationships with thousands of operators, and a team that could ship fast. The plan was contactless ordering: scan a QR code at the table, browse, pay, and the order lands at the bar. Venues needed it live for reopening. We had weeks, not months, and no second attempt.
Process
We started with a discovery week, not a build. Operators told us the thing that actually loses them money at reopening wasn't menus or payments. It was staff time spent walking orders back and forth under distancing rules. That reframed the scope. We cut a planned loyalty and rewards module entirely and focused every hour on one flow: scan, order, pay, print at the bar. Deciding what not to build is what let us hit the window.
I then led engineering across the apps that mattered: the customer order flow, the venue dashboard, an admin console, a menu builder, and the backend.
- A web app off the QR scan, not an app-store download. Shipping the customer experience as a PWA skipped store review entirely, the difference between launching for reopening and launching after it.
- One serverless backend, infra as code from day one. Lambda, API Gateway and DynamoDB with Terraform modules, so spinning up a new venue took minutes. That kept the per-venue cost of growth near zero.
- Weekly demos with the first operators. I ran the first five venues as a feedback loop, routing their comments straight into the product rather than a backlog.
I also handled the sponsor conversations, building co-branded configurations that let a national drinks brand put its offers in front of customers at the moment they were choosing a drink.
Outcome
The app went from nothing to live in 100+ venues in under eight weeks: a new revenue line before the old one could recover. Two national drinks brands, Red Bull and Jägermeister, signed on as launch sponsors, adding a second income stream on top of venue fees. The product outlasted lockdown and became a permanent line of business alongside the returning charging kiosks.
"James turned a shut-down business into a shipping product in the time most teams spend scoping. It kept us trading."
Founder
Architecture

For engineersTechnical Deep DiveExpand
Architecture
[QR scan] → /v/<venue-id>?t=<table-id>
↓
Static React PWA on CloudFront
↓
API Gateway + Lambda
↓
DynamoDB (single-table design)
↓
EventBridge → SQS → Bar printer worker
A single DynamoDB table stored venues, menus, tables, orders and sessions. Access patterns:
PK = VENUE#<id>for venue config and menu (SKdiscriminated byMENU#,TABLE#,ITEM#)PK = ORDER#<orderId>with a GSI onVENUE#<id>#OPENfor the venue dashboard's live order queue- TTL on session records to auto-expire abandoned baskets
Why serverless
The traffic profile fit Lambda perfectly: low baseline, bursty around opening hours and Friday/Saturday nights. Cold-start cost was near-zero because most of the customer flow was cached at CloudFront. The menu only hit the API on first scan, and order writes were async anyway.
Sponsor campaign isolation
Brand campaigns needed to feel like first-party experiences without forking the codebase. We modelled campaigns as a CAMPAIGN#<id> partition keyed against a venue, with theme tokens (colours, logo, hero copy) loaded into the React app at boot. A single feature flag toggled the campaign overlay on a venue's QR codes.
Trade-offs
- No native app. The PWA route was contentious. Venues wanted "an app", but app-store review timelines would have killed the launch window. We compensated with an "Add to Home Screen" prompt after the first order.
- Terraform vs CDK. Picked Terraform because the team already knew it. With hindsight CDK would have given us better TypeScript-typed infra alongside the rest of the codebase.
Lessons
Speed of iteration with operators mattered more than test coverage in the first two weeks. We added integration tests as the schema stabilised, not before.
