17 / 67 · 06 Observability-Driven Testing · A/B Tests as Quality Gates← prev⊞ allnext →☰ Read as one page
3.4Multi-Metric Quality Gates
Real-world quality gates evaluate multiple metrics simultaneously:
# multi_metric_quality_gate.py
from dataclasses import dataclass
from enum import Enum
class GateResult(Enum):
PASS = "pass"
WARN = "warn"
FAIL = "fail"
@dataclass
class MetricGate:
name: str
metric_type: str # "lower_is_better" or "higher_is_better"
critical: bool # if True, failure blocks deployment
max_degradation_pct: float # maximum allowed degradation (e.g., 0.10 = 10%)
def evaluate_quality_gates(
control_metrics: dict,
canary_metrics: dict,
gates: list[MetricGate]
) -> dict:
"""Evaluate all quality gates and produce a deployment decision."""
results = []
any_critical_fail = False
for gate in gates:
control_val = control_metrics[gate.name]
canary_val = canary_metrics[gate.name]
if gate.metric_type == "lower_is_better":
# e.g., error rate, latency -- canary should not be higher
degradation = (canary_val - control_val) / control_val if control_val > 0 else 0
passed = degradation < gate.max_degradation_pct
else:
# e.g., throughput, conversion -- canary should not be lower
degradation = (control_val - canary_val) / control_val if control_val > 0 else 0
passed = degradation < gate.max_degradation_pct
result = GateResult.PASS if passed else (GateResult.FAIL if gate.critical else GateResult.WARN)
if result == GateResult.FAIL and gate.critical:
any_critical_fail = True
results.append({
"gate": gate.name,
"control": control_val,
"canary": canary_val,
"degradation": f"{degradation:.2%}",
"threshold": f"{gate.max_degradation_pct:.0%}",
"result": result.value,
"critical": gate.critical,
})
return {
"decision": "ROLLBACK" if any_critical_fail else "PROMOTE",
"gates": results,
}
# Define quality gates
gates = [
MetricGate("error_rate", "lower_is_better", critical=True, max_degradation_pct=0.50),
MetricGate("p99_latency_ms", "lower_is_better", critical=True, max_degradation_pct=0.25),
MetricGate("p50_latency_ms", "lower_is_better", critical=False, max_degradation_pct=0.15),
MetricGate("conversion_rate", "higher_is_better", critical=False, max_degradation_pct=0.05),
MetricGate("cpu_usage_pct", "lower_is_better", critical=False, max_degradation_pct=0.30),
]
# Example metrics
result = evaluate_quality_gates(
control_metrics={"error_rate": 0.02, "p99_latency_ms": 450, "p50_latency_ms": 120,
"conversion_rate": 0.034, "cpu_usage_pct": 55},
canary_metrics={"error_rate": 0.021, "p99_latency_ms": 480, "p50_latency_ms": 125,
"conversion_rate": 0.033, "cpu_usage_pct": 58},
gates=gates,
)
print(f"Decision: {result['decision']}")
for g in result['gates']:
print(f" {g['gate']}: {g['result']} (degradation: {g['degradation']})")