Over the past few years, I’ve been through several projects - each full of talented, passionate developers.
But often… I was the only one asking the uncomfortable questions:
Why aren’t we using TypeScript?
Why are we still running Node 12 - which should already rest in peace?
Why are we keeping this one ancient library that blocks every upgrade?
Why don’t we bump the framework version?
And you know what?
Most developers didn’t argue. They knew. They were painfully aware of the problem.
But they couldn’t do anything - because “business doesn’t have time or budget for refactor.”
Except… sometimes those “good enough” technologies are not good enough at all.
They’re ticking time bombs, quietly waiting to go off.
As I mentioned earlier, my talk at js-poland.pl is coming up soon, and I’ll be speaking about migrating legacy code to modern frameworks.
And… I might’ve gotten a bit too obsessed with the topic lately. 😅
My previous post, “jQuery will outlive half of today’s JavaScript frameworks - here’s why”, sparked so many thoughtful comments and DMs that I felt I had to continue the conversation.
So here’s the next part - about the human side of migration.
The Real Problem: Communication, Not Competence
Product owners often don’t understand the true scale of the problem - not because they’re ignorant, but because we don’t speak their language.
When a dev says “we have tech debt” or “this is a security risk,” a non-technical PO hears: “They want to rewrite things and delay my roadmap.”
Meanwhile, you, the engineer, are thinking:
“If we don’t touch this soon, it’s going to blow up in production - and it’ll be on us.”
And that’s the truth: you are responsible for your code.
If a security patch stops arriving, or the product gets hacked, or the client walks away because of outdated tech - it’s not the PO who’ll get blamed. It’s you.
How to Make Product Owners Actually Listen
“It's outdated” doesn’t land.
You need to show the business impact, not just the tech reason.
Here’s a small cheat sheet for translating dev talk into business talk:
| Technical problem | Business impact | Measurable signal |
|---|---|---|
| No TypeScript | +15–30% slower code reviews, more regressions | PR cycle time, bug count |
| Node EOL | Security risk, potential client audit failure | SAST/DAST scan results |
| Old framework | No vendor support, growing migration cost | Slower velocity, merge time |
You don’t need fancy dashboards - use what you already have in your CI/CD or analytics tools.
Trends are more powerful than numbers anyway.
The 1-Minute Pitch to Your PO
Problem: “We’re running Node 12, which no longer receives security patches. That increases audit and compliance risk.”
Impact: “We’re spending roughly 12h/month maintaining compatibility and losing potential enterprise clients who require supported runtimes.”
Plan: “We can fix this over 3 sprints - half a day per sprint. No feature freeze, no rewrite.”
Success metric: “Green security scan, reduced bug count, and faster onboarding.”
Mitigation: “If a library breaks, we’ll use a local shim and toggle rollback.”
This structure works because it speaks in outcomes and risks, not code.
It turns a refactor from ‘tech work’ into ‘risk management’ - which is exactly what it is.
Small Wins: The Incremental Path
Modern migrations don’t need big-bang rewrites.
You can upgrade incrementally, with a rollback plan at every step.
TypeScript in a JS project
- Start with
allowJsandcheckJs. - Add JSDoc types in critical files.
- Gradually raise
strictsettings each sprint. - Document patterns for newcomers - onboarding eats most of your ROI.
Framework upgrades
- Use the Strangler Pattern - new features go into “the new world,” while old ones stay stable.
- Use feature toggles and canary deploys.
- Add contract tests to ensure both worlds behave the same.
Node / runtime updates
- Test the new version in CI first (matrix build).
- Swap out blocking libs or shim them temporarily.
- Deploy to 5% of traffic before the full switch.
A Tiny ROI Example (use this in your slide deck)
- Current cost: ~12h/month fixing regressions due to missing types.
- Investment: 24h adding TypeScript incrementally.
- Payback: 3 months. After that - pure savings.
You don’t need perfect data. Even rough numbers change the tone of the conversation.
Definition of Done for a Migration
✅ Unit + contract tests pass in CI
✅ Rollback plan tested in staging
✅ Documentation updated
✅ Performance + error metrics steady
✅ Team knows how to build on the new setup
Make “migration tasks” as small as feature tickets.
Refactors don’t need to be scary - just scoped and measurable.
Quick Sprint Checklist
- Enable
checkJsincore/price. - Add JSDoc or TS types for public API.
- Tighten ESLint or TS rules one step.
- Add contract tests for
/ordersendpoints. - Introduce feature toggle
uiV2.orders.enabled. - Canary release to 5% of users.
- Update README with “How to migrate this module.”
This approach builds trust - your PO sees progress without losing delivery speed.
“But what if I get fired for doing this?”
Honestly? You probably won’t.
In fact, you’ll look better when the first question comes from a client or auditor:
“Which version of Next.js are you on?”
“Do you still use Node 12?”
You’ll be the one who has the answer.
The one who manages risk instead of waiting for it.
What about you?
Have you ever tried to convince your PO to approve a refactor - or did you just do it quietly and hope no one noticed? 😅
I’d love to hear your stories (or scars) in the comments. 💜
Top comments (0)