Tema representativo de entrevista

Entrevista de diseño de sistemas: ¿Cómo migrarías OpenTelemetry en múltiples lenguajes a una configuración declarativa?

Diseño de sistemasDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

La configuración declarativa de OpenTelemetry es estable, pero las implementaciones en Java, Go, JavaScript y PHP tienen distinta madurez. ¿Cómo realizarías la migración sin vacíos de telemetría ni desvíos de configuración?

Consigna y contexto aplicable

Tu empresa ejecuta servicios en Java, Go, JavaScript y PHP. Hoy dependen de variables de entorno y de flags de inicio del SDK específicos de cada lenguaje. El equipo de plataforma desea adoptar archivos de configuración declarativa de OpenTelemetry seleccionados mediante OTEL_CONFIG_FILE, estandarizando gradualmente los atributos de recursos, el muestreo, los exportadores y los procesadores. Diseña el plan de migración, compatibilidad, validación y rollback.

En marzo de 2026, OpenTelemetry anunció partes estables del esquema JSON de configuración declarativa, la representación en archivos YAML, el modelo en memoria, el mecanismo de componentes de plugins, las operaciones de parseo/creación y OTEL_CONFIG_FILE; las implementaciones por lenguaje aún presentan distinta madurez. La entrevista evalúa la gobernanza de configuración entre versiones, no la memorización de sintaxis YAML.

Qué evalúa el entrevistador

  • Si distingues una especificación estable de implementaciones con estabilidad variable en cada lenguaje.
  • Si puedes definir una matriz de compatibilidad para el esquema, las versiones del SDK, los endpoints de exportadores y los permisos.
  • Si puedes evitar que las variables de entorno antiguas y los archivos nuevos generen anulaciones implícitas o exportadores duplicados.
  • Si puedes demostrar la completitud de la migración sin perder trazas, generar datos de alta cardinalidad o multiplicar los costos.

Una respuesta sólida trata la configuración como un artefacto versionado con linting, pruebas sintéticas, canaries, comparación y rollback rápido. Una respuesta débil se limita a decir "poner todo en YAML".

Preguntas clarificatorias antes de responder

  1. ¿Qué lenguajes admiten los campos objetivo y cuáles deben conservar las variables de entorno? Esto determina los lotes de migración.
  2. ¿El archivo se incluye en la imagen, se monta como volumen o se descarga en tiempo de ejecución? El origen modifica la firma, los permisos y la velocidad de rollback.
  3. ¿El código de la aplicación lee las variables de entorno existentes? Si es así, eliminarlas no es seguro hasta que existan un orden de precedencia y un período de desuso (deprecation).
  4. ¿Se permite la escritura dual temporal durante la migración? Implica un mayor costo, pero genera evidencia de comparación.

Estructura de respuesta en 30 segundos

"Haría un inventario del estado de implementación y las fuentes de configuración de cada lenguaje, para luego definir un esquema mínimo versionado y una matriz de compatibilidad. Un generador renderizaría YAML neutral respecto al lenguaje, mientras que CI validaría el esquema, permisos, endpoints y cardinalidad; los lenguajes sin soporte mantendrían la ruta anterior de variables de entorno. Validaría con un servicio sintético sin tráfico comercial y luego aplicaría canaries por lenguaje y nivel de riesgo, comparando métricas de envío, descarte, muestreo, latencia y costo. Cada artefacto llevaría una versión y un puntero de rollback. Si el parseo falla, el lanzador mantendría el artefacto previo o las flags antiguas en lugar de iniciar silenciosamente sin telemetría".

Respuesta detallada paso a paso

1. Construir una matriz de capacidades y delimitar fronteras

Divide la configuración en recursos, muestreo, receptores, procesadores, exportadores y componentes de plugins. Para cada lenguaje, registra la versión del SDK, los campos compatibles, los valores predeterminados, el mapeo de variables de entorno y el comportamiento ante campos desconocidos. Un modelo de datos y un mecanismo de parseo/creación estables no implican implementaciones con igual madurez en todos los lenguajes.

2. Definir una fuente única y el orden de precedencia

Establece el archivo declarativo como la fuente principal para los servicios nuevos, mientras que los servicios heredados conservan las variables de entorno. Durante la migración, define la precedencia de manera explícita: un campo explícito en el archivo anula un valor predeterminado generado por la plataforma; un campo no compatible es rechazado o marcado por el adaptador en lugar de ser ignorado silenciosamente. Emite una instantánea (snapshot) de la configuración efectiva sanitizada para auditoría.

text
repo template -> rendered config -> schema validation -> signed artifact
       |                                       |
   env defaults --------------------------> runtime loader

No permitas que cada servicio ensamble su propio YAML. Las plantillas de la plataforma administran los parámetros comunes; cada servicio puede declarar una anulación pequeña y revisada.

3. Diseñar el artefacto y la ruta de despliegue

El artefacto necesita una versión, lenguaje de destino, rango de SDK, endpoint del exportador, referencias a secretos y declaración de compatibilidad. CI valida el esquema y luego inicia un servicio mínimo para cargarlo y establecer conexiones con los exportadores. Producción utiliza un digest inmutable y firmado; el rollback cambia a un digest verificado previamente.

4. Manejar variables antiguas y exportadores duplicados

Antes de migrar, recopila la inyección de entorno, sidecars y la configuración en el código. Si el archivo y las variables coexisten, prueba la precedencia en staging y prohíbe dos exportadores para la misma señal. Mantén las variables antiguas como respaldo explícito con fecha de desuso y alertas; de lo contrario, dos sistemas de configuración coexistirán indefinidamente.

5. Verificar la semántica con telemetría sintética

Genera muestras fijas de trazas, métricas y logs para cada lenguaje. Verifica el nombre del servicio, los atributos de recursos, los nombres de spans, el muestreo, el tamaño de lote (batch size) y el destino del exportador. No te limites a verificar que "el proceso haya iniciado"; compara recuentos de entrada y salida, latencia, motivos de rechazo y etiquetas de alta cardinalidad en el Collector o backend.

6. Ejecutar canaries en lotes y definir condiciones de parada

Elige primero un flujo de negocio, un lenguaje e instancias de bajo tráfico, manteniendo un grupo de control en la versión anterior. Detén el despliegue ante errores de parseo, pérdida de telemetría, fallas de exportación, incremento en CPU/RSS, costo de escritura en el backend o regresiones en el p99 del negocio. Si la implementación de un lenguaje es incompleta, mantén la ruta antigua y registra la brecha en lugar de forzar una uniformidad superficial.

7. Gestionar el rollback y la gobernanza a largo plazo

El lanzador debe admitir tres estados: archivo nuevo, variables antiguas y telemetría deshabilitada explícitamente. Una falla de parseo no debe convertirse en un SDK "vacío pero exitoso"; recurre al artefacto firmado previo o a las variables antiguas y genera una alerta. Vuelve a ejecutar la matriz en cada actualización del SDK y retira las variables de entorno gradualmente.

Respuesta de ejemplo de alta calidad

Comenzaría con una matriz de capacidades por lenguaje y SDK, separando el esquema declarativo estable de la madurez de la implementación. La plataforma proporcionaría plantillas versionadas y pequeñas anulaciones a nivel de servicio. Tras el renderizado, CI verificaría el esquema, permisos, endpoints, cardinalidad y un inicio mínimo, generando un artefacto inmutable y firmado. Durante la migración, las variables de entorno antiguas permanecen como respaldo explícito y se registra la configuración efectiva para que los archivos y las variables no creen exportadores duplicados. Validaría muestras de telemetría fijas para los cuatro lenguajes y luego aplicaría canaries por lenguaje y riesgo del negocio. Las condiciones de parada incluyen fallas de parseo, tasa de descarte, errores de exportación, sobrecarga de recursos y regresión de costos. Ante un fallo en el archivo, el lanzador vuelve al artefacto previo o a las variables antiguas, nunca a una instancia desprovista de instrumentación de forma silenciosa; las actualizaciones de SDK vuelven a evaluar la matriz y avanzan en la discontinuación.

Errores comunes

  • Error → migrar todos los lenguajes porque el esquema es estable → una especificación estable no implica campos completos en cada implementación; solución: mantener una matriz de capacidades por lenguaje y SDK.
  • Error → permitir archivos y variables sin orden de precedencia → los exportadores o muestreadores pueden instanciarse dos veces; solución: declarar la precedencia y emitir una instantánea efectiva sanitizada.
  • Error → verificar únicamente que el proceso inicie → se pueden descartar spans, la cardinalidad puede dispararse o los datos pueden enviarse al endpoint incorrecto; solución: comparar recuentos y costos mediante telemetría sintética fija.
  • Error → usar silenciosamente una configuración vacía tras un fallo de parseo → una caída del sistema se transforma en un punto ciego de observabilidad; solución: recurrir a un artefacto previo firmado o a las variables antiguas y emitir una alerta.

Preguntas de seguimiento y respuestas

¿Qué ocurre si un lenguaje carece de un campo obligatorio para el exportador?

No fuerces la uniformidad. Mantén ese servicio en la ruta de variables antiguas, registra la brecha de campos y el riesgo asociado, y migra únicamente a través de un adaptador auditado si es necesario. Clasifica el resultado como parcialmente compatible en lugar de simular compatibilidad total con el esquema.

¿Cómo evitas que los equipos anulen la política de muestreo de la plataforma?

Establece una lista de campos permitidos para anulación, ejecuta validaciones de políticas tras el renderizado y registra el resumen efectivo junto con el responsable del cambio. La plataforma debe aplicar de forma estricta los exportadores costosos, los endpoints sensibles y los límites máximos de muestreo.

¿Qué sucede si la configuración carga exitosamente pero el costo del backend se duplica?

Segmenta por lenguaje, versión, muestreo, procesamiento por lotes, reintentos y atributos de recursos. Verifica primero exportadores duplicados y etiquetas de alta cardinalidad. Pausa el canary, revierte el artefacto, corrige la configuración y vuelve a ejecutar la misma carga sintética en lugar de incrementar inmediatamente la capacidad del backend.

Fuentes públicas

Preguntas relacionadas

Herramienta de entrevista relacionada

Usa Resolver para una respuesta de diseño de sistemas

Aclara primero los requisitos y luego avanza a través de la escala, la arquitectura, la elección de componentes y las compensaciones (trade-offs).

Ver la herramienta