The task landed on my desk as one line: fifty-plus AWS Lambda functions running Python 3.8, and the runtime was months from end of life. On paper, an AWS Lambda Python 3.8 end-of-life runtime upgrade is a version bump. In reality, once AWS stops patching a deprecated runtime, every unpatched security vulnerability becomes yours to own. Nobody had touched these functions in over a year. The collective wisdom was three words: "They work. Don't touch them."

Famous last words.

Opening the time capsules

I started where every honest audit starts — CloudWatch. Invocation frequency, error rates, last-modified dates. Anything that hadn't fired in 90 days went on the deletion-candidate list, because the fastest migration is the function you delete. Then I opened the survivors.

It wasn't a Python upgrade. It was archaeology. Every function was a little time capsule of "I wrote this at midnight and never came back." Here's what was actually inside:

  • Hardcoded credentials — not even environment variables, raw strings sitting in source. One held database credentials from 2019.
  • Deprecated libraries with known CVEs, pinned to nothing.
  • Zero tests across all fifty-plus functions. Not a thin suite. Zero.
  • IAM roles with Action: "*", Resource: "*" — wide open, every one of them.
  • Functions talking to RDS over the open internet, no VPC isolation.
  • Documentation that was the function name. That was it.

Why not a big bang

The tempting move is to rewrite everything, flip the switch, and pray. I've seen that movie. Instead I went one function at a time.

Each function got the same treatment: runtime moved from 3.8 to 3.11, every dependency pinned and audited against known CVEs, and shared libraries pulled out into Lambda layers so the next update happens in one place instead of fifty. Then I wrote tests — for most of these, the first tests they'd ever had — and documentation that explained why the function existed, not just what it did. The "why" is the part the next engineer actually needs at 3 AM.

Rollout was canary deployments with traffic shifting: 5 percent, then 25 percent, then full rollover, with automatic rollback if error rates spiked. No big bang. No prayers. If a function misbehaved on 5 percent of traffic, it never reached the other 95.

Security hardening in the same pass

Since I was already inside every function, it would have been malpractice to fix the runtime and leave the rest rotting. So the hardening ran in parallel:

  • IAM rewritten from scratch to least privilege. No more wildcards. Each role got exactly the permissions its function used and nothing else.
  • Hardcoded credentials moved to Secrets Manager, rotated and pulled at runtime.
  • Database-connected functions moved into a VPC with private subnets, so RDS stopped being reachable from the open internet.

The results

Fifty-plus functions later:

  • Every function on a supported runtime.
  • Zero downtime during the migration.
  • Zero wildcard IAM policies remaining.
  • Zero hardcoded secrets.
  • Actual tests. Docs the next engineer won't curse me for.

And a small gift I didn't expect: cold starts dropped, because Python 3.11 is simply faster than 3.8. The boring maintenance chore quietly made the platform quicker.

"Don't touch it" is fear, not strategy

Here's the thing serverless people forget. "Serverless" doesn't mean maintenance-free — it means the maintenance is invisible until the runtime hits end of life and nothing works. There's no server to point at, so the debt hides even better.

"Don't touch it" is technical debt spoken in the language of fear. Every day you ignore a runtime at end of life, the interest compounds — until it comes due as a security incident or a 3 AM page. Every "don't touch it" system in your stack is a future page waiting to happen.

So pick one this week. The oldest, scariest, best-left-alone system you've got. Open it. Read it. Test it. You'll either find it's fine and sleep better, or you'll find the 2019 credentials before an attacker does.

What's the oldest untouched system in your stack right now?