I Rewrote My Python Code Using Modern Python 3.12+ — Here’s What Changed

I thought my Python code was already clean and efficient… until I rewrote it using Python 3.12 and beyond. The result? Fewer lines, better…

I Rewrote My Python Code Using Modern Python 3.12+ — Here’s What Changed
Photo by Chris Anderson on Unsplash

You Won’t Believe What You’re Missing in Python Until You Upgrade

I Rewrote My Python Code Using Modern Python 3.12+ — Here’s What Changed

I thought my Python code was already clean and efficient… until I rewrote it using Python 3.12 and beyond. The result? Fewer lines, better readability, and smarter performance.

Python has come a long way from the early 3.x days. Over the past few versions, the language has evolved not just with syntax improvements, but with a philosophy shift — more expressive, more efficient, and sometimes even a bit more functional.

When Python 3.12 dropped, I decided to take a chunk of legacy code I wrote back in 3.6 and rewrite it using all the modern goodness now available in 3.10, 3.11, and 3.12.

I didn’t expect a total transformation.

But that’s exactly what happened.

In this article, I’ll walk you through:

  • What changed in my code
  • What new features I used
  • Why these changes made a real difference
  • What you should consider refactoring in your own codebase

Let’s jump in.


Pattern Matching Made My If-Else Blocks Vanish

Before:

def handle_event(event): 
    if event['type'] == 'click': 
        return handle_click(event) 
    elif event['type'] == 'hover': 
        return handle_hover(event) 
    else: 
        return handle_default(event)

After (Python 3.10+):

def handle_event(event): 
    match event: 
        case {'type': 'click'}: 
            return handle_click(event) 
        case {'type': 'hover'}: 
            return handle_hover(event) 
        case _: 
            return handle_default(event)
Easier to read — no more nested if/elif
Structured matching means fewer bugs
More declarative and expressive

Pattern matching isn’t just sugar — it fundamentally changes how you write decision logic, especially when dealing with dictionaries or nested data.

except* in Python 3.11 Helped Me Handle Parallel Exceptions Gracefully

If you’re using threads, asyncio, or concurrent tasks, you’ve probably hit situations where multiple exceptions are raised — and only the first one gets shown.

Now with except*, you can handle multiple exceptions separately.

async def main(): 
    try: 
        await asyncio.gather(task1(), task2()) 
    except* ValueError as ve_group: 
        for ve in ve_group.exceptions: 
            log(ve)

This is huge for concurrent code. It lets you reason about multiple failures without swallowing important errors.

F-strings Got Smarter — And I’m Not Going Back

Python 3.12 finally introduced f-string debugging with expressions inside curly braces. This means you can now do:

value = 42 
print(f"{value=}")  # Prints: value=42

It’s such a small change, but during debugging, it becomes an essential tool. No more concatenating variable names manually — Python does it for you.

Type Hints Went From Optional to Essential

My old codebase used types sporadically, mostly as comments. Today, using features like | (introduced in Python 3.10) and Self (in 3.11), I rewrote function signatures like this:

Before:

def get_data(user: Union[str, None]) -> Union[str, None]:

After:

def get_data(user: str | None) -> str | None:

And with Self:

class Config: 
    def set(self, key: str, value: Any) -> Self: 
        self.data[key] = value 
        return self

This isn’t just cleaner — it’s also more maintainable. Tools like mypy, pyright, and even your IDE can now catch issues before runtime.

tomllib Replaced Yet Another External Dependency

Before Python 3.11, if you wanted to parse a pyproject.toml, you had to install toml or tomli.

Now:

import tomllib 
 
with open("pyproject.toml", "rb") as f: 
    data = tomllib.load(f)

Less boilerplate. One less dependency. And it’s native.

Performance Gains Are Built Into the Language

While refactoring, I also noticed:

Function calls were faster thanks to 3.11’s optimizations
List comprehensions performed better
The overall interpreter speed improved by up to 25%

Even without changing your code, upgrading to Python 3.12 gives you free performance wins.

Soft Keywords Made Syntax Extensions Easier to Read

Python 3.10+ introduced soft keywords, which means Python now supports keywords that don’t conflict with variable names — opening doors to more flexible syntax.

match command: 
    case 'start': 
        start() 
    case 'stop': 
        stop()

No need to worry about match being a reserved keyword for years to come. It’s context-aware — a subtle, yet forward-thinking change in the language’s design.

Better Error Messages Changed How I Debug

Python 3.11+ has dramatically improved tracebacks.

Example:

TypeError: unsupported operand type(s) for +: 'int' and 'str' 
    --> in line 34: total = price + "5"

You now get precise file locations, code snippets, and contextual hints. It’s like having a linter baked into your exceptions.

I now fix bugs faster — and spend less time deciphering vague errors.


Conclusion: Upgrade Your Mindset, Not Just Your Python Version

What started as a quick refactor turned into a full-on mindset shift. Python today isn’t the same language it was just a few versions ago.

If you’re:

  • Still writing long if-else blocks
  • Avoiding type hints
  • Installing external packages for basic tasks
  • Not using match, Self, or f-string debugging…

…it might be time to rethink how you write Python.

Refactoring my old code wasn’t just about using the newest shiny features — it was about embracing Python the way it was meant to be used in 2025.


Final Thoughts

Next time you open an old script, ask yourself:

“How would I write this today — using the best tools Python has to offer?”

Odds are, your answer will lead you to cleaner, faster, and more Pythonic code.


If you found this helpful, follow me for more Python deep dives, refactoring tips, and software engineering insights. Let’s write better code — together.

Photo by Dhruv on Unsplash