<p><strong>Meta Description:</strong> Aprenda a monitorar qualidade de software: métricas de QA, dashboards de teste, KPIs deDevOps e como usar dados para melhoria contínua.</p>

<hr>
<h2>Por Que Monitorar Qualidade?</h2>

<ul><li><strong>Visibilidade</strong> - Entender estado atual</li><ol><li><strong>Tendências</strong> - Identificar melhoras/pioras</li>
<li><strong>Decisões</strong> - Base para ação</li>
<li><strong>Accountability</strong> - Responsabilidade clara</li>
</ol>
<hr>
<h2>Métricas de QA</h2>

<h3>Cobertura de Testes</h3>

<pre><code># Métricas de cobertura
metrics = {
    "line_coverage": calculate_line_coverage(),
    "branch_coverage": calculate_branch_coverage(),
    "function_coverage": calculate_function_coverage(),

<p>"covered_lines": 450,<br>    "total_lines": 500,<br>    "uncovered_lines": [12, 45, 78],  # Linhas críticas</p>

<p>"coverage_trend": {<br>        "week_1": 65,<br>        "week_2": 68,<br>        "week_3": 72,<br>        "week_4": 78<br>    }<br>}</p>

<p># Dashboard metrics<br>def calculate_coverage_metrics():<br>    return {<br>        "current": get_current_coverage(),<br>        "target": 80,<br>        "delta": get_current_coverage() - 80,<br>        "trend": get_coverage_trend(weeks=4)<br>    }<br></code></pre></p>

<h3>Defect Metrics</h3>

<pre><code>class DefectMetrics:
    def __init__(self, sprint_data):
        self.sprint = sprint_data

<p>def defect_leakage_rate(self):<br>        """{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} de defeitos encontrados em produção"""<br>        prod = len([d for d in self.sprint.defects if d.found_in == 'production'])<br>        total = len(self.sprint.defects)<br>        return (prod / total) * 100 if total &gt; 0 else 0</p>

<p>def defect_density(self):<br>        """Defeitos por story point"""<br>        return len(self.sprint.defects) / self.sprint.story_points_completed</p>

<p>def mean_time_to_detect(self):<br>        """Tempo médio para detectar defeito"""<br>        return sum(d.time_to_detect for d in self.sprint.defects) / len(self.sprint.defects)</p>

<p>def mean_time_to_fix(self):<br>        """Tempo médio para corrigir"""<br>        return sum(d.time_to_fix for d in self.sprint.defects) / len(self.sprint.defects)</p>

<p>def escape_rate(self):<br>        """{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} de defeitos escapados para produção"""<br>        escaped = len([d for d in self.sprint.defects if d.escaped_to_prod])<br>        total = len(self.sprint.defects)<br>        return (escaped / total) * 100 if total &gt; 0 else 0</p>

<p>def defect_by_severity(self):<br>        return {<br>            "critical": len([d for d in self.sprint.defects if d.severity == 'critical']),<br>            "high": len([d for d in self.sprint.defects if d.severity == 'high']),<br>            "medium": len([d for d in self.sprint.defects if d.severity == 'medium']),<br>            "low": len([d for d in self.sprint.defects if d.severity == 'low'])<br>        }</p>

<p>def defect_by_type(self):<br>        return {<br>            "functional": len([d for d in self.sprint.defects if d.type == 'functional']),<br>            "performance": len([d for d in self.sprint.defects if d.type == 'performance']),<br>            "security": len([d for d in self.sprint.defects if d.type == 'security']),<br>            "ux": len([d for d in self.sprint.defects if d.type == 'ux'])<br>        }<br></code></pre></p>

<h3>Test Execution Metrics</h3>

<pre><code>class TestExecutionMetrics:
    def __init__(self, execution_history):
        self.history = execution_history

<p>def pass_rate(self):<br>        """{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} de testes passando"""<br>        passed = len([t for t in self.history if t.status == 'passed'])<br>        total = len(self.history)<br>        return (passed / total) * 100 if total &gt; 0 else 0</p>

<p>def flaky_rate(self):<br>        """{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} de testes flaky"""<br>        flaky = len([t for t in self.history if t.is_flaky])<br>        total = len(self.history)<br>        return (flaky / total) * 100 if total &gt; 0 else 0</p>

<p>def execution_time(self):<br>        """Tempo de execução total"""<br>        return sum(t.duration for t in self.history)</p>

<p>def execution_time_trend(self):<br>        """Tendência de tempo de execução"""<br>        return self.history.group_by('date').avg('duration')</p>

<p>def coverage_trend(self):<br>        """Tendência de cobertura"""<br>        return self.history.group_by('date').avg('coverage')</p>

<p>def blocked_tests(self):<br>        """Testes bloqueados"""<br>        return [t for t in self.history if t.status == 'blocked']</p>

<p>def skipped_tests(self):<br>        """Testes pulados"""<br>        return [t for t in self.history if t.status == 'skipped']<br></code></pre></p>

<hr>
<h2>DORA Metrics</h2>

<h3>Deployment Frequency</h3>

<pre><code>def deployment_frequency():
    """
    Target: On-demand ou múltiplas vezes por dia
    """
    deployments = get_deployments(last_30_days=True)

<p>return {<br>        "count": len(deployments),<br>        "frequency": len(deployments) / 30,  # por dia<br>        "target": "&gt;1/day",<br>        "status": "elite" if len(deployments) &gt;= 30 else "high" if len(deployments) &gt;= 5 else "medium"<br>    }<br></code></pre></p>

<h3>Lead Time for Changes</h3>

<pre><code>def lead_time_for_changes():
    """
    Target: &lt;1 hora (elite)
    &quot;&quot;&quot;
    changes = get_code_changes()

<p>lead_times = []<br>    for change in changes:<br>        commit_time = change.commit_timestamp<br>        deploy_time = change.deployed_timestamp<br>        lead_time = (deploy_time - commit_time).total_seconds() / 3600  # horas<br>        lead_times.append(lead_time)</p>

<p>return {<br>        "median_hours": statistics.median(lead_times),<br>        "p95_hours": statistics.quantiles(lead_times, n=20)[18],  # P95<br>        "target": "&lt;1 hour&quot;,<br>        "status": "elite" if statistics.median(lead_times) &lt; 1 else &quot;high&quot;<br>    }<br></code></pre></p>

<h3>Time to Restore</h3>

<pre><code>def time_to_restore():
    """
    Target: &lt;1 hora (elite)
    &quot;&quot;&quot;
    incidents = get_incidents(last_30_days=True)

<p>restore_times = []<br>    for incident in incidents:<br>        restore_hours = (incident.resolved_at - incident.detected_at).total_seconds() / 3600<br>        restore_times.append(restore_hours)</p>

<p>return {<br>        "median_hours": statistics.median(restore_times),<br>        "p99_hours": max(restore_times),  # P99 worst case<br>        "target": "&lt;1 hour&quot;,<br>        "incidents_count": len(incidents)<br>    }<br></code></pre></p>

<h3>Change Failure Rate</h3>

<pre><code>def change_failure_rate():
    """
    Target: &lt;15{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} (elite)
    &quot;&quot;&quot;
    deployments = get_deployments(last_30_days=True)
    failed = len([d for d in deployments if d.failed or d.rolled_back])

<p>return {<br>        "failed_count": failed,<br>        "total_count": len(deployments),<br>        "rate_percent": (failed / len(deployments)) * 100 if deployments else 0,<br>        "target": "&lt;15{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}&quot;,<br>        "status": "elite" if failed / len(deployments) &lt; 0.15 else &quot;high&quot;<br>    }<br></code></pre></p>

<hr>
<h2>Dashboard Design</h2>

<h3>Grafana Dashboard</h3>

<pre><code># dashboard.json
{
  "dashboard": {
    "title": "QA Metrics Dashboard",
    "panels": [
      {
        "title": "Test Pass Rate",
        "type": "stat",
        "targets": [
          {
            "expr": "sum(test_results{status='passed'}) / sum(test_results) * 100"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "percent",
            "thresholds": {
              "mode": "absolute",
              "steps": [
                {"value": 0, "color": "red"},
                {"value": 90, "color": "yellow"},
                {"value": 95, "color": "green"}
              ]
            }
          }
        }
      },
      {
        "title": "Test Execution Time",
        "type": "timeseries",
        "targets": [
          {
            "expr": "rate(test_execution_seconds_sum[5m]) / rate(test_execution_seconds_count[5m])"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "s",
            "custom": {
              "lineWidth": 2,
              "fillOpacity": 10
            }
          }
        }
      },
      {
        "title": "Coverage Trend",
        "type": "timeseries",
        "targets": [
          {
            "expr": "test_coverage_percent",
            "legendFormat": "Coverage {6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}"
          }
        ]
      },
      {
        "title": "DORA Metrics",
        "type": "table",
        "targets": [
          {
            "expr": "deployment_frequency"
          },
          {
            "expr": "lead_time_hours"
          },
          {
            "expr": "time_to_restore_hours"
          },
          {
            "expr": "change_failure_rate_percent"
          }
        ]
      },
      {
        "title": "Defect Trends",
        "type": "timeseries",
        "targets": [
          {
            "expr": "rate(defects_created_total[1h])",
            "legendFormat": "Created"
          },
          {
            "expr": "rate(defects_resolved_total[1h])",
            "legendFormat": "Resolved"
          }
        ]
      },
      {
        "title": "Flaky Tests",
        "type": "table",
        "targets": [
          {
            "expr": "flaky_tests",
            "format": "table"
          }
        ]
      }
    ],
    "refresh": "5m",
    "time": {
      "from": "now-7d",
      "to": "now"
    }
  }
}
</code></pre>

<hr>
<h2>Relatórios</h2>

<h3>Sprint Quality Report</h3>

<pre><code># Sprint Quality Report - Sprint 42

<h2>Resumo Executivo</h2><ol><li>Sprint durou: 14 dias</li>
<li>Velocity: 42 story points</li>
<li>Defect density: 0.4/story point</li>
</ol>
<h2>Métricas de Teste</h2>
<figure class="wp-block-table"><table>
<tr><th>Métrica</th><th>Sprint Atual</th><th>Sprint Anterior</th><th>Target</th></tr>
<tr><td>Test Pass Rate</td><td>97{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>95{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>&gt;95{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td></tr>
<tr><td>Coverage</td><td>78{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>75{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>&gt;80{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td></tr>
<tr><td>Execution Time</td><td>25 min</td><td>28 min</td><td>&lt;30 min</td></tr>
<tr><td>Flaky Tests</td><td>2{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>3{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>&lt;2{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td></tr>
</table></figure>

<h2>DORA Metrics</h2>
<figure class="wp-block-table"><table>
<tr><th>Métrica</th><th>Valor</th><th>Status</th></tr>
<tr><td>Deployment Frequency</td><td>12/day</td><td>Elite</td></tr>
<tr><td>Lead Time</td><td>2h</td><td>High</td></tr>
<tr><td>MTTR</td><td>45min</td><td>Elite</td></tr>
<tr><td>Change Failure Rate</td><td>8{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</td><td>Elite</td></tr>
</table></figure>

<h2>Defeitos</h2><ol><li>Total criados: 17</li>
<li>Total resolvidos: 15</li>
<li>Em aberto: 2 (1 medium, 1 low)</li>
<li>Em produção: 1</li>
</ol>
<h2>Riscos</h2>
⚠️ Cobertura abaixo do target
⚠️ 1 defeito crítico em produção

<h2>Ações</h2><ol><li>[ ] Aumentar cobertura para 80{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442}</li>
<li>[ ] Investigar causa do defeito em produção</li>
<li>[ ] Revisar testes flaky</li></ol></ul>
</code></pre>

<hr>
<h2>Conclusão</h2>

<p>Monitoramento é essencial para melhoria contínua. As chaves são:<br><ol><li><strong>Definir métricas certas</strong> - Alinhadas com objetivos</li><br><li><strong>Coletar automaticamente</strong> - CI/CD integration</li><br><li><strong>Visualizar claramente</strong> - Dashboards efetivos</li><br><li><strong>Analisar tendências</strong> - Não apenas snapshots</li><br><li><strong>Agir sobre dados</strong> - Métricas sem ação não valem nada</li></ol></p>