5 Versions Behind, 4 Databases to Merge: Upgrading Postgres with Ninad Pundalik

These are the highlights from an episode of Tern Stories. You can watch the full conversation with Ninad Pundalik on YouTube, Spotify, Apple), or wherever you get your podcasts.
When Postgres 11 approached end-of-life, a major travel company brought in site reliability engineer Ninad Pundalik to lead the upgrade. The setup: multiple Ruby on Rails apps, with multiple Postgres 11 instances on AWS RDS. No in-house infra team. No clear ownership. Some tables still in use, others long forgotten.
Despite the uncertainty, the migration went smoothly. The team cut over cleanly, avoided downtime, and ended up with a simpler, more reliable setup than they started with.
Rather than upgrading one version at a time, Ninad took a different approach: jump straight to Postgres 16, consolidate the databases onto a single node, and rebuild the infrastructure from scratch. That meant navigating unclear ownership, missing context, and infrastructure-as-code that had long since drifted.
As Ninad put it:
“Versions change, like seasons change. And eventually, support runs out.”
A brand new DB
The core job of a database—storing data—doesn’t change from version to version. The risk of upgrades stems from interacting with the data: query performance, downtime at cutovers, and unknown semantics changes. Starting from scratch gave them flexibility — and a clean slate.
Skipping 4 incremental major versions gave them the time and space to really understand how to work with Postgres 16. They revisited every parameter — from buffer sizes to replication settings — and tuned them based on actual workload behavior. Different workloads had landed in separate databases for performance reasons. Now, with better observability and newer defaults, they could safely consolidate them, versioning the changes in code with AWS CDK.
Who owns this data?
The migration wasn’t just about data. It was about decisions — which tables to keep, which to drop, and which to investigate more closely. Over the years, the database had accumulated layers: active workloads alongside stale analytics tables, legacy features, and even production test data.
Rather than migrate everything blindly, Ninad’s team took the time to understand what was actually in use. They traced query patterns. They talked with product engineers. They reviewed alerting rules and critical user flows. In a schema where ownership had faded and context had gone missing, this was the work of rediscovery.
That effort paid off. They were able to clean up confidently — deleting unused tables, trimming outdated rows, and migrating only what still mattered to the business.
One of the clearest takeaways from this part of the episode:
You can’t migrate what you don’t understand. And you can’t understand a schema without knowing what the business actually needs.
Reducing capacity to increase safety
As the team prepared for the final cutover, they made a deliberate, counterintuitive choice: scale things down.
To avoid the risk of traffic hitting both the old and new databases mid-deploy, they reduced the Kubernetes deployments to a single pod. Fewer replicas meant fewer threads, fewer connections, and—most importantly—no chance of inconsistent reads or writes across different sources of truth.
It wasn’t flashy, but it was careful. And it worked.
That kind of decision only makes sense when you understand the system end-to-end. Ninad and his team had traced the full path—from infrastructure and application behavior down to end-user impact. They knew which features were actively used, which could tolerate delay, and which could be safely paused.
And because the site was an additional, not primary, business channel, they kept it simple: a five-minute maintenance window, a static maintenance page, and a clean, quiet cutover.
Key Lessons from a Postgres Migration Done Right
- Sometimes the cleanest upgrade is a rebuild. Skip the version-by-version pain when you can, and design the right system for the version you’re moving to.
- Infrastructure needs to understand the product. Find the people who know the data, map what matters, and treat deletion as a first-class tool.
- Cutover planning is application design. Reducing capacity and accepting downtime isn’t a failure—it’s a strategy, when done with care.
Today, the new system runs on fewer nodes, tuned for actual traffic, with failover support and no more mystery tables. And the best part? It’s been stable ever since.
You can follow Ninad on Mastodon.