Module 7: Modernize Legacy Code
Modernizing Deprecated Syntax and Patterns
Modernizing Deprecated Syntax and Patterns
Capsule description
The scan from the previous module gave you the inventory. This capsule teaches you to execute each type of modernization with Claude Code: from old Python to modern Python. Each modernization is a separate commit with regression tests.
Syntax Modernization
F-strings (Python 3.6+)
# Before:
message = "Hello, %s! You have %d items." % (name, count)
message = "Hello, {}! You have {} items.".format(name, count)
# After:
message = f"Hello, {name}! You have {count} items."
# Prompt to Claude Code:
> "Convert all the %-formatting and .format() strings
in src/services/ to f-strings. Don't change strings that
don't have interpolated variables."
Type Hints (Python 3.5+)
# Before:
def get_users(active_only, limit):
# What types? What does it return? You can't tell without reading the code
pass
# After:
def get_users(active_only: bool, limit: int = 100) -> list[dict]:
pass
# Prompt:
> "Add type hints to all the public functions in
src/services/user_service.py. Infer the types from the
current use in the code. Use modern syntax (list[str]
instead of List[str])."
Dataclasses (Python 3.7+)
# Before:
class Config:
def __init__(self, host, port, debug=False):
self.host = host
self.port = port
self.debug = debug
def __repr__(self):
return f"Config(host={self.host}, port={self.port})"
def __eq__(self, other):
return (self.host == other.host and
self.port == other.port and
self.debug == other.debug)
# After (3 lines replace 12):
from dataclasses import dataclass
@dataclass
class Config:
host: str
port: int
debug: bool = False
Pathlib (Python 3.4+)
# Before:
import os
file_path = os.path.join(base_dir, "config", "settings.json")
if os.path.exists(file_path):
with open(file_path) as f:
data = json.load(f)
parent_dir = os.path.dirname(os.path.abspath(__file__))
# After:
from pathlib import Path
file_path = Path(base_dir) / "config" / "settings.json"
if file_path.exists():
data = json.loads(file_path.read_text())
parent_dir = Path(__file__).parent.resolve()
Walrus Operator (Python 3.8+)
# Before:
results = get_results()
if results:
process(results)
match = pattern.search(text)
if match:
handle(match.group())
# After:
if results := get_results():
process(results)
if match := pattern.search(text):
handle(match.group())
Pattern Modernization
Context Managers
# Before:
try:
file = open("data.txt")
data = file.read()
file.close()
except IOError:
data = ""
# After:
try:
with open("data.txt") as file:
data = file.read()
except FileNotFoundError:
data = ""
Enums instead of Magic Strings
# Before:
status = "active"
if user["status"] == "active":
pass
elif user["status"] == "inactive":
pass
# The typo "acitve" passes silently
# After:
from enum import Enum
class UserStatus(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
SUSPENDED = "suspended"
if user.status == UserStatus.ACTIVE:
pass
# The typo UserStatus.ACITVE → immediate error
Specific Exceptions
# Before:
try:
result = process_data(input)
except: # catches EVERYTHING, including KeyboardInterrupt
return None # silences errors
# After:
try:
result = process_data(input)
except ValueError as e:
logger.warning(f"Invalid input: {e}")
return None
except ConnectionError as e:
logger.error(f"Connection failed: {e}")
raise
Executing with Claude Code
The master modernization prompt
> "Modernize src/services/order_service.py applying
these changes (one at a time, confirm each one):
1. %-formatting → f-strings
2. os.path → pathlib
3. bare except → specific exceptions
4. manual __init__ classes → dataclasses (where it applies)
5. Add type hints to public functions
For each change:
- Show before and after
- Confirm that the tests pass
- Make a commit with a descriptive message"
Connection with the Project
In the Module Project (capsule 05), you execute at least 5 different modernizations in a legacy module, each with a regression test and a separate commit.
Troubleshooting
Problem 1: An f-string breaks a string with backslashes
Solution: f-strings don't allow backslashes inside {}. Use a variable:
# Error:
f"Path: {path.replace('\\', '/')}"
# Fix:
cleaned = path.replace('\\', '/')
f"Path: {cleaned}"
Problem 2: Type hints break compatibility with Python 3.8
Solution: Use from __future__ import annotations for modern syntax in older versions.
Problem 3: Dataclass doesn't work with complex inheritance
Solution: Not everything needs to be a dataclass. If the class has complex logic in init, keep it as a normal class.
Exercises
Exercise 1: Modernize syntax (Easy)
Modernize this code:
import os
name = "Hello %s" % user
path = os.path.join("/tmp", "data.txt")
if os.path.exists(path):
f = open(path)
data = f.read()
f.close()
See solution
from pathlib import Path
name = f"Hello {user}"
path = Path("/tmp") / "data.txt"
if path.exists():
data = path.read_text()
From 6 lines to 4, safer (implicit context manager) and more readable.
Exercise 2: Write a modernization prompt (Medium)
Write the prompt for Claude Code to modernize a 200-line file incrementally (not all at once).
See solution
> "Modernize src/legacy_module.py INCREMENTALLY.
Apply one type of change at a time in this order:
1. First: convert %-formatting to f-strings.
Show the changes. Run tests.
2. Second: replace os.path with pathlib.
Show the changes. Run tests.
3. Third: add type hints to public functions.
Show the changes. Run tests.
4. Fourth: replace bare except with specific exceptions.
Show the changes. Run tests.
Do NOT make all the changes at once.
WAIT for confirmation between each step."
Summary
- Syntax modernization: f-strings, type hints, dataclasses, pathlib, walrus operator
- Pattern modernization: context managers, enums, specific exceptions
- Claude Code converts semantically, not just find-and-replace
- One type of change per commit — never mix
- Regression tests at each step
Next capsule: Incremental vs Big Bang Refactoring.
Additional Resources
- Python 3.12 What's New - The most recent features
- pyupgrade - Automates syntax modernization
- dataclasses - Python Docs - Official reference
- pathlib - Python Docs - Official reference
- PEP 484 - Type Hints - The type hints PEP
- mypy - A type checker to verify your type hints
Module 7, Capsule 03 — Refactoring & Legacy Code with Claude Code Guide