Un extraño arregló mi bug... pero arregló la mitad equivocada y funcionó

Un contribuyente corrigió un bug en un servidor MCP de Safari. Pero la mitad de su parche no hacía nada. Descubre la verdadera lección sobre invariantes

miércoles, 29 de julio de 2026 • 5 min de lectura • Equipo Q2BSTUDIO

La lección oculta en una corrección de código que parecía perfecta

Recibir una contribución externa siempre genera cierta expectación. Cuando un desarrollador ajeno a tu proyecto envía un pull request corrigiendo un bug que tú mismo no habías detectado, la primera reacción suele ser de gratitud. Pero cuando te sientas a analizar el código línea por línea, descubres que la mitad del parche no altera el comportamiento en absoluto, mientras que la otra mitad —la que el autor apenas mencionó— es la que realmente resuelve el fallo. Esa fue la experiencia que vivió el responsable de un servidor MCP para Safari, y que sirve como lección magistral sobre cómo la falta de documentación de invariantes puede convertir el código más inocente en una bomba de relojería silenciosa.

El escenario técnico es un protocolo de comunicación simple entre un proceso Node.js y un helper nativo de Swift. Las peticiones y respuestas se correlacionan por orden FIFO, sin identificadores únicos. Una cola de callbacks espera por las respuestas, y cada respuesta entrante se asigna al primer callback en la cola. Este diseño funciona perfectamente mientras cada solicitud enviada tenga una respuesta pendiente. Pero si alguna solicitud se abandona sin haber sido enviada realmente —por ejemplo, por un error de escritura en el pipe—, la cola se desincroniza de forma permanente. La respuesta de la siguiente llamada va a parar al callback zombie, y el verdadero solicitante se queda esperando hasta que salta el timeout. El síntoma nunca es un error, sino un cuelgue que puede pasar desapercindido durante horas.

El contribuyente, con buen criterio, identificó que era necesario gestionar esas callbacks huérfanas. Envió un parche que modificaba tres funciones idénticas reemplazando el callback abandonado por un consumidor vacío explícito. Eso parecía sensato, pero al analizar el código existente resultó que el callback original ya se protegía a sí mismo con un flag booleano (resolved), por lo que el reemplazo era funcionalmente idéntico. El verdadero arreglo estaba en el bloque catch de la escritura, donde el autor del PR no solo limpiaba el timeout sino que eliminaba la entrada de la cola con un splice. Esa línea, casi oculta en el diff, era la que impedía que el error de escritura envenenara todas las llamadas posteriores. El resto del parche era un comentario que compila.

Lo fascinante del caso no es solo que el colaborador acertara con la parte correcta a pesar de describir mal la solución, sino que el propio autor original del código había escrito el mismo patrón siete veces en el mismo archivo. En cuatro de esas siete funciones incluyó una línea defensiva —un consumidor vacío para respuestas tardías— pero sin documentar por qué era necesaria. En las otras tres, esa línea simplemente no estaba, aunque el bug de escritura sí existía en todas ellas. La ausencia de una función centralizada que encapsulara el invariante obligaba a recordar una regla no escrita cada vez que se añadía una nueva llamada al helper. El resultado era una ruleta rusa: cuatro de cada siete veces el desarrollador recordaba aplicar la defensa cosmética, pero el fallo real —la gestión del error de escritura— nunca se abordó en ninguna de las siete.

En Q2BSTUDIO, empresa especializada en aplicaciones a medida, sabemos que el código duplicado no es solo un problema estético. Cada copia es una oportunidad para que el siguiente desarrollador —incluso tú mismo meses después— tenga que reconstruir mentalmente las reglas de negocio o los invariantes del sistema. Cuando esas reglas no están documentadas, el riesgo de introducir errores sutiles se multiplica. Y cuando el sistema maneja estados compartidos que trascienden una sola llamada, como ocurre en muchos procesos de automatización o en sistemas de agentes IA, un fallo en la sincronización puede tener consecuencias catastróficas sin que nadie levante una alarma.

La lección va más allá de este caso concreto. El protocolo sin IDs es solo un ejemplo de cómo los diseños simples pueden ocultar complejidades ocultas. En el mundo de la ciberseguridad, por ejemplo, un error de desincronización en la autenticación puede derivar en accesos no autorizados. En entornos cloud AWS/Azure, las colas de mensajes mal gestionadas pueden provocar pérdidas de datos. Incluso en herramientas de BI/Power BI, la correlación incorrecta de consultas puede generar informes con datos equivocados. La raíz del problema es siempre la misma: un invariante no escrito que todos asumen pero que nadie ha formalizado.

El verdadero arreglo, tanto en el caso del servidor MCP como en cualquier proyecto de software, no es aplicar un parche puntual, sino refactorizar para que la regla quede explícita en un solo lugar. Una función _helperRequest que centralice la lógica de timeout, error de escritura y limpieza de cola elimina la dependencia de la memoria humana. El colaborador corrigió tres instancias, pero la deuda técnica seguía viva en la estructura del archivo. El autor original asumió la responsabilidad de hacer esa refactorización, reconociendo que el verdadero bug no estaba en las líneas que faltaban, sino en la ausencia de un lugar donde esas líneas debieran estar.

Este caso también nos recuerda que las pruebas automatizadas no siempre cubren los escenarios de fallo más relevantes. Las 54 pruebas del proyecto pasaban todas, pero ninguna simulaba una rotura del pipe de comunicación porque ese test no se escribe hasta que el bug ya ha ocurrido. En Q2BSTUDIO, al desarrollar soluciones de software llave en mano, insistimos en incorporar pruebas de integración que ejerciten los caminos de error y los estados compartidos entre llamadas. Solo así se puede reducir la brecha entre lo que el código debería hacer y lo que realmente hace cuando las condiciones son adversas.

Al final, la anécdota del pull request que arregló la mitad equivocada nos deja una reflexión valiosa: en el desarrollo de software, especialmente cuando trabajamos con arquitecturas distribuidas, colas, agentes autónomos o sistemas en tiempo real, documentar los invariantes no es opcional. Es tan importante como el propio código. Y si además logramos encapsular esa lógica en una única función o módulo, estaremos protegiendo a todos los futuros mantenedores —incluido nosotros mismos— de cometer el mismo error una y otra vez. Porque, como demuestra esta historia, la memoria humana falla; el código bien diseñado no debería.

¿UNA PAUSA?

Juega un momento antes de irte

NUESTROS SERVICIOS

Cómo podemos ayudarte

¿Tienes un proyecto en mente?

Cuéntanos tu visión y la convertimos en una solución de software. Sea cual sea el alcance, hacemos realidad tu idea.