Aula: 05 - Governança de código e qualidade (critérios de aceite e testes)
- Objetivos: Caracterizar governança de código e critérios de qualidade e testes.
- Curso: CST em Análise e Desenvolvimento de Sistemas
- Série: 4º Semestre
- Disciplina: Gerenciamento de Projetos Ágil
5.1 Governança de código
5.1.1 Conceito e princípios
Governança de código é o conjunto de políticas, processos e práticas que orientam como o código é produzido, revisado, integrado e mantido. Em ambientes ágeis, a governança deve ser leve, automatizada e centrada em feedback rápido, garantindo qualidade sem criar burocracia excessiva.
Princípios orientadores: - Clareza: políticas simples, visíveis no repositório. - Automação: regra que pode ser automatizada não deve depender apenas de disciplina humana. - Ownership: responsabilidades explícitas sobre áreas do código. - Conformidade mínima necessária: proteções suficientes para reduzir risco, sem travar o fluxo.
5.1.2 Modelos de branching e suas implicações
- Trunk-Based Development: commits frequentes na principal, feature flags para mudanças parciais, favorece integração contínua e lead time curto.
- GitHub Flow: branches curtos a partir da principal, Pull Request (PR) obrigatório, deploy contínuo.
- GitFlow: branches longa-vida (develop, release, hotfix). Útil para cadência de releases; aumenta complexidade.
Diretrizes práticas: - Nomes de branch padronizados:
feature/
5.1.3 Proteções e permissões
- Proteção de branch principal: exigir PR, revisões obrigatórias, checks de CI verdes, proibir force-push e commits diretos.
- CODEOWNERS: define responsáveis por pastas/arquivos.
- Revisões obrigatórias: número mínimo de revisores, requerer aprovação dos code owners.
- Assinatura/validação de commits (DCO/GPG) quando exigências de compliance forem necessárias.
Exemplo de CODEOWNERS:
# .github/CODEOWNERS
* @org/engineering
/docs/** @org/product @tech-writers
/infra/** @devops-team
/src/backend/** @backend-team
/src/frontend/** @frontend-team
5.1.4 Padrões de commit e versionamento
- Conventional Commits: padroniza mensagens para automação de changelog, versionamento semântico e detecção de breaking changes.
- Semantic Versioning (SemVer): MAJOR.MINOR.PATCH.
Exemplos:
feat(auth): adiciona login via JWT
fix(api): trata nulos no endpoint /users (#123)
perf(cache)!: altera chave de cache (breaking change)
Configuração básica do commitlint:
// commitlint.config.cjs
module.exports = { extends: ['@commitlint/config-conventional'] };
5.1.5 Revisão de código eficaz
Objetivos: detectar defeitos, melhorar design, disseminar conhecimento e manter padrões.
Checklist sugerido: - Correção: a mudança resolve o problema/estória? Há testes cobrindo cenários críticos? - Simplicidade: há código morto, duplicado ou complexidade desnecessária? - Segurança: segredos expostos, validações, entradas confiáveis? - Performance: impactos em hot paths, alocações, N+1 queries? - Manutenibilidade: nomes claros, modularização, docs atualizados. - Conformidade: estilo (lint), padrões de commit, DoD.
Template de Pull Request:
<!-- .github/pull_request_template.md -->
## Contexto
Descreva o problema e o porquê da mudança.
## O que foi feito
- [ ] Item 1
- [ ] Item 2
## Como testar
Passos para validar localmente.
## Critérios de aceite
- [ ] CA1
- [ ] CA2
## Riscos e rollback
Riscos identificados e plano de reversão.
Resolves #<id>
5.1.6 Automatização de políticas (CI)
Automatize lint, testes, cobertura mínima, análise estática e verificação de mensagens de commit.
Exemplo de pipeline no GitHub Actions:
# .github/workflows/ci.yml
name: ci
on:
pull_request:
branches: [ "main" ]
jobs:
build-test:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run lint
- run: npm test -- --coverage
- name: Quality Gate (coverage >= 80%)
shell: bash
run: |
COVERAGE=$(node -p "require('./coverage/coverage-summary.json').total.lines.pct")
echo "Coverage: $COVERAGE%"
THRESHOLD=80
COVERAGE_INT=$(printf "%.0f" "$COVERAGE")
if [ "$COVERAGE_INT" -lt "$THRESHOLD" ]; then
echo "Coverage abaixo de $THRESHOLD%"; exit 1; fi
5.1.7 Documentação do repositório
- README: visão geral, setup, execução, deploy.
- CONTRIBUTING: fluxo de contribuição, padrões de commit, como abrir PRs.
- CODE_OF_CONDUCT: normas de convivência.
- Arquivos de configuração visíveis (.editorconfig, lint, formatador).
Exemplo simples de .editorconfig:
# .editorconfig
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
5.1.8 Fluxo de governança (visão geral)
5.2 Qualidade: critérios de aceite e testes
5.2.1 Critérios de aceite (CA) bem definidos
Critérios de aceite descrevem comportamentos verificáveis que, quando satisfeitos, demonstram que a estória atende às necessidades. Boas práticas: - Clareza e testabilidade (Given-When-Then auxiliam). - Cobrir caminhos felizes e erros relevantes. - Evitar detalhes de implementação.
Exemplo em Gherkin:
Feature: Autenticação
Scenario: Login válido
Given que o usuário "ana@exemplo.com" existe e a senha é "Secreta!123"
When ele envia credenciais válidas para "/api/login"
Then a API retorna status 200
And o corpo contém um token JWT válido com expiração em 15 minutos
Scenario: Login inválido
Given que o usuário "ana@exemplo.com" existe
When ele envia senha incorreta para "/api/login"
Then a API retorna status 401
And a resposta não contém token
Relacionando CA com DoR/DoD: - DoR: estória pronta para planejar quando tem CA claros, dados de teste e dependências identificadas. - DoD: estória concluída quando CA passam, testes automatizados atualizados, revisão feita e documentação mínima entregue.
Exemplo de DoD focado em qualidade:
- Código revisado e aprovado (>= 1 owner).
- Lint sem erros; formatação aplicada.
- Testes unitários e integração cobrindo casos críticos.
- Cobertura de linhas >= 80% e mutação >= 60% (quando aplicável).
- Sem vulnerabilidades críticas abertas.
- Logs e métricas adicionados conforme necessidade.
- Documentação atualizada (README/CHANGELOG).
5.2.2 Pirâmide de testes e estratégia
- Unidade: rápida, isolada, maior volume.
- Integração: valida módulos/serviços se comunicando.
- Contrato/API: garante compatibilidade entre serviços.
- Funcional/E2E: valida jornada do usuário de ponta a ponta.
- Não funcionais: performance, segurança, acessibilidade, confiabilidade.
Visualização: 
5.2.3 Testes unitários: exemplo
// src/sum.js
export function sum(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') throw new TypeError('invalid');
return a + b;
}
// __tests__/sum.test.js
import { sum } from '../src/sum';
describe('sum', () => {
it('soma números', () => {
expect(sum(2, 3)).toBe(5);
});
it('lança erro para entradas inválidas', () => {
expect(() => sum('2', 3)).toThrow('invalid');
});
});
Configuração básica de cobertura no Jest:
// jest.config.cjs
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageThreshold: { global: { lines: 80, branches: 70, functions: 80, statements: 80 } },
};
5.2.4 Testes de integração/contrato: exemplo
// __tests__/users.int.test.js
import request from 'supertest';
import app from '../src/app';
describe('GET /api/users/:id', () => {
it('retorna 200 e payload esperado', async () => {
const res = await request(app).get('/api/users/123').expect(200);
expect(res.body).toMatchObject({ id: '123', name: expect.any(String) });
});
});
Contrato OpenAPI como fonte de verdade:
# openapi.yaml (trecho)
paths:
/api/users/{id}:
get:
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
5.2.5 Testes funcionais (E2E) e flakiness
- Use dados determinísticos e ambientes efêmeros quando possível.
- Sincronize por condições de tela/estado, não por sleeps fixos.
- Marque testes instáveis e trate-os como dívida.
Exemplo com Playwright:
// e2e/login.spec.ts
import { test, expect } from '@playwright/test';
test('login sucesso', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('ana@exemplo.com');
await page.getByLabel('Senha').fill('Secreta!123');
await page.getByRole('button', { name: 'Entrar' }).click();
await expect(page.getByText('Bem-vinda, Ana')).toBeVisible();
});
5.2.6 Testes não funcionais
- Performance: metas de latência/p95 e throughput; perfis por cenário de carga.
- Acessibilidade: WCAG AA como referência.
- Segurança: SAST/DAST/SCA integrados à pipeline.
Exemplos de ferramentas: - Performance: k6, JMeter. - Acessibilidade: axe-core, pa11y. - Segurança: Semgrep (SAST), OWASP ZAP (DAST), scanners de dependência.
Script k6 simples:
// perf/login.k6.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = { vus: 20, duration: '1m', thresholds: { http_req_duration: ['p(95)<500'] } };
export default function () {
const res = http.post('https://api.exemplo.com/login', { user: 'ana', pass: 'Secreta!123' });
check(res, { 'status 200': (r) => r.status === 200 });
sleep(1);
}
5.2.7 Qualidade automatizada e gates
- Análise estática: code smells, complexidade, duplicações.
- Cobertura mínima: linhas/branches.
- Debt ratio: acompanhar evolução da dívida técnica.
Exemplo de configuração SonarQube:
# sonar-project.properties
sonar.projectKey=org.exemplo.app
sonar.sources=src
sonar.tests=__tests__
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.qualitygate.wait=true
5.2.8 Integração de qualidade no fluxo
Integre critérios de aceite e testes desde o refinamento: - Durante o refinamento: escrever CA com o time, identificar riscos e dados de teste. - Durante a implementação: TDD/BDD quando aplicável; criar testes a partir dos CA. - Na revisão/CI: checks automatizados verificam padrões, cobertura e análise estática. - Após merge: monitorar métricas em produção (erros, latência) e alimentar melhoria contínua.
5.2.9 Indicadores úteis
- Defeitos escapados por release.
- Taxa de aprovação de PR na primeira tentativa.
- Tempo médio de revisão de PR.
- Cobertura e mutação ao longo do tempo.
- DORA (lead time, frequência de deploy, taxa de falha de mudança, MTTR) como visão holística.
5.2.10 Exemplo de mapeamento CA → testes
- CA: “Retornar 401 para login inválido” → Teste de integração validando status e corpo.
- CA: “Exibir mensagem de boas-vindas após login” → Teste E2E validando UI.
- CA: “Tempo p95 < 500ms no login” → Teste de performance com threshold.
- CA: “Sem segredos no repositório” → Check automatizado (secret scanning) na CI.
Exercícios propostos
- Exercício 1: Crie um CODEOWNERS adequado à estrutura de um projeto de exemplo e justifique as escolhas.
- Exercício 2: Escreva critérios de aceite em Gherkin para a funcionalidade “Recuperar senha”.
- Exercício 3: Configure um pipeline CI mínimo com lint, testes e gate de cobertura de 80%.
- Exercício 4: Elabore um checklist de revisão de código de até 10 itens alinhado ao contexto do seu time.
- Exercício 5: Defina um DoD focado em qualidade para um microserviço HTTP simples.
Conclusão
Nesta aula, estruturamos a governança de código como um conjunto de políticas leves e automatizadas que protegem o fluxo de desenvolvimento sem engessá-lo. Vimos como padrões de commit, proteção de branches, revisão por pares e documentação do repositório sustentam a qualidade. Conectamos critérios de aceite claros à pirâmide de testes e aos gates automatizados da pipeline, cobrindo testes unitários, integração, E2E e não funcionais. Por fim, relacionamos métricas de qualidade e práticas de integração contínua que promovem previsibilidade e confiança nas entregas.
Próxima aula
Dependências e vulnerabilidades; operação e gerenciamento de incidentes. Veremos como identificar e mitigar riscos em bibliotecas de terceiros (SCA), aplicar varreduras de segurança (SAST/DAST), e organizar resposta a incidentes para manter confiabilidade operacional.