Módulo 7: Modernizar Legacy Code

Modernizar Syntax y Patterns Deprecated

Modernizar Syntax y Patterns Deprecated

Descripción de la cápsula

El scan del módulo anterior te dio el inventario. Esta cápsula te enseña a ejecutar cada tipo de modernización con Claude Code: de old Python a modern Python. Cada modernización es un commit separado con tests de regresión.


Modernización de Syntax

F-strings (Python 3.6+)

# Antes:
message = "Hello, %s! You have %d items." % (name, count)
message = "Hello, {}! You have {} items.".format(name, count)

# Después:
message = f"Hello, {name}! You have {count} items."

# Prompt a Claude Code:
> "Convierte todas las %-formatting y .format() strings
   en src/services/ a f-strings. No cambies strings que
   no tienen variables interpoladas."

Type Hints (Python 3.5+)

# Antes:
def get_users(active_only, limit):
    # ¿Qué tipos? ¿Qué retorna? No se sabe sin leer el código
    pass

# Después:
def get_users(active_only: bool, limit: int = 100) -> list[dict]:
    pass

# Prompt:
> "Agrega type hints a todas las funciones públicas en
   src/services/user_service.py. Infiere los tipos del
   uso actual en el código. Usa syntax moderna (list[str]
   en vez de List[str])."

Dataclasses (Python 3.7+)

# Antes:
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)

# Después (3 líneas reemplazan 12):
from dataclasses import dataclass

@dataclass
class Config:
    host: str
    port: int
    debug: bool = False

Pathlib (Python 3.4+)

# Antes:
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__))

# Después:
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+)

# Antes:
results = get_results()
if results:
    process(results)

match = pattern.search(text)
if match:
    handle(match.group())

# Después:
if results := get_results():
    process(results)

if match := pattern.search(text):
    handle(match.group())

Modernización de Patterns

Context Managers

# Antes:
try:
    file = open("data.txt")
    data = file.read()
    file.close()
except IOError:
    data = ""

# Después:
try:
    with open("data.txt") as file:
        data = file.read()
except FileNotFoundError:
    data = ""

Enums en vez de Magic Strings

# Antes:
status = "active"
if user["status"] == "active":
    pass
elif user["status"] == "inactive":
    pass
# Typo "acitve" pasa silenciosamente

# Después:
from enum import Enum

class UserStatus(Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    SUSPENDED = "suspended"

if user.status == UserStatus.ACTIVE:
    pass
# Typo UserStatus.ACITVE → error inmediato

Specific Exceptions

# Antes:
try:
    result = process_data(input)
except:                          # atrapa TODO, incluyendo KeyboardInterrupt
    return None                  # silencia errores

# Después:
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

Ejecutando con Claude Code

El prompt maestro de modernización

> "Moderniza src/services/order_service.py aplicando
   estos cambios (uno a la vez, confirma cada uno):
   
   1. %-formatting → f-strings
   2. os.path → pathlib
   3. bare except → specific exceptions
   4. manual __init__ classes → dataclasses (donde aplique)
   5. Agregar type hints a funciones públicas
   
   Para cada cambio:
   - Muestra antes y después
   - Confirma que los tests pasan
   - Haz un commit con mensaje descriptivo"

Conexión con Proyecto

En el Proyecto del Módulo (cápsula 05), ejecutas al menos 5 modernizaciones diferentes en un módulo legacy, cada una con test de regresión y commit separado.


Troubleshooting

Problema 1: F-string rompe string con backslashes

Solución: f-strings no permiten backslashes dentro de {}. Usa variable:

# Error:
f"Path: {path.replace('\\', '/')}"
# Fix:
cleaned = path.replace('\\', '/')
f"Path: {cleaned}"

Problema 2: Type hints rompen compatibilidad con Python 3.8

Solución: Usa from __future__ import annotations para syntax moderna en versiones viejas.

Problema 3: Dataclass no funciona con herencia compleja

Solución: No todo necesita ser dataclass. Si la clase tiene lógica compleja en init, mantenla como clase normal.


Ejercicios

Ejercicio 1: Modernizar syntax (Fácil)

Moderniza este código:

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()
Ver solución
from pathlib import Path
name = f"Hello {user}"
path = Path("/tmp") / "data.txt"
if path.exists():
    data = path.read_text()

De 6 líneas a 4, más seguro (context manager implícito) y más legible.

Ejercicio 2: Escribir prompt de modernización (Medio)

Escribe el prompt para que Claude Code modernice un archivo de 200 líneas de forma incremental (no todo de golpe).

Ver solución
> "Moderniza src/legacy_module.py de forma INCREMENTAL.
   Aplica un tipo de cambio a la vez en este orden:
   
   1. Primero: convierte %-formatting a f-strings.
      Muestra los cambios. Ejecuta tests.
   2. Segundo: reemplaza os.path con pathlib.
      Muestra los cambios. Ejecuta tests.
   3. Tercero: agrega type hints a funciones públicas.
      Muestra los cambios. Ejecuta tests.
   4. Cuarto: reemplaza bare except con specific exceptions.
      Muestra los cambios. Ejecuta tests.
   
   NO hagas todos los cambios a la vez.
   ESPERA confirmación entre cada paso."

Resumen

  • Syntax modernization: f-strings, type hints, dataclasses, pathlib, walrus operator
  • Pattern modernization: context managers, enums, specific exceptions
  • Claude Code convierte semánticamente, no solo busca-y-reemplaza
  • Un tipo de cambio por commit — nunca mezclar
  • Tests de regresión en cada paso

Próxima cápsula: Refactoring Incremental vs Big Bang.


Recursos Adicionales

  1. Python 3.12 What's New - Features más recientes
  2. pyupgrade - Automatiza modernización de syntax
  3. dataclasses - Python Docs - Referencia oficial
  4. pathlib - Python Docs - Referencia oficial
  5. PEP 484 - Type Hints - La PEP de type hints
  6. mypy - Type checker para verificar tus type hints