Ir al contenido
wannacode

Requisitos no funcionales que puedes medir

En resumen: Un requisito no funcional dice qué tan bien trabaja un sistema (velocidad, disponibilidad) y se escribe como un escenario con una medida, por ejemplo p95 y p99, no como «que sea rápido».

Problema

El tablero está en verde y los clientes esperan

La dueña de la tienda le dice al equipo: «La tienda tiene que ser rápida y no caerse nunca». El equipo pone en el tablero el tiempo promedio de pago, con la meta «menos de 500 ms». Estos son los últimos 20 pagos del registro:

// 20 pagos del registro, en milisegundos
int[] times = {120, 95, 130, 110, 105, 2400, 140, 98, 115, 125,
101, 135, 990, 108, 122, 99, 118, 1500, 112, 104};
int average = Arrays.stream(times).sum() / times.length;
System.out.println("promedio: " + average + " ms");
System.out.println("meta: menos de 500 ms " + (average < 500 ? "✓" : "✗"));
Salida
promedio: 341 ms
meta: menos de 500 ms ✓

El tablero está en verde. Y soporte recibe quejas: «Pulsé "Pagar" y esperé dos segundos. Volví a pulsar». El tablero y los clientes dicen la verdad los dos.

«Rápido» no dice rápido para quién, con qué carga ni medido cómo. «Que no se caiga nunca» no dice cuántos minutos al mes son aceptables. Un requisito así no se puede comprobar, así que tampoco se puede cumplir.

La idea

El trabajo del arquitecto empieza con números

Este módulo trata del trabajo real de un arquitecto: decisiones que cuestan caro si se cambian más tarde, y cómo defenderlas con números en lugar de gusto. Al final te espera tu primera tarea de arquitectura: la tienda arranca en tres países y se abre a vendedores externos. La conviertes en requisitos medibles, propones al menos dos opciones con su costo, registras la decisión en un ADR y planificas la migración del sistema en vivo de modo que cada paso se pueda deshacer.

La primera habilidad está aquí. Un requisito funcional dice qué hace el sistema: «el cliente paga con tarjeta». Un requisito no funcional (NFR) dice qué tan bien: qué tan rápido, con qué frecuencia funciona, cuántos clientes aguanta, cuánto cuesta. La arquitectura la decide sobre todo el segundo tipo: la misma función puede ser un servidor o tres países con réplicas.

Un NFR que puedes medir se escribe como un escenario de calidad:

  • fuente: un cliente en la aplicación móvil;
  • estímulo: pulsa «Pagar»;
  • entorno: un día normal, hasta 50 pagos por segundo;
  • artefacto: la cadena del pedido;
  • respuesta: el pedido se paga, o el cliente ve un error claro;
  • medida: el 95 % de los pagos responde en menos de 800 ms y el 99 % en menos de 2 s, medido en el servidor durante los últimos 7 días.

Los números de aquí son un ejemplo: los reales vienen del negocio y de lo que el sistema puede hoy. Lo importante es que, pasada una semana, cualquiera pueda decir «cumplido» o «no cumplido».

La idea

Percentiles en lugar del promedio

Ordena los tiempos del más rápido al más lento. El percentil p95 es el tiempo por debajo del cual cae el 95 % de las peticiones: solo 5 de cada 100 son más lentas. p50 es el medio (la mediana), y p99 es el tiempo que espera uno de cada cien clientes.

Así se encuentra con el rango más cercano: de n tiempos ordenados, el percentil p es el número que está en el lugar ⌈p · n / 100⌉. La división entera con +99 redondea hacia arriba sin fracciones:

// rango más cercano: ceil(p · n / 100), con enteros
static int percentile(int[] sorted, int p) {
int rank = (p * sorted.length + 99) / 100;
return sorted[rank - 1];
}

Los mismos 20 pagos:

int[] sorted = times.clone();
Arrays.sort(sorted);
for (int p : new int[] {50, 95, 99})
System.out.println("p" + p + ": " + percentile(sorted, p) + " ms");
Salida
p50: 115 ms
p95: 1500 ms
p99: 2400 ms

La mitad de los clientes espera hasta 115 ms. Pero p95 es de 1,5 segundos: uno de cada veinte espera al menos eso. El promedio de 341 ms no describe a ningún cliente: los rápidos están por debajo y los lentos muy por encima. Por eso los requisitos de velocidad se escriben con percentiles, y el promedio se deja para otras cosas, como cuánto tiempo de CPU pagas.

La idea

Un presupuesto de latencia a lo largo de la cadena

Un pago no es un solo programa. La petición pasa por la pasarela, el servicio de pedidos, el proveedor externo de pagos y el almacén. Si la meta es p95 en menos de 800 ms, cada parte recibe su porción:

  • pasarela: 50 ms;
  • pedidos: 150 ms;
  • pago (proveedor externo): 450 ms;
  • almacén: 100 ms;
  • reserva: 50 ms.

Esto es un presupuesto de latencia. Cada equipo conoce su número, y cuando el almacén pide 300 ms, la conversación es sobre cifras: ¿de dónde salen los 200 ms? Los números vuelven a ser un ejemplo.

También hay una trampa. Cuando una página llama a varios servicios a la vez y espera a todos, es lenta en cuanto al menos uno es lento. Cuantos más servicios, más a menudo ocurre.

Pruébalo

Predice la salida

Cada servicio es lento en el 1 % de las peticiones. La página llama a 1, 5 o 20 servicios así a la vez y espera a todos. ¿Qué parte de las páginas es lenta?

// cada servicio es lento en el 1 % de las peticiones (por encima de su p99)
for (int n : new int[] {1, 5, 20}) {
double slow = 1 - Math.pow(0.99, n);
System.out.printf("servicios: %d → páginas lentas: %.2f%%%n", n, slow * 100);
}
Respuesta

La idea

Disponibilidad: los porcentajes se multiplican

La disponibilidad es la parte del tiempo en que el sistema responde correctamente. Suele decirse en «nueves». Esta es la caída que permite cada una en 30 días (30 × 24 × 60 = 43 200 minutos):

  • 99 %: 432 minutos, algo más de 7 horas;
  • 99,9 %: 43,2 minutos;
  • 99,99 %: 4,3 minutos.

Un pedido sale bien solo si funcionan la pasarela, el servicio de pedidos, el pago y el almacén. Si cada servicio falla de forma independiente de los demás, la probabilidad de que todos funcionen es el producto de sus porcentajes. Por eso la cadena es más débil que su servicio más débil.

Míralo

La cadena de servicios

static double chain(List<Service> services) {
double total = 1.0;
for (Service s : services) {
total *= s.availability() / 100;
System.out.printf("%-9s %.2f%% → %.2f%%%n",
s.name(), s.availability(), total * 100);
}
return total;
}
// números de ejemplo, no la promesa de un proveedor real
List<Service> services = List.of(
new Service("pasarela", 99.95),
new Service("pedidos", 99.9),
new Service("pago", 99.5),
new Service("almacén", 99.9));
double total = chain(services);
long down = Math.round((1 - total) * 30 * 24 * 60);
System.out.println("caída en 30 días: " + down + " min");
Paso a paso, en palabras
  1. Un pedido pasa por cuatro servicios. Sale bien solo si cada uno de ellos funciona en ese momento. Los porcentajes son de ejemplo.
  2. pasarela: 99,95 %. La cadena empieza en el 100 % y, después del primer servicio, está en 99,95 %.
  3. pedidos: 99,90 %. La cadena hasta aquí: 99,95 % × 99,90 % = 99,85 %.
  4. pago: 99,50 %. La cadena hasta aquí: 99,85 % × 99,50 % = 99,35 %. El proveedor externo de pagos es el eslabón más débil.
  5. almacén: 99,90 %. La cadena hasta aquí: 99,35 % × 99,90 % = 99,25 %.
  6. La cadena: 99,25 %, menos que su servicio más débil. En 30 días son 323 minutos en los que nadie puede pedir.

Un servicio se describe con un nombre y una disponibilidad:

record Service(String name, double availability) {}
static double chain(List<Service> services) {
double total = 1.0;
for (Service s : services) {
total *= s.availability() / 100;
System.out.printf("%-9s %.2f%% → %.2f%%%n",
s.name(), s.availability(), total * 100);
}
return total;
}
// números de ejemplo, no la promesa de un proveedor real
List<Service> services = List.of(
new Service("pasarela", 99.95),
new Service("pedidos", 99.9),
new Service("pago", 99.5),
new Service("almacén", 99.9));
double total = chain(services);
long down = Math.round((1 - total) * 30 * 24 * 60);
System.out.println("caída en 30 días: " + down + " min");
Salida
pasarela 99.95% → 99.95%
pedidos 99.90% → 99.85%
pago 99.50% → 99.35%
almacén 99.90% → 99.25%
caída en 30 días: 323 min

Cada servicio tiene el 99,5 % o más, pero el pedido tiene el 99,25 %: 323 minutos sin pedidos al mes, más de cinco horas. Cada nueva dependencia síncrona baja la cifra. Por eso el almacén no debería estar en el camino del pago: puede enterarse del pedido más tarde, por un evento en una cola. Así, una caída del almacén no detiene el pago.

Pruébalo

Completa

La disponibilidad de la cadena es el producto de las disponibilidades. ¿Qué falta?

double total = 1.0;
for (Service s : services)
    total  s.availability() / 100;

Pruébalo

Encuentra el error

Un colega calcula así la disponibilidad de la cadena y le promete a un cliente el 99,5 %, «lo mismo que el servicio más débil». ¿En qué línea está el error?

Elige la línea con el error.

La idea

Un presupuesto de errores

Tres términos que te vas a encontrar:

  • SLI (service level indicator): lo que mides, por ejemplo «la parte de pagos correctos»;
  • SLO (objective): la meta, por ejemplo «99,5 % en la semana»;
  • SLA (agreement): un contrato con un cliente, con una penalización si no se cumple la meta. El SLA suele ser más flojo que el SLO, para dejar un margen.

Si la meta es el 99,5 %, el 0,5 % restante es el presupuesto de errores. Con 60 000 pagos a la semana son 300 fallidos. Mientras haya presupuesto, el equipo publica versiones nuevas. Cuando se gasta, hasta el final del período solo se publican correcciones de confiabilidad. Así, la discusión «lo nuevo rápido, o estable» pasa a ser aritmética en lugar de opinión.

La idea

Cuando duele

  • «99,99 % para todo». Cada nueve más pide más copias, guardias y un sistema más complejo. El informe semanal para contabilidad no necesita lo que necesita el pago. Pon una meta alta solo donde el dinero o la confianza lo exigen.
  • Requisitos que nadie mide. Una lista de 40 NFR sin medición es ruido. Mejor cinco, cada uno con una medida y una persona que lo vigila.
  • Un percentil con pocos datos. Con 20 tiempos, p99 es solo el más lento. Necesitas miles de mediciones.
  • Requisitos que se pelean entre sí. Más seguridad son más comprobaciones, y las comprobaciones llevan tiempo. Más consistencia es menos disponibilidad. Esa es la siguiente lección: Trade-offs.

La medición en sí (métricas, tableros, alarmas) es infraestructura y se enseña en el curso de DevOps ZeroToOps. Aquí está la parte que decide el arquitecto: qué se mide y cuánto es suficiente.

Más precisamente, para curiosos

Los percentiles no se promedian. El p95 de dos servidores no es el promedio de los dos p95. Los sistemas de métricas guardan un histograma (el número de peticiones en cada intervalo de tiempo) y calculan el percentil a partir de la suma: HdrHistogram, los histogramas de Prometheus, OpenTelemetry. Tampoco los percentiles a lo largo de una cadena se suman exactamente: el p95 de la suma no es la suma de los p95. El presupuesto de latencia es un plan, no una fórmula.

Coordinated omission. Si una herramienta de carga espera la respuesta antes de enviar la siguiente petición, no mide los momentos en que el sistema estaba atascado. Entonces p99 se ve mejor que la verdad.

Independencia. La multiplicación supone que los servicios fallan de forma independiente. Si todos están en un mismo centro de datos y se va la luz, fallan juntos. Al revés, dos copias de un servicio que fallan de forma independiente dan 1 − (1 − a)²: dos copias del 99 % cada una son el 99,99 %. Por eso las copias van en zonas distintas.

De dónde vienen las palabras. El escenario de calidad viene de los métodos del SEI (Software Engineering Institute), como ATAM. ISO/IEC 25010 enumera las características de la calidad: rendimiento, confiabilidad, seguridad, mantenibilidad y otras. SLI, SLO y el presupuesto de errores vienen del libro de Google sobre SRE.

System.out.printf con %n escribe un salto de línea, y %% es el propio signo %. El formato depende del Locale por defecto: con uno español saldrá una coma.

Ejercicio

Un presupuesto de errores para una semana

Resuelve la tarea en tu propio editor.

La meta para los pagos es el 99,5 % correctos. El plan es de 60 000 pagos en la semana. Los fallidos por día son 20, 35, 150, 60 y 50. Escribe budget, que calcula cuántos errores permite la meta, resta los fallidos día a día y al final dice si hay presupuesto para versiones nuevas.

Salida
presupuesto: 300 errores de 60000 peticiones
lun: 20 → quedan 280
mar: 35 → quedan 245
mié: 150 → quedan 95
jue: 60 → quedan 35
vie: 50 → quedan -15
el presupuesto está agotado: hasta el fin de la semana solo correcciones

Más tareas

  • Velocidad. Añade un SLO de velocidad: p95 en menos de 800 ms. Para cada día tienes 20 tiempos. ¿Qué días incumplen la meta?
  • Dos copias. El pago tiene dos proveedores que fallan de forma independiente, con el 99,5 % cada uno. Si el segundo toma el relevo cuando falla el primero, ¿cuál es la disponibilidad del pago y en qué se convierte la cadena?

Test

Ponte a prueba

1. ¿Cuál de estas frases es un requisito que se puede comprobar?
2. El promedio es de 341 ms y p95 es de 1500 ms. ¿Qué significa?
3. Un pedido pasa por cuatro servicios, cada uno con una disponibilidad del 99,9 %. ¿Cuál es la disponibilidad del pedido?
4. La meta es el 99,5 % de pagos correctos y el presupuesto de errores de la semana se agotó el miércoles. ¿Qué se suele hacer?
5. ¿Por qué p99 de 20 mediciones dice poco?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·