Holiday Weather: off Heroku, onto serverless AWS — and 4× faster at the tail
An undocumented Java "microservice" estate on Heroku became a TypeScript serverless platform on AWS. Median response time fell from 334 ms to 207 ms and the slowest request from 2.3 s to 0.3 s.
- Service
- AWS configuration & scaling
- Industry
- Travel media
- Client
- Holiday Weather ↗
- Year
- 2020
Holiday Weather — a site serving weather forecasts and averages for holiday destinations — ran as a set of Java/Spring "microservices" on Heroku with no documentation, tangled cross-dependencies and little support from the original team. Every change was risky, and the 90th-percentile response time was over 1.3 seconds.
We proposed a lift-and-shift to AWS ECS first, then made the case for a rewrite instead — TypeScript on Lambda, DynamoDB with streams, SQS, S3 and a Redis cache in front of the site — and built it. Weather feeds arrive by FTP on a schedule, locations are managed through an admin API, and every stage is observable in CloudWatch.
The new platform serves a median request in 207 ms (was 334 ms) and its slowest request in 302 ms (was 2,290 ms) — and once it was stable, we replaced the inherited FTP downloads with the provider's API.
The starting point
Holiday Weather gives UK holidaymakers live weather, forecasts and monthly averages for hundreds of destinations — Majorca, Tenerife, the Maldives, Cancún. Behind the site sat a Java (Spring) application hosted on Heroku, split into “microservices” that were more tangled than modular: undocumented, cross-dependent, backed by Redis, and supported only sporadically by the team that had built it.
For the owners this meant three problems at once: nobody could safely change the system, the platform cost more to run than it should, and users felt it — the 90th-percentile response time was 1.3 seconds and the slowest requests took over two.
Two proposals
Proposal one — lift and shift. Containerise the existing Java services and run them on AWS ECS. Lowest risk on paper, but it would have moved the undocumented complexity to a new bill without removing it.
Proposal two — rewrite for the platform. Re-implement the data pipeline and API in TypeScript on AWS Lambda, store locations and forecasts in DynamoDB and use DynamoDB Streams to fan out changes, decouple the stages with SQS, archive raw feeds to S3, and keep a Redis cache in front of the website. More work up front; a system the client’s team could actually understand and operate afterwards.
The client chose the rewrite.
The final architecture
| Stage | What runs |
|---|---|
| Ingest | Forecast data pulled from the provider’s API (originally scheduled downloads of zipped files over FTP — see below) |
| Locations | An admin API defines which destinations exist; they live in DynamoDB |
| Processing | Lambda functions parse the feeds, filter them against the location list and push work onto SQS |
| Storage | Processed forecasts in DynamoDB; raw files archived to S3 |
| Serving | The website reads from a Redis cache that is refreshed from the pipeline |
Everything is infrastructure-as-code and every stage reports to CloudWatch, so a bad feed or a slow provider shows up as a graph, not a customer complaint.
The numbers
Benchmarked with the same load against both platforms (ApacheBench, connection times in ms):
| Heroku (old) | AWS (new) | |
|---|---|---|
| Mean total | 521 | 216 |
| Median | 334 | 207 |
| 90th percentile | 1,316 | 297 |
| 99th percentile | 2,290 | 302 |
| Longest request | 2,290 | 302 |
The tail is the story: the slowest request on the new platform is faster than the median on the old one.
And then we asked the right question
The pipeline still began with scheduled downloads of zipped files from the data provider’s FTP server — because that was how it had always been done. Once the platform was stable we went back to the provider and asked what else they offered. They had a proper API. We replaced the FTP polling with direct API integration: forecasts arrive as they are published instead of on a timer, the ingest code shrank to a fraction of its size, and one more moving part disappeared from the on-call list.
The lesson we carry into every migration since: rebuild the platform, but also question the inputs you inherited.

A consultation with one of our engineers
Tell us what you are dealing with. Within two working days you get a concrete proposal with scope and price.
- Reply from a senior engineer or lead auditor, not an account manager
- Fixed scope and price before any work starts
- ISO 9001 and ISO 27001 certified processes, NDA on request