Puedes operar una infraestructura de clase mundial y aun así perder toda la misión porque un número “parecía correcto”.
No explotó, no produjo NaN, no ardía. Simplemente estaba mal en silencio: unidades equivocadas, suposición errónea, interfaz rota,
hasta que la física hace la autopsia por ti.
Mars Climate Orbiter es el caso canónico: una nave se perdió porque una parte del sistema produjo datos en unidades imperiales
mientras otra esperaba métricas. Si estás pensando “eso no pasaría en mi organización”,
felicidades: tienes exactamente la mentalidad que hace que pase en tu organización.
Qué ocurrió realmente (y por qué importó)
Mars Climate Orbiter (MCO) se lanzó en 1998 y debía estudiar el clima marciano: atmósfera, polvo,
vapor de agua, cambios estacionales. También servía como repetidor de comunicaciones para su sonda hermana. Nunca
llegó a hacer nada de eso. Durante la inserción en órbita de Marte en septiembre de 1999, la nave voló demasiado bajo, probablemente
encontró fuerzas atmosféricas para las que no estaba diseñada y se perdió. O bien se quemó o bien se escapó hacia una órbita solar; en operaciones,
“perdida” es una forma educada de decir “no tenemos idea de dónde está y no podemos comunicarnos con ella”.
La causa raíz, en términos de ingeniería simples: el equipo de navegación usó datos de rendimiento de propulsores
expresados en pound-force segundos, mientras que el software de navegación esperaba newton segundos. Mismo concepto físico
(impulso). Diferente sistema de unidades. Ese desajuste produjo un sesgo consistente en las estimaciones de trayectoria.
La nave no hizo algo repentinamente loco. Hizo algo consistentemente equivocado por suficiente tiempo como para que
el error fuera terminal.
Y aquí está la lección operativa que la mayoría pasa por alto: no fue “un error tipográfico”. Fue una falla de disciplina
en la interfaz. El sistema tuvo múltiples oportunidades para detectarlo: revisiones, comprobaciones, tendencias de telemetría,
simulación, respuesta a anomalías. La falla no fue un solo bug; fue una pila de decisiones no-bug que mantuvieron vivo el bug.
Chiste #1: Los desajustes de unidades son los únicos bugs que pueden arreglarse tanto con un compilador como con una cinta métrica.
Datos rápidos y contexto histórico
- Parte del programa Mars Surveyor ’98: MCO iba en par con Mars Polar Lander, buscando un ritmo de “más rápido, mejor, más barato”.
- Lanzada en un cohete Delta II: Un vehículo fiable; el lanzamiento en sí no fue el problema.
- Pérdida durante la inserción en órbita: La fase más arriesgada: una ventana estrecha en la que la precisión de la navegación determina la supervivencia.
- El desajuste implicó datos de impulso: Las pequeñas correcciones de trayectoria de la nave dependían de modelado preciso de los encendidos de los propulsores.
- Imperial vs métrico: Específicamente, pound-force segundos (lbf·s) frente a newton segundos (N·s). Un lbf equivale a aproximadamente 4.44822 N.
- La tendencia operativa era visible: Los residuales de navegación y las predicciones de trayectoria derivaban; el sistema estaba “desviado” de una forma que debería haber demandado una pausa y verificación.
- El control de interfaces importa: La expectativa de unidades estaba documentada; la aplicación y la validación no lo estuvieron.
- La presión del programa fue real: Las restricciones de coste y calendario redujeron la redundancia y volvieron culturalmente aceptable el “lo arreglaremos después”.
Anatomía del fallo: dónde el sistema mintió
El número no estaba equivocado; el significado sí
En trabajo de confiabilidad existe una categoría especial de fallos: nonsense calculado correctamente. Las matemáticas están bien.
El software “funciona”. La instrumentación reporta algo consistente. Y el operador lo interpreta con una suposición compartida equivocada.
Eso no es un bug en una línea de código. Es un bug en el contrato entre equipos, herramientas y la realidad.
Los desajustes de unidades son especialmente molestos porque son plausibles. Un impulso de propulsor de “10” podría ser 10 N·s
o 10 lbf·s. Ninguno parece obviamente absurdo. Y si el sistema downstream espera uno y recibe el otro,
el error escala linealmente—sin picos dramáticos, sin caos inmediato, solo un sesgo persistente.
El sesgo vence al ruido
Los equipos de operaciones están entrenados para buscar picos: cambios súbitos, umbrales, presupuestos de error que se rompen en minutos.
El sesgo es más lento y paciente. El sesgo puede permanecer dentro de tolerancias durante días mientras consume tu margen. Parecerá
“estamos un poco desviados, pero aún dentro de la variación esperada”, hasta que sales del corredor durante la única maniobra que importa.
Las interfaces son donde la confiabilidad va a morir
El código más peligroso en cualquier sistema es el que “solo” transforma datos entre dos subsistemas. La capa de mapeo.
El job ETL. El adaptador. El script que reescribe un campo. El envoltorio que “arregla” una API. Ahí es donde desaparece la información de tipo,
donde la semántica se pasa por alto, donde la cobertura de pruebas es escasa y donde el “todos saben” se convierte en sustituto de la verificación.
En MCO, la interfaz entre el modelado de empuje/datos de operaciones y el software de navegación efectivamente despojó las semánticas hasta dejar solo números.
Los humanos “sabían” las unidades. El sistema no. Cuando los humanos rotaron, o las suposiciones derivaron, el sistema no tenía líneas de defensa.
Una cita de confiabilidad que vale la pena poner en la pantalla
Gene Kranz (director de vuelo de la NASA) dijo: “Failure is not an option.” Si lo has visto impreso en pósters,
también has visto gente usarlo como sustituto de la ingeniería. Aquí está la versión adulta: la falla siempre es una opción.
Tu trabajo es hacer que sea caro para que fallos pequeños se vuelvan catastróficos.
Transferencias, interfaces y el “límite de unidades”
Hablemos de lo que “desajuste de unidades” realmente significa en una organización moderna. No es solo métrico vs imperial. Es cualquier
desajuste semántico:
- milisegundos vs segundos
- bytes vs kibibytes
- UTC vs hora local
- rangos inclusivos vs exclusivos
- valores con signo vs sin signo
- tasas por segundo vs por minuto
- tamaños comprimidos vs sin comprimir
- “customer_id” con significado de cuenta vs usuario vs hogar
Estos fallos cruzan límites de equipo. Eso importa porque los equipos optimizan localmente. Un equipo que entrega un componente puede
documentar unidades y seguir adelante. El equipo receptor puede leer esa documentación una vez, integrar y después fiarse de la memoria.
La memoria no es un control operativo.
Los ICD no son papeleo; son restricciones ejecutables
Los programas espaciales usan Documentos de Control de Interfaz (ICD) para especificar cómo hablan los sistemas: formatos, tiempos, unidades, tolerancias.
En sistemas empresariales, a menudo tenemos lo mismo en formas débiles: especificaciones OpenAPI, esquemas protobuf, ejemplos JSON,
contratos de datos, runbooks, “conocimiento tribal”. El modo de falla es el mismo cuando esos artefactos no se hacen cumplir mediante pruebas
y verificaciones en tiempo de ejecución.
Si tu especificación de interfaz no está probada, no es una especificación. Es un cuento para dormir.
Por qué las revisiones no detectan esto de forma fiable
A la gente le encanta decir: “¿Cómo lo pasó la revisión de código?” Porque la revisión es un proceso de muestreo humano, no un sistema de prueba.
Los desajustes de unidades son un error semántico. El código puede parecer perfectamente razonable. El revisor puede no tener el contexto completo,
puede no conocer la unidad upstream, puede no notar que una variable llamada impulse debería ser impulse_ns.
Las revisiones ayudan, pero no hacen cumplir. La aplicación viene de:
- tipos con unidades o convenciones de nombres explícitas
- pruebas de contrato entre productor y consumidor
- afirmaciones y validaciones en tiempo de ejecución
- simulación de extremo a extremo en condiciones realistas
- comprobaciones de sanidad de telemetría con alarmas afinadas para sesgo, no solo picos
Cómo se ve esto en sistemas de producción
Si manejas almacenamiento, flotas o plataformas de datos, ya has vivido alguna versión de MCO. El desajuste de unidades se vuelve:
- un SLO de latencia medido en milisegundos, pero el panel lo muestra en segundos
- un limitador de tasa configurado en solicitudes/minuto, pero el cliente asume solicitudes/segundo
- una retención de backup “30” interpretada como días por un job y como horas por otro
- un sistema de almacenamiento reportando “GB” (decimal) mientras finanzas espera “GiB” (binario)
La moraleja no es “usa el sistema métrico”. La moraleja es: haz que las unidades sean explícitas y verificables en cada límite.
Chiste #2: La forma más rápida de reducir el gasto en la nube es tratar por accidente milisegundos como segundos—hasta que el CFO vea tu informe de incidente.
Playbook de diagnóstico rápido
Cuando sospeches un desajuste de interfaz/unidades/semántica, no empieces reescribiendo código ni añadiendo reintentos. Los reintentos
son como convertir un valor equivocado en el mismo valor equivocado más rápido. En su lugar, realiza un diagnóstico en tres pasadas que priorice
“qué cambió” y “qué suposiciones no están verificadas”.
Primero: prueba el contrato en el límite
- Captura una carga real (trama de telemetría, petición/respuesta API, registro de archivo) desde producción.
- Compárala contra la especificación (esquema, ICD, definición protobuf, expectativas de unidades).
- Verifica unidades y marcos de referencia: base de tiempo, sistema de coordenadas, factores de escala, compresión.
Si la carga no autodescribe sus unidades, asume que ya estás en peligro.
Segundo: busca sesgo, no solo picos
- Grafica residuales (predicción vs observación) a lo largo del tiempo.
- Busca deriva monotónica o un offset consistente tras un despliegue o cambio de configuración.
- Compara múltiples señales independientes si es posible (p. ej., estimación de navegación vs sensor bruto, métrica derivada por API vs conteo de logs crudo).
Tercero: reproduce con una entrada “conocida buena”
- Encuentra un fixture: una carga histórica, dataset dorado, secuencia de maniobra registrada.
- Ejecuta la canalización/componente de extremo a extremo.
- Confirma que la salida coincide con lo esperado dentro de tolerancia.
Si no puedes definir “esperado”, tu sistema no es testeable y la confiabilidad se convierte en un pasatiempo de apuestas.
Tareas prácticas: comandos, salidas, decisiones
A continuación hay tareas prácticas que puedes ejecutar en sistemas Linux reales para detectar desajustes de unidad/contrato, deriva y “números sin
significado”. Cada tarea incluye un comando, lo que significa la salida y la decisión que tomarás.
Task 1: Find where units are implied (grep for suspicious fields)
cr0x@server:~$ rg -n "ms|millis|seconds|secs|ns|newton|lbf|pound|unit|scale|multiplier|conversion" /etc /opt/app/config
/opt/app/config/telemetry.yaml:41: impulse_scale: 1.0
/opt/app/config/telemetry.yaml:42: impulse_unit: "lbf*s"
Qué significa: Tu configuración dice explícitamente lbf·s. Bien—al menos está escrito.
Decisión: Verificar que cada consumidor espere la misma unidad; añadir una afirmación de inicio que rechace cadenas de unidad desconocidas.
Task 2: Check service build/version skew across a fleet
cr0x@server:~$ ansible -i inventory all -m shell -a 'myservice --version'
host-a | CHANGED | rc=0 >>
myservice 2.3.1
host-b | CHANGED | rc=0 >>
myservice 2.2.9
Qué significa: Versiones mixtas. Si el manejo de unidades cambió entre versiones, podrías tener semántica en estado de split-brain.
Decisión: Congelar despliegues; forzar convergencia a una versión; ejecutar pruebas de contrato contra el nodo más antiguo aún en ejecución.
Task 3: Inspect OpenAPI/protobuf/schema for explicit unit annotations
cr0x@server:~$ jq '.components.schemas.Telemetry.properties.impulse' openapi.json
{
"type": "number",
"description": "Thruster impulse",
"example": 12.5
}
Qué significa: “Impulse” sin unidades es una trampa. El ejemplo no ayuda.
Decisión: Actualizar descripciones del esquema para incluir unidades; añadir la extensión x-unit o renombrar campos (p. ej., impulse_ns).
Task 4: Validate a payload against schema (catch missing/extra fields)
cr0x@server:~$ python3 -m jsonschema -i sample_payload.json telemetry_schema.json
sample_payload.json validated successfully
Qué significa: La forma estructural está bien. Esto no valida semánticas como unidades.
Decisión: Añadir validación semántica: rangos aceptables y etiquetas de unidad; considerar rechazar cargas que no incluyan metadatos de unidad.
Task 5: Spot scale-factor drift in time series (quick-and-dirty)
cr0x@server:~$ awk '{sum+=$2; n++} END{print "avg=",sum/n}' impulse_residuals.txt
avg= 4.43
Qué significa: El residual promedio está sesgado (no centrado cerca de cero). Eso huele a desajuste de unidad/escalado.
Decisión: Compara la magnitud del sesgo con factores de conversión conocidos (p. ej., ~4.448 para lbf→N). Si coincide, deja de suponer y analiza el límite de unidades.
Task 6: Check NTP/time sync (time base mismatches mimic unit bugs)
cr0x@server:~$ timedatectl
Local time: Wed 2026-01-22 10:48:11 UTC
Universal time: Wed 2026-01-22 10:48:11 UTC
RTC time: Wed 2026-01-22 10:48:11
Time zone: UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Qué significa: La hora está sana y en UTC. Bien. Los desajustes de unidades a menudo se esconden detrás de la deriva temporal.
Decisión: Si no está sincronizado, arregla la hora primero; de lo contrario malinterpretarás causalidad y correlación.
Task 7: Confirm log timestamps and units in structured logs
cr0x@server:~$ jq -r '.ts,.duration_ms' /var/log/myservice/events.json | head
2026-01-22T10:46:01.120Z
250
2026-01-22T10:46:01.421Z
280
Qué significa: Los nombres de campo de duración incluyen “_ms”. Es un pequeño milagro.
Decisión: Hacer cumplir convenciones de sufijos (_ms, _bytes, _ns) mediante linting y checklist de revisión de código.
Task 8: Trace an API call end-to-end to see unit translation points
cr0x@server:~$ curl -sS -H 'X-Request-ID: diag-123' http://localhost:8080/telemetry | jq .
{
"impulse": 12.5,
"impulse_unit": "lbf*s",
"burn_duration_ms": 250
}
Qué significa: La carga autodescribe unidades para impulse pero no necesariamente para todo lo demás; la duración es explícita.
Decisión: Asegurar que todos los campos que llevan unidades sean explícitos; si los consumidores ignoran impulse_unit, trátalo como un bug y rompe la build.
Task 9: Confirm which config is live (avoid “fixed in git, broken in prod”)
cr0x@server:~$ systemctl show myservice -p FragmentPath -p Environment
FragmentPath=/etc/systemd/system/myservice.service
Environment=UNIT_SYSTEM=imperial IMPULSE_UNIT=lbf*s
Qué significa: El servicio está explícitamente configurado en imperial vía entorno. Eso puede sobreescribir valores por defecto del código.
Decisión: Rastrear la procedencia de la configuración; si existe una bandera de sistema de unidades, bloquearla y alertar sobre cambios.
Task 10: Identify recent changes (units break at the edges of change)
cr0x@server:~$ git log -n 5 --oneline -- config/telemetry.yaml
a91b2cf set impulse scale
71c0d1a rename burn_duration to burn_duration_ms
9f10bb2 initial telemetry config
Qué significa: Alguien tocó el escalado de impulse recientemente. Ese es tu principal sospechoso.
Decisión: Vincula el cambio con la línea temporal del incidente; exige una prueba que demuestre que la nueva escala coincide con las reglas físicas/negocio esperadas.
Task 11: Check metric queries for unit confusion (Prometheus example)
cr0x@server:~$ promtool query instant http://localhost:9090 'rate(myservice_impulse_total[5m])'
myservice_impulse_total{job="myservice"} 0.21
Qué significa: Se devuelve una tasa, pero la unidad es poco clara. ¿Es N·s por segundo? ¿lbf·s por segundo? ¿Solo “impulsos”?
Decisión: Codifica unidades en nombres de métricas y en HELP; p. ej., myservice_impulse_newton_seconds_total y rechaza métricas ambiguas en revisión.
Task 12: Verify dashboard axis units (Grafana JSON check)
cr0x@server:~$ jq -r '.panels[] | select(.title=="Burn duration") | .fieldConfig.defaults.unit' dashboard.json
ms
Qué significa: El panel usa explícitamente milisegundos. Eso evita que un operador vea “250” y lo interprete como “250 segundos”.
Decisión: Estandariza unidades de panel; haz cumplir vía CI en exportaciones JSON de dashboards.
Task 13: Detect silent scaling changes in binaries (strings can betray you)
cr0x@server:~$ strings /usr/local/bin/myservice | rg -n "lbf|newton|N\\*s|unit"
10233:IMPULSE_UNIT
10234:lbf*s
Qué significa: El binario contiene una cadena de unidad por defecto. Si falta configuración, ese valor por defecto puede aplicarse silenciosamente.
Decisión: Sustituye valores por defecto por comportamiento de “debe especificar”; fallar rápido en el arranque.
Task 14: Confirm your ingestion pipeline preserves unit fields
cr0x@server:~$ jq -r '.impulse_unit' raw_event.json enriched_event.json
lbf*s
null
Qué significa: La enriquecida eliminó impulse_unit. Ahora la analítica downstream asumirá suposición.
Decisión: Trata los campos de unidad como requeridos; bloquear despliegues de canalizaciones que los eliminen; añadir pruebas de evolución de esquema.
Tres mini-historias corporativas (lo bastante reales para doler)
Mini-historia 1: El incidente causado por una suposición equivocada
Una plataforma de pagos tenía dos servicios: uno calculaba un “risk score”, otro aplicaba reglas antifraude. El servicio de scoring
emitía un campo llamado score como entero de 0 a 1000. El servicio de aplicación creía que era 0.0 a 1.0.
Ambos equipos tenían documentación. Ambos creyeron que el otro la había leído. Ninguno tenía una prueba de contrato.
La integración pasó pruebas básicas porque los datos de ejemplo usaban puntuaciones pequeñas y los umbrales de reglas se ajustaron de forma laxa
durante el despliegue inicial. Luego se lanzó un nuevo modelo con una dispersión mayor. De repente, la aplicación empezó a rechazar un trozo
limpio de tráfico. No todo. Justo lo suficiente para encender soporte al cliente y arruinar una semana tranquila.
On-call inicialmente persiguió latencia y errores de base de datos porque el síntoma parecía reintentos y timeouts: los clientes
re-enviaban pagos. La plataforma no cayó; simplemente dijo “no” con más frecuencia, lo que es operativamente peor porque parece una decisión
de política, no una falla técnica.
El arreglo fue vergonzosamente simple: renombrar el campo a score_milli (0–1000), añadir score_unit y
una prueba de integración que falle si llega un score > 1.0 a la API de “ratio”. La solución real fue cultural: forzar a ambos equipos
a tratar los contratos de datos como código, no como páginas wiki.
Mini-historia 2: La optimización que salió mal
Un equipo de almacenamiento optimizó una canalización de ingestión cambiando de JSON a un formato binario compacto y “ahorrando bytes” al
eliminar campos descriptivos. Entre los campos eliminados: block_size y su unidad. Todos “sabían” que eran bytes.
Excepto el equipo consumidor que históricamente lo había tratado como kilobytes porque su UI lo etiquetaba “KB”.
El primer signo de problema fue sutil: los paneles mostraron un cambio lento y constante en el “tamaño medio de bloque”. No se disparó ninguna alerta.
La plataforma siguió funcionando. Pero la planificación de capacidad empezó a desviarse. La organización comenzó a comprar hardware demasiado pronto
en una región y demasiado tarde en otra. Ese desajuste es cómo te llama finanzas a las 3 a.m.
La optimización también eliminó la capacidad de hacer comprobaciones de cordura en logs. Los operadores perdieron observabilidad a cambio de un
pequeño incremento en throughput. Este es un modo de fallo común: una optimización local que degrada la habilidad global del sistema para detectar errores.
Eventualmente reintrodujeron unidades—no añadiendo una cadena a cada registro (lo que sería costoso), sino versionando el esquema e incorporando
metadatos de unidad en el registro de esquemas. También añadieron un consumidor canario que valida propiedades estadísticas (como tamaños típicos de bloque)
y alerta a humanos cuando la distribución cambia fuera de tolerancia.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Un equipo que gestionaba una flota de respaldos de bases de datos tenía la costumbre poco glamorosa: cada prueba de restauración registraba no solo “éxito”,
sino duración de restauración, bytes restaurados y throughput con unidades explícitas en la salida. También almacenaban
esos resultados en una pequeña base de series temporales. Era aburrido. La gente ocasionalmente se quejaba del “overhead del proceso”.
Luego una actualización de proveedor cambió un ajuste de compresión por defecto. El throughput de restauración cayó y la duración subió, pero
el job seguía devolviendo código de salida 0. Nadie lo hubiera notado hasta el día en que necesitaran una restauración—cuando sería demasiado tarde
para renegociar RTO con la realidad.
Porque el equipo tenía métricas trazables y con unidades explícitas, detectaron la regresión en una revisión semanal. Revirtieron el cambio,
documentaron el nuevo por defecto y añadieron una barrera: si el throughput de restauración baja por debajo de un umbral en dos pruebas consecutivas,
la canalización bloquea despliegues a producción.
La “práctica aburrida” no fue el logging. Fue la disciplina de convertir la prueba de restauración en una señal de producción de primera clase.
Así se sobrevive: no con heroísmos, sino con hábitos que asumen que el sistema te mentirá a menos que lo forcemos a no hacerlo.
Errores comunes: síntomas → causa raíz → arreglo
1) “Todo está dentro de límites” hasta que no
Síntomas: Deriva lenta en residuales, precisión que empeora gradualmente, eventos intermitentes de “casi fallo”.
Causa raíz: Sesgo introducido por desajuste de escalado/unidades; alertas afinadas para picos, no para offsets.
Arreglo: Añadir detección de sesgo: alarmas de media rodante, chequeos estilo CUSUM y validación explícita de conversiones en ingestión.
2) Paneles confiados, eje equivocado
Síntomas: Los operadores juran que el sistema está bien porque las gráficas parecen normales; los incidentes son “sorpresivos”.
Causa raíz: La capa de visualización asume unidades; los paneles usan formato por defecto; campos renombrados sin actualizar unidades.
Arreglo: Hacer cumplir unidades de panel vía CI; exigir sufijos de unidad en nombres de métricas; añadir etiquetas de “unit” donde sea posible.
3) Documentación de interfaz existe, pero la realidad diverge
Síntomas: Equipos productor y consumidor citan docs; la integración aún falla.
Causa raíz: Docs no vinculados a pruebas automatizadas; cambios de contrato desplegados sin validación del consumidor.
Arreglo: Implementar pruebas de contrato conducidas por consumidores; tratar cambios de esquema como versiones liberadas con puertas de compatibilidad.
4) “Lo convertimos después”
Síntomas: Lógica de conversión duplicada entre servicios; salidas inconsistentes; bugs difíciles de reproducir.
Causa raíz: No hay una fuente única de verdad para conversiones; código de conversión ad-hoc en cada cliente.
Arreglo: Centralizar conversiones en una librería bien testeada; prohibir conversiones locales salvo justificación y revisión.
5) Eliminación silenciosa de campos semánticos en pipelines
Síntomas: Informes downstream divergen tras una “optimización de rendimiento”; campos de unidad quedan nulos.
Causa raíz: Jobs ETL/enrichment tratan campos de unidad como opcionales; evolución de esquemas no probada.
Arreglo: Hacer campos que llevan unidad obligatorios; añadir comprobaciones en la canalización para detectar campos perdidos; versionar esquemas y hacer cumplir compatibilidad.
6) Semántica de versiones mixtas durante rollout
Síntomas: Solo algunos nodos se comportan “mal”; el incidente parece aleatorio; el canario se ve bien pero la flota no.
Causa raíz: El manejo de unidades cambió en una nueva versión; el despliegue parcial causa interpretación inconsistente.
Arreglo: Usar compatibilidad hacia atrás estricta; incluir metadatos de unidad versionados; bloquear rollouts cuando se detecta semántica mixta.
Listas de verificación / plan paso a paso
Plan paso a paso: hacer difícil enviar desajustes de unidades
- Inventario de campos que llevan unidad en APIs, telemetría, logs, métricas y configuraciones. Si es un número, asume que tiene unidades.
- Renombra campos para incluir unidades donde sea práctico:
duration_ms,size_bytes,temp_c. - Añade metadatos de unidad cuando renombrar no sea posible: campos
unito anotaciones de esquema. - Implementa pruebas de contrato entre productores y consumidores; bloquear merges por desajuste.
- Fallar rápido en el inicio cuando falta o es desconocida la configuración de unidades; los defaults son cómo la ambigüedad sobrevive.
- Define rangos permitidos (min/max) para señales clave; los desajustes de unidades suelen producir valores fuera de plausibilidad física/negocio.
- Alertas conscientes de sesgo: media rodante, detectores de deriva, seguimiento de residuales.
- Fixtures dorados: entradas conocidas con salidas conocidas, incluidas conversiones (p. ej., lbf→N).
- Ejecutar simulaciones de extremo a extremo con cargas realistas; los caminos sintéticos felices son donde los bugs van a esconderse.
- Revisiones operativas: cada traspaso de on-call incluye “¿qué suposiciones hacemos sobre unidades/tiempo/marcos?”
Checklist de integración pre-vuelo (úsala como si te importara)
- Todos los campos numéricos tienen unidades explícitas en nombre, esquema o metadatos.
- Toda conversión ocurre en una librería/módulo, no distribuida.
- Cada interfaz tiene una prueba de compatibilidad en CI.
- Los dashboards especifican unidades de eje y no dependen de valores por defecto.
- Las alertas incluyen detección de deriva/sesgo, no solo umbrales.
- El plan de despliegue incluye análisis de comportamiento con versiones mixtas.
- El runbook incluye “desajuste de unidades” como hipótesis de primera clase.
Preguntas frecuentes
1) ¿Se perdió Mars Climate Orbiter únicamente por un bug de conversión de unidades?
El desajuste de unidades fue la causa técnica desencadenante, pero la misión se perdió porque múltiples redes de seguridad no lo detectaron:
aplicación de interfaz, validación, respuesta operativa a anomalías y rigor en las revisiones.
2) ¿Por qué no lo detectaron antes?
Porque el error se manifestó como un sesgo gradual en lugar de una falla obvia. La deriva es fácil de racionalizar como ruido,
especialmente bajo presión de calendario y cuando el sistema aún “parece estable”.
3) ¿Cuál es el equivalente moderno de lbf·s vs N·s en sistemas cloud?
Segundos vs milisegundos, bytes vs MiB, UTC vs hora local y unidades de tasa (por segundo vs por minuto) son las grandes. También
“porcentaje” vs “fracción” (0–100 vs 0–1) aparece constantemente en ML y sistemas de riesgo.
4) ¿Deberíamos almacenar unidades con cada punto de datos?
No necesariamente con cada registro. Puedes almacenar unidades en esquemas, registros o metadatos versionados—siempre que
los consumidores no puedan ignorarlos accidentalmente y seguir adelante.
5) ¿No queda feo nombrar campos con sufijos como _ms?
Sí. También es efectivo. La confiabilidad está llena de cosas feas que te mantienen vivo: circuit breakers, reintentos con jitter
y sufijos que evitan que los humanos fantaseen con significados.
6) ¿Cuál es la mejor manera de prevenir bugs de unidades en código?
Usa tipos con conciencia de unidades cuando sea posible (o patrones de strong typedef), centraliza conversiones y escribe pruebas
que comparen con constantes de conversión conocidas y rangos plausibles. Lo más importante: hacer cumplir contratos entre servicios.
7) ¿Cómo detecto un desajuste de unidades cuando ya estoy en un incidente?
Busca ratios consistentes en los errores (p. ej., ~1000×, ~60×, ~4.448×). Compara mediciones independientes. Valida la carga cruda en el límite
y busca metadatos de unidad eliminados.
8) ¿Y si dos equipos no se ponen de acuerdo sobre unidades y ambos tienen “documentación”?
La documentación no es autoridad; la producción sí. Captura cargas reales, escribe una prueba de contrato que codifique la expectativa correcta
de unidad y versiona la interfaz para que la ambigüedad no vuelva de forma silenciosa.
9) ¿Por qué siguen ocurriendo estos fallos incluso en organizaciones maduras?
Porque las organizaciones escalan más rápido que el contexto compartido. Las interfaces se multiplican y las semánticas se pierden en los traspasos.
La única solución durable es hacer ejecutables las suposiciones: tipos, pruebas, validaciones en tiempo de ejecución y esquemas aplicados.
Conclusión: siguientes pasos que puedes tomar esta semana
Mars Climate Orbiter no fue derrotada por Marte. Fue derrotada por una interfaz que dejó evaporar el significado. Ese es el
verdadero peligro operativo: números que viajan sin su semántica, cruzando límites organizacionales donde “todos saben” se convierte en “nadie verificó”.
Pasos prácticos:
- Elige tus 10 principales campos numéricos en telemetría o APIs y haz las unidades explícitas (nombre o metadatos).
- Añade una prueba de contrato que falle por desajuste de unidades—haz que bloquee merges.
- Añade una alerta de deriva/sesgo en un residual métrico clave.
- Elimina un ajuste de unidad por defecto y fuerza configuración explícita.
- Ejecuta un game day donde la falla inyectada sea “las unidades cambiaron upstream”. Observa cuánto tardan en encontrarlo.
Si solo haces una cosa: trata la semántica de las interfaces como crítica en producción. Porque lo es. A la nave no le importa que tu organigrama sea complejo.