Módulo 4: Refactoring Multi-File Coordinado
Move Module y Actualización de Imports
Move Module y Actualización de Imports
Descripción de la cápsula
Rename y extract cambian nombres y lógica, pero la estructura de directorios queda igual. Move module es el refactoring que reorganiza esa estructura: mover un archivo de un directorio a otro, dividir un módulo en varios archivos, o consolidar archivos dispersos en un solo módulo. Y cada movimiento requiere actualizar todos los imports que apuntan al viejo location.
En proyectos reales, la estructura de directorios se degrada con el tiempo. Un directorio utils/ crece hasta tener 30 archivos. Un services/ mezcla lógica de negocio con infraestructura. Un archivo que empezó en src/ debería estar en src/api/. Move module corrige esto — pero manualmente es uno de los refactoring más propensos a errores porque un solo import olvidado rompe todo en runtime.
Claude Code maneja esto porque puede leer todos los imports del proyecto, entender la estructura de dependencias, y actualizar cada reference coherentemente. En esta cápsula vas a reorganizar estructura de directorios con confianza.
Por Qué Mover Módulos es Difícil
El problema de los imports
# Estructura actual (desorganizada):
# src/
# utils/
# email_helper.py # ← debería estar en services/
# payment_calculator.py # ← debería estar en services/
# string_utils.py # ← está bien aquí
# auth_middleware.py # ← debería estar en middleware/
# services/
# order_service.py
# user_service.py
# payment_calculator.py es importado por:
# src/services/order_service.py: from utils.payment_calculator import calculate_total
# src/api/routes/checkout.py: from utils.payment_calculator import calculate_total
# src/api/routes/invoices.py: from utils.payment_calculator import PaymentCalculator
# src/tasks/billing.py: from utils.payment_calculator import calculate_total
# tests/test_payment.py: from utils.payment_calculator import calculate_total
# tests/test_orders.py: from utils.payment_calculator import PaymentCalculator
# Mover payment_calculator.py a services/ requiere actualizar
# 6 imports en 6 archivos diferentes
Un import olvidado no genera error de syntax — genera ModuleNotFoundError en runtime. Si ese import está en un path de código poco frecuente, el bug puede llegar a producción.
Move Simple: Un Archivo
El ciclo completo con Claude Code
Paso 1: Tests
> "Ejecuta todos los tests y confirma que pasan antes
de mover payment_calculator.py"
Paso 2: Identificar impacto
> "Lista todos los archivos que importan payment_calculator.
Para cada uno, muestra la línea de import exacta."
# Output esperado:
# 1. src/services/order_service.py:3 → from utils.payment_calculator import calculate_total
# 2. src/api/routes/checkout.py:5 → from utils.payment_calculator import calculate_total
# 3. src/api/routes/invoices.py:4 → from utils.payment_calculator import PaymentCalculator
# 4. src/tasks/billing.py:2 → from utils.payment_calculator import calculate_total
# 5. tests/test_payment.py:1 → from utils.payment_calculator import calculate_total
# 6. tests/test_orders.py:3 → from utils.payment_calculator import PaymentCalculator
Paso 3: Mover y actualizar
> "Mueve src/utils/payment_calculator.py a
src/services/payment_calculator.py.
Actualiza todos los imports en los 6 archivos
que lo importan. Los imports deben cambiar de
'from utils.payment_calculator' a
'from services.payment_calculator'."
# Claude Code:
# 1. Mueve el archivo
# 2. Actualiza los 6 imports
# 3. Verifica que no hay más references al viejo path
Paso 4: Verificar
> "Ejecuta los tests y también haz un grep de
'from utils.payment_calculator' para confirmar
que no quedan references al viejo path"
# Output esperado:
# ✅ Tests: 47 passed
# ✅ grep: 0 results (no quedan references viejas)
Move Complejo: Reorganizar Directorio
Escenario: utils/ tiene 30 archivos
# Antes (desorganizado):
# src/utils/
# string_utils.py
# date_utils.py
# email_helper.py # → services/email/
# sms_helper.py # → services/notifications/
# payment_calculator.py # → services/billing/
# invoice_generator.py # → services/billing/
# auth_middleware.py # → middleware/
# cors_middleware.py # → middleware/
# rate_limiter.py # → middleware/
# logger.py # → infrastructure/
# cache.py # → infrastructure/
# db_connection.py # → infrastructure/
# ... (18 archivos más)
# Después (organizado):
# src/utils/ → Solo utilities puras (string, date, etc.)
# src/services/ → Lógica de negocio
# src/middleware/ → Middleware HTTP
# src/infrastructure/ → Logging, cache, DB
Plan de reorganización con Claude Code
# Paso 1: Analizar y planificar
> "Analiza todos los archivos en src/utils/ y clasifícalos
en categorías: utils puras, services, middleware, e
infrastructure. Para cada archivo, sugiere el directorio
destino y lista cuántos archivos importan cada uno."
# Output esperado:
# Utils puras (quedan): string_utils.py, date_utils.py (4 importers)
# Services (mover a src/services/): email_helper.py (8), payment_calculator.py (6), ...
# Middleware (mover a src/middleware/): auth_middleware.py (3), cors.py (2), ...
# Infrastructure (mover a src/infrastructure/): logger.py (12), cache.py (7), ...
# Paso 2: Mover en orden de menor a mayor impacto
> "Empieza por los archivos con menos importers.
Mueve cors_middleware.py a src/middleware/cors.py.
Actualiza los 2 imports. Ejecuta tests."
# Paso 3: Repetir para cada archivo
> "Mueve auth_middleware.py a src/middleware/auth.py.
Actualiza los 3 imports. Ejecuta tests."
# Paso 4: Los de mayor impacto al final
> "Mueve logger.py a src/infrastructure/logger.py.
Actualiza los 12 imports. Ejecuta tests."
Regla: mueve un archivo a la vez, ejecuta tests después de cada movimiento. Nunca muevas 5 archivos de golpe.
Move con Re-export (Backwards Compatibility)
Cuándo usarlo
Si el módulo es público (otros proyectos lo importan) o si quieres hacer la migración gradual, puedes dejar un re-export en el viejo location:
# src/utils/payment_calculator.py (archivo viejo, ahora re-exporta)
"""
DEPRECATED: Movido a src/services/payment_calculator.py
Este archivo existe solo por backwards compatibility.
Eliminar cuando todos los consumidores hayan actualizado sus imports.
"""
from services.payment_calculator import * # re-export todo
import warnings
warnings.warn(
"Importar de utils.payment_calculator está deprecated. "
"Usa services.payment_calculator en su lugar.",
DeprecationWarning,
stacklevel=2
)
# Prompt a Claude Code:
> "Mueve payment_calculator.py a src/services/.
En el viejo location, deja un archivo que re-exporta
todo desde el nuevo location con un DeprecationWarning.
Actualiza los imports internos del proyecto al nuevo path.
Los imports externos (si existen) seguirán funcionando
con el re-export."
Move con Split: Un Archivo → Múltiples
Cuándo splitear
Cuando un archivo tiene 500+ líneas y contiene múltiples clases/funciones con responsabilidades diferentes:
# src/services/user_service.py (600 líneas, 3 responsabilidades)
class UserAuthService: # Autenticación
def login(self): ...
def logout(self): ...
def reset_password(self): ...
class UserProfileService: # Perfil
def get_profile(self): ...
def update_profile(self): ...
def upload_avatar(self): ...
class UserNotificationService: # Notificaciones
def send_welcome(self): ...
def send_password_reset(self): ...
Ejecutar el split con Claude Code
> "Divide src/services/user_service.py en 3 archivos:
1. src/services/user_auth_service.py — UserAuthService
2. src/services/user_profile_service.py — UserProfileService
3. src/services/user_notification_service.py — UserNotificationService
Para cada clase:
- Mueve la clase y sus imports al nuevo archivo
- Actualiza todos los imports en el proyecto
Si algún archivo importaba múltiples clases de user_service.py,
actualiza a imports separados de cada nuevo archivo.
Ejecuta los tests después de cada movimiento."
Move con Merge: Múltiples Archivos → Uno
Cuándo consolidar
Lo opuesto del split: archivos de 20-30 líneas que deberían ser un solo módulo:
# Antes (fragmentado):
# src/validators/
# email_validator.py (25 líneas, 1 función)
# phone_validator.py (30 líneas, 1 función)
# age_validator.py (20 líneas, 1 función)
# name_validator.py (25 líneas, 1 función)
# Después (consolidado):
# src/validators/
# validators.py (100 líneas, 4 funciones)
> "Consolida los 4 archivos de validators en
src/validators/validators.py. Mueve todas las funciones
al archivo consolidado. Actualiza todos los imports.
Elimina los archivos viejos. Ejecuta tests."
Comparación: Move Manual vs Claude Code
| Criterio | Manual | Claude Code |
|---|---|---|
| Encontrar todos los imports | grep + revisión manual | Automático y completo |
| Actualizar imports | Find-and-replace (propenso a errores) | Semántico y contextual |
| Imports relativos | Fácil olvidar .. paths | Calcula automáticamente |
| Re-exports | Escribir manualmente | Genera con deprecation warning |
| Verificación | Ejecutar tests manualmente | Ejecuta y reporta |
| Split/Merge | Tedioso, muchos pasos | Coordinado en un prompt |
Conexión con Proyecto
En el Proyecto del Módulo (cápsula 06), si el codebase tiene problemas de organización (archivos en directorios incorrectos, utils/ inflado, naming inconsistente), move module es una de las técnicas que vas a usar. Combina con rename (cápsula 02) y extract (cápsula 02) para una reorganización completa.
Troubleshooting
Problema 1: ModuleNotFoundError después del move
Causa: Quedó un import apuntando al viejo path.
Solución:
> "Busca cualquier import que todavía apunte a
'utils.payment_calculator' (el viejo path).
Actualiza al nuevo path 'services.payment_calculator'."
Problema 2: Import circular después de mover
Causa: El nuevo directorio crea una dependencia circular que no existía antes.
Solución: Analiza antes de mover:
> "Si muevo payment_calculator.py a services/,
¿se creará alguna dependencia circular? Analiza
los imports de payment_calculator y los imports
de los archivos en services/."
Problema 3: __init__.py no actualizado
Causa: El directorio destino tiene un __init__.py que no exporta el nuevo módulo.
Solución:
> "Después de mover el archivo, actualiza también
el __init__.py del directorio destino para exportar
las clases/funciones del nuevo módulo."
Problema 4: Paths relativos se rompen
Causa: El archivo movido usaba imports relativos que ya no son válidos.
Solución:
> "Actualiza los imports DENTRO del archivo movido.
Los imports relativos (from . import X) pueden
haber cambiado al cambiar de directorio."
Ejercicios
Ejercicio 1: Planificar un move (Fácil)
src/helpers/email_sender.py necesita moverse a src/services/email/sender.py. Escribe los 4 pasos del ciclo con prompts para Claude Code.
Ver solución
# Paso 1: Tests
> "Ejecuta tests y confirma que pasan."
# Paso 2: Impacto
> "Lista todos los archivos que importan email_sender
desde src/helpers/. Muestra la línea de import exacta."
# Paso 3: Mover
> "Crea el directorio src/services/email/ con __init__.py.
Mueve src/helpers/email_sender.py a
src/services/email/sender.py. Actualiza todos los imports
de 'from helpers.email_sender' a 'from services.email.sender'."
# Paso 4: Verificar
> "Ejecuta tests. También busca 'helpers.email_sender'
en todo el proyecto para confirmar 0 references viejas."
Ejercicio 2: Reorganizar utils/ (Medio)
Tu src/utils/ tiene estos 8 archivos. Clasifícalos y propón la estructura destino:
string_utils.py, date_utils.py, db_connection.py,
redis_cache.py, auth_check.py, rate_limiter.py,
csv_exporter.py, pdf_generator.py
Ver solución
# Clasificación:
# Utils puras (quedan en utils/):
# string_utils.py, date_utils.py
# Infrastructure (mover a infrastructure/):
# db_connection.py, redis_cache.py
# Middleware (mover a middleware/):
# auth_check.py, rate_limiter.py
# Services (mover a services/):
# csv_exporter.py, pdf_generator.py
# Estructura final:
# src/
# utils/ → string_utils.py, date_utils.py
# infrastructure/ → db_connection.py, redis_cache.py
# middleware/ → auth_check.py, rate_limiter.py
# services/ → csv_exporter.py, pdf_generator.py
Criterio: utils puras = sin side effects, sin I/O. Infrastructure = conexiones externas. Middleware = intercepta requests. Services = lógica de negocio.
Ejercicio 3: Diseñar un split (Medio)
src/services/app_service.py tiene 800 líneas con 4 clases: AuthService, UserService, OrderService, NotificationService. Escribe el prompt para Claude Code que haga el split.
Ver solución
> "Divide src/services/app_service.py en 4 archivos:
1. src/services/auth_service.py — AuthService + sus imports
2. src/services/user_service.py — UserService + sus imports
3. src/services/order_service.py — OrderService + sus imports
4. src/services/notification_service.py — NotificationService + sus imports
Para cada clase:
- Incluye solo los imports que esa clase necesita
- Actualiza todos los imports en el proyecto que apuntaban
a app_service.AuthService → auth_service.AuthService (etc.)
- Si alguna clase depende de otra del mismo archivo,
agrega el import al nuevo archivo
Después de mover las 4 clases, elimina app_service.py.
Ejecuta tests después de cada movimiento individual."
Ejercicio 4: Move con backwards compatibility (Difícil)
Tu módulo src/utils/logger.py es importado por 15 archivos internos y 3 proyectos externos. Diseña un plan de migración que no rompa los proyectos externos.
Ver solución
# Fase 1: Mover con re-export
> "Mueve src/utils/logger.py a src/infrastructure/logger.py.
En src/utils/logger.py, deja un archivo que:
1. Re-exporta todo desde infrastructure.logger
2. Emite DeprecationWarning al importar
3. Tiene un comentario 'Eliminar después de v2.0'
Actualiza los 15 imports internos al nuevo path.
Los 3 proyectos externos seguirán funcionando con el re-export."
# Fase 2: Comunicar a proyectos externos
# (fuera de Claude Code: enviar PR/notificación a los 3 proyectos)
# Fase 3: Eliminar re-export (después de que los externos migraron)
> "Elimina el archivo de re-export src/utils/logger.py.
Verifica que ningún import interno lo usa."
Clave: re-export + DeprecationWarning permite migración gradual sin breaking changes.
Resumen
En esta cápsula aprendiste:
- Move module reorganiza la estructura de directorios actualizando todos los imports
- El ciclo es: tests → identificar impacto → mover → verificar — un archivo a la vez
- Move simple: mover un archivo y actualizar N imports
- Move con re-export: backwards compatibility para consumidores externos
- Split: dividir un archivo grande en múltiples especializados
- Merge: consolidar archivos pequeños en un módulo coherente
- Claude Code encuentra todos los imports incluyendo relativos,
__init__.py, y paths dinámicos - Mover de menor a mayor impacto reduce riesgo
Próxima cápsula: Interface Changes y Propagación. Vas a aprender el refactoring más complejo: cambiar la firma de una función y propagar a todos los consumers.
Recursos Adicionales
- Python Import System - Documentación oficial del sistema de imports de Python
- PEP 328 - Imports Multi-Line and Absolute/Relative - La PEP que define imports absolutos y relativos
- Refactoring Guru - Move Method/Class - Explicación visual del move refactoring
- importlib - Python Docs - Para entender cómo Python resuelve imports
- isort - Python Import Sorter - Herramienta para organizar imports automáticamente
- absolufy-imports - Convertir imports relativos a absolutos