«Solo hay que añadir un método de pago; ¿por qué tarda tanto?»
Este artículo continúa el anterior Olor de código #4.
Es una pregunta frecuente del área de producto y también resulta incómoda de responder para los desarrolladores.
Porque en realidad hay doce lugares que modificar: añadir un caso al enum, cambiar cinco switch, la tabla de mapeo de iconos, las constantes de cadena, los nombres de eventos de analítica, la conversión de parámetros del servidor y los fixtures de prueba.
Cada cambio ocupa dos o tres líneas y no tiene nada de difícil. Lo difícil es encontrar los doce lugares sin omitir ninguno.
Martin Fowler da un nombre a esta situación en «Refactoring»: cirugía de escopeta (Shotgun Surgery) (Original del autor).
Por qué el nombre es preciso
Al disparar una escopeta, los perdigones se dispersan ampliamente. El término indica que un cambio aparece como pequeñas modificaciones repartidas por todo el código base.
Cada herida es superficial, pero son muchas, y omitir siquiera una provoca problemas.
El verdadero problema aparece al omitir una. Si corriges once de doce lugares y despliegas, el restante puede manifestarse solo bajo una condición concreta.
La pantalla de pago funciona, pero solo la de recibos muestra «método de pago desconocido». El coste de la cirugía de escopeta no es el tiempo de edición, sino la probabilidad de omisión.
Existe un olor especular
En la misma lista aparece el olor opuesto: cambio divergente (Divergent Change, llamado «cambio enredado» en algunas traducciones).
- Cambio divergente: un módulo cambia con frecuencia por varios motivos diferentes. Si cambia la base de datos, modificas este archivo; si cambian las reglas de pago, vuelves a modificarlo.
- Cirugía de escopeta: un cambio por un único motivo afecta a varios módulos (Artículo original sobre Shotgun Surgery).
Si dibujas la relación entre ambos, los ejes están invertidos.
| Cambio divergente | Cirugía de escopeta | |
|---|---|---|
| Responsabilidades y código | Varias responsabilidades en un lugar | Una responsabilidad en varios lugares |
| Síntoma | Este archivo cambia constantemente | Este cambio se propaga constantemente |
| Solución | Separar | Consolidar |
Es importante que las soluciones sean opuestas. Las conversaciones sobre olores de código suelen acabar en «sepáralo», pero la respuesta a la cirugía de escopeta es consolidar.
Si identificas mal el problema, la solución avanza exactamente en la dirección contraria.
Solo hay un eje que los distingue: el motivo del cambio.
Por eso el Principio de Responsabilidad Única se define no como «debe hacer una sola cosa», sino como «debe tener un único motivo para cambiar». El número de tareas depende de quién las cuente, pero los motivos del cambio pueden verificarse en el historial real de commits.
Medición mediante el historial de commits
Puedes usar datos en lugar de intuición. La cirugía de escopeta aparece como archivos que siempre cambian juntos.
Esto se denomina acoplamiento de cambios (change coupling) o acoplamiento lógico.
importLa clave es que se trata de un acoplamiento invisible en las relaciones. Si dos archivos no se referencian entre sí, pero siempre aparecen en el mismo commit, una regla no escrita en el código los mantiene unidos.
Cuenta las parejas de archivos que cambiaron juntas en los commits recientes para encontrar candidatos.
git log --format='%H' --since=6.months.ago | while read c; do
git show --format= --name-only "$c" | grep '\.swift$' | sort | \
awk 'NR==FNR{a[NR]=$0;n=NR} END{for(i=1;i<n;i++)for(j=i+1;j<=n;j++)print a[i]" + "a[j]}'
done | sort | uniq -c | sort -rn | head -20
Después comprueba si las parejas que aparecen arriba son realmente código que debería estar junto.
Por supuesto, algunas parejas cambian juntas de forma natural, como una implementación y su archivo de pruebas.
Lo que queda después de filtrarlas es lo que debes investigar.
Cómo consolidar
Consolidar las ramas condicionales dispersas en un solo tipo es la forma más común.
// Antes: el conocimiento de los métodos de pago switch está disperso por el proyecto
func iconName(for method: PaymentMethod) -> String {
switch method {
case .card: return "creditcard"
case .transfer: return "building.columns"
}
}
func displayName(for method: PaymentMethod) -> String { ... }
func serverCode(for method: PaymentMethod) -> String { ... }
Si estas tres funciones están en archivos distintos, cada nuevo método de pago exige buscar en tres lugares. Al consolidarlas en un tipo, solo queda un lugar.
// Después: solo hay que mirar en un lugar
extension PaymentMethod {
var iconName: String { ... }
var displayName: String { ... }
var serverCode: String { ... }
}
Deja que el compilador detecte las omisiones. En Swift, switch debe cubrir todos los casos.
Al añadir un caso al enum, todos los switch sin default producen un error de compilación. No elimina la cirugía de escopeta, pero traslada las omisiones del tiempo de ejecución al tiempo de compilación.
Como el coste real mencionado antes era la probabilidad de omisión, esto por sí solo cambia la situación.
Por eso no debes añadir habitualmente default: break a los switch que manejan enums. Es como cortar tú mismo esta red de seguridad.
Convierte las claves de cadena en tipos. Si los nombres de eventos de analítica, las claves de valores predeterminados del usuario y los nombres de notificaciones están dispersos como literales de cadena, el compilador no puede ayudarte.
Al reunirlos como constantes en un único lugar o envolverlos en un tipo, el punto de adición y modificación pasa a ser uno solo.
Convierte la configuración en datos. Si cada método de pago necesita un icono, un nombre y un código, puedes definirlos en un único array de structs en lugar de código disperso.
Añadir un método nuevo se reduce a agregar un elemento al array.
Si es difícil consolidar, deja al menos una señal. Hay casos que no pueden consolidarse físicamente.
Por ejemplo, cuando el servidor y el cliente deben compartir la misma regla.
En ese caso, añade comentarios que se remitan entre sí en cada punto, o una prueba que falle cuando los valores diverjan. Si dependes de la memoria humana, al menos deja escrito dónde hay que recordar.
Entre separar y consolidar
Aquí enlazamos con los artículos anteriores. Las soluciones para los olores de código suelen ir en dos direcciones, separar o consolidar, pero llevar cualquiera demasiado lejos crea otro olor.
- Separar para corregir el cambio divergente → código Ravioli
- Consolidar para corregir la cirugía de escopeta → objeto Dios
- Separar en capas → código Lasaña
Por eso no debes fijar como objetivo el tamaño o el número de piezas. Solo hay un objetivo.
Mantén juntas las cosas que cambian juntas y separadas las que cambian por separado. Eso es, en última instancia, lo que expresan la cohesión y el acoplamiento.
Cuando no estés seguro, puedes hacerte una pregunta: intenta recordar unas tres veces «por qué cambié este código la última vez».
Si el motivo fue distinto cada vez, toca separar; si corregiste varios lugares por el mismo motivo cada vez, toca consolidar.
Resumen
- La cirugía de escopeta es el estado en el que un cambio se dispersa en pequeñas modificaciones de varios archivos.
- El coste real no es el tiempo de edición, sino la probabilidad de omisión.
- Su olor opuesto, el cambio divergente, es el estado en el que un archivo cambia por varios motivos, y la solución también es opuesta. Uno se consolida y el otro se separa.
- Extraer del historial de commits las parejas de archivos que siempre cambian juntas permite medir los candidatos.
- En Swift, la comprobación exhaustiva de enums y
switchconvierte las omisiones en errores de compilación. No añadirdefaultpor costumbre es la forma de conservar esa red de seguridad.
El próximo artículo aborda la metáfora que hizo que todos estos olores recibieran un solo nombre: deuda técnica.
El significado original de Ward Cunningham no era «código desordenado».
Fuentes y criterios de verificación
- Refactoring — Martin Fowler · Original del autor · Verificado 2026-08-17 · Base: olores de código Shotgun Surgery y Divergent Change, y principios de refactorización
- The Shotgun Surgery Problem — Martin Fowler · Original del autor · Verificado 2026-08-17 · Base: casos de cirugía de escopeta en los que los cambios se dispersan entre varios módulos
Seguir leyendo
Serie de olores de código
- Artículo anterior: [Olor de código #4] Objeto Dios (God Object): guía completa
- Artículo anterior: [Olor de código #3] Código Ravioli: código cuyo flujo nadie entiende
- Artículo anterior: [Olor de código #2] Código Lasaña: por qué hay que modificar siete archivos

![Imagen de portada de [Olor de código #5] Cirugía de escopeta vs. cambio divergente](/assets/images/posts/d4a472ee-9fec-4ab5-9b61-0e9875b3dae5/shotgun-surgery-scattered-changes.jpg)