Competições Senac RS · Seletiva 26

Módulo 08 · Apostila teórica

Módulo 08 — Lapidação e qualidade de código

BaseUC4 do plano de curso (Desenvolver código orientado a objetos, 92h)Peso na provanão tem peso próprio, é o acabamento que empurra todas as notas de julgamento de 2 pra 3NaturezaRefino
Ocupação: Desenvolvimento de Sistemas Treinador: Diogo Roehrs
💡Nota

Onde esse módulo entra. O cronograma reserva outubro pra isso. Nesse ponto o sistema já roda ponta a ponta: banco modelado (03), API com autenticação (04), front que consome a API (05), interface pensada (06) e as funcionalidades novas montadas (07). Lapidação é o mês que transforma "funciona" em "impecável". É o menos glamouroso e o mais decisivo do treino inteiro: na prática é aqui que se ganha a competição, porque a diferença entre 2 e 3 (o nosso jogo inteiro, lembra do módulo 01) mora quase toda no acabamento. Não é conteúdo novo, é a demão final em cima de tudo que já existe.

Deixando claro pra quem não é técnico: isso é 🆕 novo no sentido de que a etapa Regional não chega aqui. Quem lapida está jogando o jogo do nível 3. As duas já sabem construir o sistema; esse módulo é sobre deixar o sistema redondo, e sobre fazer isso rápido, porque na prova o tempo de lapidar é curto.

🔗 O fio que atravessa 07 e 08: compressão. No módulo 07 a gente treinou montar uma feature inteira em quatro horas. Aqui é o irmão dele: lapidar um sistema inteiro nas horas finais da prova. Os dois vivem do mesmo segredo, ter os padrões prontos na mão. Máscara você não inventa na prova, você cola a que já treinou. Componente de confirmação você não desenha na hora, você reusa. Esse módulo é a caixa de ferramentas que a gente monta em outubro pra sacar em dezembro sem pensar.


1. O que é lapidação (e o que não é)

Lapidar não é adicionar funcionalidade. É pegar o que já funciona e deixar redondo: sem aresta, sem susto, sem feiúra. É a distância entre um sistema que passa no teste feliz (o "caminho certo", que a própria competidora testa porque sabe usar o que fez) e um sistema que aguenta o mundo real: a usuária fazendo besteira, o dado faltando, a internet caindo, o clique duplo, o campo vazio.

Uma forma de enxergar: todo sistema tem dois públicos. O primeiro é a pessoa que usa direito. O segundo é a pessoa que usa errado, de propósito ou por acidente. O caminho feliz atende o primeiro. A lapidação atende o segundo. E a banca é, por profissão, o segundo público.

🎯Foco de prova

🎯 Por que isso é o coração do nível 3. Quase toda avaliação de julgamento (0 a 3) pergunta "está bem-feito?", e "bem-feito" é acabamento. Duas competidoras entregam o mesmo CRUD de convidado. Uma valida o formulário, dá mensagem clara, tem máscara no telefone, confirma antes de apagar e mostra "nenhum convidado ainda" na lista vazia. A outra não. Mesma funcionalidade, notas diferentes em UX, em qualidade de código e em robustez. Três critérios subindo de uma vez por causa de acabamento. Lapidação é onde essa diferença é construída de propósito, não por sorte.

⚠️ Pega-ratão de calendário. Lapidação não é uma etapa que acontece "no fim". As máscaras, os nomes bons, o componente de confirmação, tudo isso a gente combina e treina desde agosto, senão outubro não dá conta. O que acontece em outubro é a passada final: revisar tudo com o checklist na mão. Se a base já veio limpa, a passada é rápida. Se veio suja, outubro vira faxina e não sobra tempo pra brilhar. Padronizar no começo custa barato; padronizar no fim custa a prova.

1.1 O mapa da lapidação

Pra não virar "melhorar no olho", a lapidação tem endereço. Toda a passada final cabe em seis frentes, e é assim que esse módulo está organizado:

Frente O que é Onde toca
Entrada máscara e formatação enquanto digita (§2) front
Validação regex e casos de canto (§3, §4) front + back + banco
Segurança de ação confirmar antes de destruir (§5) front
Navegação de dados busca, filtro, ordenação (§6) banco → back → front
Código por dentro refatoração, OO, código limpo, padrão (§7, §8) tudo
Comunicação de erro mensagem que diz o que fazer (§9) tudo

E fechando, a mentalidade que garante que nada disso passou batido: testar como a banca testa (§10). Cada frente vira item de checklist. É esse checklist que a gente saca na prova.


2. Máscaras e formatação de entrada

Campo que se formata sozinho enquanto a pessoa digita: telefone que vira (51) 99999-9999, data que vira 31/08/2026, valor que vira R$ 1.250,00. Parece detalhe, mas comunica cuidado já na primeira olhada, e previne erro na origem (a usuária não consegue digitar letra num campo de telefone). É das coisas de melhor retorno pelo esforço na prova inteira.

Só que máscara tem uma regra de ouro que separa quem sabe de quem só copiou: formata pra mostrar, guarda limpo pra mandar.

2.1 O padrão formata-mostra / guarda-limpo

O banco não quer (51) 99999-9999, quer 51999999999. O DECIMAL do módulo 03 não quer R$ 1.250,00, quer 1250.00. Então a máscara vive só na tela. Na hora de mandar pra API (o api() do módulo 05), a gente limpa o valor de volta pro cru.

São sempre duas funções por máscara: uma que formata (cru → bonito, roda a cada tecla) e uma que limpa (bonito → cru, roda antes de enviar).

// mascaras.js — front, vale igual nas duas stacks

// TELEFONE: formata enquanto digita, limpa antes de mandar
function formataTelefone(valor) {
  const numeros = valor.replace(/\D/g, '').slice(0, 11); // só dígitos, no máx 11
  if (numeros.length <= 2)  return numeros;
  if (numeros.length <= 7)  return `(${numeros.slice(0,2)}) ${numeros.slice(2)}`;
  return `(${numeros.slice(0,2)}) ${numeros.slice(2,7)}-${numeros.slice(7)}`;
}

function limpaTelefone(valor) {
  return valor.replace(/\D/g, ''); // guarda só os números
}

Ligar no campo é um addEventListener de input (dispara a cada tecla). O truque de sempre reposicionar o cursor no fim resolve 99% dos casos da prova sem complicar:

const campoTel = document.querySelector('#telefone');
campoTel.addEventListener('input', (e) => {
  e.target.value = formataTelefone(e.target.value);
});

E na hora de enviar, limpa:

const payload = {
  nome: campoNome.value.trim(),
  telefone: limpaTelefone(campoTel.value), // "51999999999", não "(51) 99999-9999"
};
await api('/convidados', { method: 'POST', body: payload });
🛠️Gambiarra boa

🛠️ Gambiarra boa: uma máscara genérica por template. Em vez de escrever uma função por campo, dá pra ter uma que segue um molde de # (dígito) e caractere fixo. Cola pronta na prova e serve pra telefone, CEP, data, o que aparecer:

js function mascara(valor, molde) { // molde ex.: "(##) #####-####" ou "##/##/####" const nums = valor.replace(/\D/g, ''); let i = 0, saida = ''; for (const ch of molde) { if (i >= nums.length) break; saida += (ch === '#') ? nums[i++] : ch; } return saida; } // uso: mascara(v, "(##) #####-####") ou mascara(v, "##/##/####")

Uma função na cola, todas as máscaras da prova resolvidas. Isso é compressão: você não pensa em máscara na hora, você aplica o molde.

2.2 Dinheiro é o caso chato (e cai)

Valor em reais tem vírgula decimal, ponto de milhar e o R$ na frente. E o back quer 1250.00 (ponto decimal, sem milhar). O caminho que menos dá dor de cabeça na prova é formatar na saída do campo (evento blur) e ter o par de conversão sempre à mão:

// mostra: 1250.5 -> "R$ 1.250,50"
function formataReais(numero) {
  return numero.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
}
// limpa: "R$ 1.250,50" -> 1250.5 (número que o DECIMAL aceita)
function limpaReais(texto) {
  const cru = texto.replace(/[^\d,]/g, '').replace(',', '.');
  return parseFloat(cru) || 0;
}
⚠️Pega-ratão

⚠️ Pega-ratão clássico: mandar "R$ 1.250,00" pro campo DECIMAL(10,2) do banco. O MySQL não entende, ou pior, entende errado e guarda 1.00. Todo campo de dinheiro precisa passar pelo limpaReais antes de virar payload. Formata pra mostrar, guarda limpo: é a mesma regra do telefone, e é a que a banca verifica olhando o que chegou no banco, não o que aparece na tela.

🔗 Continuidade. No módulo 05 a máscara apareceu como parte da validação no cliente, meio de passagem. Aqui ela vira padrão fechado, com o par formata/limpa explícito. Guarda esse par: ele é o que transforma "tem máscara" (bonito) em "tem máscara certa" (bonito e o dado chega limpo no banco). A banca de nível 3 testa os dois.


3. Biblioteca de regex pra validação

Regex (expressão regular) é o canivete da validação: um padrão que diz "esse texto tem a cara certa?". Vale a pena chegar na prova com uma bibliotequinha pronta na cola, porque validar formato é ponto garantido e escrever regex sob pressão é onde todo mundo trava.

// validacao.js — os padrões que caem no Wedding Pass
const REGEX = {
  email:    /^[^\s@]+@[^\s@]+\.[^\s@]+$/,        // algo@algo.algo
  telefone: /^\d{10,11}$/,                        // 10 ou 11 dígitos (já limpo)
  data:     /^\d{2}\/\d{2}\/\d{4}$/,              // dd/mm/aaaa
  codigo:   /^[A-Z0-9]{8}$/,                      // código do convite, CHAR(8)
  soLetras: /^[A-Za-zÀ-ÿ\s]+$/,                   // nome sem número
};

function pareceEmail(v)    { return REGEX.email.test(v.trim()); }
function pareceTelefone(v) { return REGEX.telefone.test(v.replace(/\D/g, '')); }

3.1 CPF: regex não basta, precisa do dígito verificador

Um erro comum é achar que /^\d{11}$/ valida CPF. Valida o formato (onze dígitos), não o CPF. 00000000000 passa nesse regex e não é CPF nenhum. CPF de verdade tem dígito verificador, e a banca sabe disso. A função abaixo é feia mas é a que separa nível 2 de nível 3 num campo de documento:

function cpfValido(cpf) {
  cpf = cpf.replace(/\D/g, '');
  if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) return false; // 111.111... é inválido

  const digito = (fatiaAte) => {
    let soma = 0, peso = fatiaAte + 1;
    for (let i = 0; i < fatiaAte; i++) soma += Number(cpf[i]) * (peso--);
    const resto = (soma * 10) % 11;
    return resto === 10 ? 0 : resto;
  };
  return digito(9) === Number(cpf[9]) && digito(10) === Number(cpf[10]);
}
🛠️Gambiarra boa

🛠️ Gambiarra boa: o Wedding Pass talvez nem peça CPF (o tema é casamento, não banco). Mas leva a função na cola mesmo assim: se a Prova Surpresa (módulo 07) jogar um campo de documento, você cola e ganha o ponto de validação forte enquanto os outros só contam onze dígitos. Preparo é o que vira sorte na prova.

⚠️ Pega-ratão de regex de e-mail. Tem gente que decora um regex de e-mail gigante de trinta caracteres achando que é mais "certo". Na prova, /^[^\s@]+@[^\s@]+\.[^\s@]+$/ (tem arroba, tem ponto depois, sem espaço) resolve tudo que a banca testa e você lembra de cabeça. Regex perfeito de e-mail não existe e não paga. O suficiente e memorizável ganha do perfeito e esquecido.

No PHP a lógica é idêntica, muda a função: preg_match('/^[^\s@]+@[^\s@]+\.[^\s@]+$/', $email) no lugar do .test(). O padrão entre barras é o mesmo texto. Regex é uma linguagem só, viaja entre as stacks sem tradução.


4. Validações finas: os cantos que ninguém testa

O caminho feliz qualquer uma faz. Lapidação é cuidar dos cantos, e o jeito de não esquecer nenhum é varrer campo por campo perguntando "e se vier vazio? e se vier errado? e se vier absurdo?". Vira uma tabelinha por tela:

Campo Vazio Errado Absurdo
nome "informe o nome" número no nome 300 caracteres
telefone opcional, ok menos de 10 dígitos letra
capacidade da mesa "informe a capacidade" texto negativa, 0, ou 5000
data do evento "informe a data" formato inválido data no passado
acompanhantes assume 0 texto 500 num casamento

Cada linha dessas tratada com uma resposta clara é um ponto que sobe. Cada uma que quebra a tela é um ponto que cai.

💡Nota

🔗 As três camadas (do módulo 04, agora fechadas). Validação boa mora em três lugares e cada um tem um papel: - Front valida pra ser gentil: avisa na hora, antes de gastar uma ida ao servidor ("a data precisa ser futura"). - Back valida pra ser seguro: nunca confia no que chegou, porque o front pode ser burlado (Postman, DevTools). É o "front esconde, back barra" do módulo 04. - Banco valida pra ser íntegro: o CHECK (capacidade > 0) e o NOT NULL do módulo 03 são a última muralha, valem mesmo se todo o resto falhar.

A banca adora furar a camada de front (manda direto pela API) pra ver se o back segura. Validar só no front é nível 2 com cara de nível 3: bonito até alguém abrir o DevTools.

🎯 A banca testa o caminho que falha. Quando você desenvolve, testa o que dá certo, porque sabe usar o próprio sistema. A banca não sabe e nem quer: vai clicar errado, deixar em branco, mandar lixo, cadastrar o mesmo convidado duas vezes. Cada caso desses previsto e respondido é robustez, que pontua. A pergunta que abre esse ponto ("e se vier vazio, errado, absurdo?") é literalmente o roteiro que a banca segue. Antecipa ela.


5. Componente de confirmação de ação destrutiva

Toda ação que apaga ou desfaz algo importante pede uma pergunta antes: "tem certeza que quer apagar essa convidada?". É prevenção de erro (Nielsen, módulo 06) na veia, e evita o desastre do clique acidental que apaga a lista toda na frente da banca.

5.1 A gambiarra que já serve

O confirm() nativo do navegador resolve na marra e não custa nada:

if (confirm('Apagar essa convidada? Essa ação não volta.')) {
  await api(`/convidados/${id}`, { method: 'DELETE' });
  render();
}

Funciona, protege, e num aperto de tempo está de bom tamanho. Mas é feio (aquela caixa cinza do navegador quebra a identidade visual que a gente cuidou no módulo 06) e trava a página inteira enquanto está aberto.

5.2 O componente reusável (o que dá nível 3)

O pulo do gato é ter um componente de confirmação que combina com o sistema e serve pra tudo: apagar convidada, cancelar convite, resetar o evento. Faz uma vez, usa em todo lugar (componentização, módulo 05). A sacada é ele devolver uma Promise, pra usar com await igualzinho ao confirm() nativo, sem espalhar callback pelo código:

// confirmar.js — um componente, toda confirmação do sistema
function confirmar(mensagem) {
  return new Promise((resolve) => {
    const fundo = document.createElement('div');
    fundo.className = 'modal-fundo';
    fundo.innerHTML = `
      <div class="modal-caixa" role="alertdialog" aria-modal="true">
        <p>${mensagem}</p>
        <div class="modal-acoes">
          <button class="btn-cancelar" autofocus>Cancelar</button>
          <button class="btn-confirmar">Confirmar</button>
        </div>
      </div>`;
    document.body.appendChild(fundo);

    const fecha = (resposta) => { fundo.remove(); resolve(resposta); };
    fundo.querySelector('.btn-confirmar').onclick = () => fecha(true);
    fundo.querySelector('.btn-cancelar').onclick  = () => fecha(false);
    fundo.onkeydown = (e) => { if (e.key === 'Escape') fecha(false); }; // Esc cancela
  });
}

Uso no lugar do nativo, mesma cara, visual do sistema:

if (await confirmar('Apagar essa convidada? Essa ação não volta.')) {
  await api(`/convidados/${id}`, { method: 'DELETE' });
  render();
}
🛠️Gambiarra boa

🛠️ Gambiarra boa: o botão perigoso (Confirmar de um DELETE) na cor de erro (--cor-erro, o token do módulo 05) e o Cancelar com autofocus. Aí um Enter distraído cancela em vez de apagar. É segurança de graça: o caminho fácil é o caminho seguro. Nielsen aprovaria.

⚠️ Pega-ratão de acessibilidade que a banca de UX cobra. Modal precisa de três coisas pra não perder ponto: fechar no Esc, foco começar dentro dele (o autofocus no Cancelar) e o role="alertdialog". São três detalhes que somam num critério que pesa 10% (módulo 06). Barato de pôr, caro de esquecer.

🔗 Continuidade. Nos módulos anteriores a gente usou confirm() cru sem culpa, pra não travar o aprendizado. Aqui ele "cresce" pro componente honesto. Esse é o movimento da lapidação inteira: pega o rascunho que funcionava e troca pela versão redonda. Rascunho na construção, componente na entrega.


6. Busca, filtro e ordenação de ponta a ponta

Quando as listas crescem (e o seed em massa do módulo 03 faz elas crescerem de propósito), rolar a tela atrás de uma convidada é sofrimento. Lapidação adiciona três coisas que andam juntas: busca (achar pelo nome ou código), filtro (ver só confirmados, só pendentes, só quem já entrou) e ordenação (por nome, por status, por hora do check-in).

O ponto que pontua: isso acontece no servidor, não no front. A API recebe o que filtrar e o banco devolve só o que interessa, com o WHERE e o ORDER BY dos módulos 03 e 04. Trazer a lista inteira e filtrar no navegador funciona com 10 convidados e derrete com 500. Mostrar que você sabe onde cada coisa mora (o banco filtra, o back orquestra, o front mostra) é a maturidade que a banca lê como nível 3, e é como escala no mercado.

6.1 O SQL é um só

-- busca por nome OU código, filtro por status, ordenação escolhida
SELECT c.id, c.nome, c.status, ck.registrado_em
FROM convidado c
LEFT JOIN checkin ck ON ck.convidado_id = c.id
WHERE (c.nome LIKE ? OR c.codigo LIKE ?)
  AND (? = 'todos' OR c.status = ?)
ORDER BY c.nome ASC;

O LIKE com %termo% acha pedaço de nome. O truque do (? = 'todos' OR c.status = ?) deixa o mesmo SQL servir pra "todos" e pra um status específico, sem montar query diferente pra cada caso.

6.2 O endpoint muda de sintaxe entre as stacks

Conceito idêntico: ler os parâmetros da URL (?busca=ana&status=confirmado&ordem=nome), montar a consulta com prepared statement (nunca concatenar string, é a porta da SQL injection do módulo 04) e devolver o resultado.

Node + Express + mysql2:

app.get('/convidados', async (req, res) => {
  const { busca = '', status = 'todos', ordem = 'nome' } = req.query;

  // ordenação não entra por prepared statement (é nome de coluna), então valida por lista branca
  const colunas = { nome: 'c.nome', status: 'c.status', checkin: 'ck.registrado_em' };
  const orderBy = colunas[ordem] || 'c.nome';

  const termo = `%${busca}%`;
  const [linhas] = await db.query(
    `SELECT c.id, c.nome, c.status, ck.registrado_em
       FROM convidado c
       LEFT JOIN checkin ck ON ck.convidado_id = c.id
      WHERE (c.nome LIKE ? OR c.codigo LIKE ?)
        AND (? = 'todos' OR c.status = ?)
      ORDER BY ${orderBy} ASC`,
    [termo, termo, status, status]
  );
  res.json(linhas);
});

PHP + PDO:

$busca  = $_GET['busca']  ?? '';
$status = $_GET['status'] ?? 'todos';
$ordem  = $_GET['ordem']  ?? 'nome';

$colunas = ['nome' => 'c.nome', 'status' => 'c.status', 'checkin' => 'ck.registrado_em'];
$orderBy = $colunas[$ordem] ?? 'c.nome'; // lista branca, mesmo motivo

$termo = "%{$busca}%";
$sql = "SELECT c.id, c.nome, c.status, ck.registrado_em
          FROM convidado c
          LEFT JOIN checkin ck ON ck.convidado_id = c.id
         WHERE (c.nome LIKE ? OR c.codigo LIKE ?)
           AND (? = 'todos' OR c.status = ?)
         ORDER BY {$orderBy} ASC";
$stmt = $pdo->prepare($sql);
$stmt->execute([$termo, $termo, $status, $status]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
⚠️Pega-ratão

⚠️ Pega-ratão que é armadilha de segurança. O ORDER BY não aceita prepared statement, porque nome de coluna não é valor. A tentação é concatenar ORDER BY ${req.query.ordem} direto, e aí abriu SQL injection pela porta dos fundos. A defesa é a lista branca: um objeto que mapeia o que a usuária pediu pro nome real da coluna, e cai no padrão se pedir algo fora da lista. Isso a banca de back testa mandando ?ordem=; DROP TABLE.

6.3 No front, debounce pra não sufocar a API

Busca que dispara uma requisição a cada tecla manda dez chamadas pra escrever "roberto". O debounce segura: só busca quando a pessoa para de digitar por um tempinho.

function debounce(fn, ms = 300) {
  let t;
  return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}

const buscar = debounce(async (termo) => {
  const lista = await api(`/convidados?busca=${encodeURIComponent(termo)}`);
  renderLista(lista); // reusa o render dos quatro estados do módulo 05
}, 300);

campoBusca.addEventListener('input', (e) => buscar(e.target.value));
🛠️Gambiarra boa

🛠️ Gambiarra boa: o debounce é uma função de quatro linhas que você cola e usa em busca, em auto-save, em qualquer coisa que dispara com o teclado. Mais um item pra caixa de ferramentas da compressão: pequeno, genérico, sempre útil.

🔗 Continuidade. A renderLista aqui é a mesma dos quatro estados do módulo 05 (carregando, vazio, erro, sucesso). Busca que não acha nada cai no estado vazio com "nenhuma convidada encontrada", não numa tela branca. Lapidação é onde os estados vazios finalmente cobrem todos os cantos, inclusive o resultado de busca sem resultado.


7. Código limpo, refatoração e OO (UC4)

Aqui a lapidação vira pra dentro do código. O sistema funciona, mas o código pode estar embolado, e a banca lê o código (é critério de julgamento explícito). Refatorar é melhorar a organização sem mudar o comportamento. Código limpo não é frescura: é o que faz a banca dar nível 3 em qualidade e é o que faz você achar o bug rápido quando o relógio da prova aperta. As duas coisas ao mesmo tempo.

7.1 Os quatro movimentos que pagam

Não precisa decorar os SOLID inteiros. Na prova, quatro movimentos resolvem quase tudo (é o Clean Code do Martin filtrado pro que a banca vê):

1. Nome que explica. Variável e função com nome que diz o que é.

// antes: ninguém sabe o que é
const x = c.filter(i => i.s === 'confirmado');
// depois: o nome é a documentação
const convidadosConfirmados = convidados.filter(c => c.status === 'confirmado');

2. Função curta que faz uma coisa. Se a função tem "e" no meio do que ela faz (valida e salva e renderiza), são três funções.

// antes: uma função que faz tudo, 40 linhas
async function salvar() { /* valida, monta payload, chama api, trata erro, re-renderiza... */ }
// depois: cada passo com nome, o salvar vira um roteiro legível
async function salvar() {
  const erros = validarFormulario();
  if (erros.length) return mostrarErros(erros);
  const payload = montarPayload();
  await enviarConvidado(payload);
  await recarregarLista();
}

3. DRY, o mesmo código num lugar só. O trecho copiado em cinco telas vira uma função. Se precisar mudar, muda num lugar (menos bug). O api() do módulo 05 já é isso: em vez de fetch repetido com header de token em toda tela, um helper só.

4. Sem número mágico, sem código morto. O 90 do alerta de capacidade (módulo 07) vira const LIMITE_ALERTA = 0.9. A função comentada que ninguém usa mais sai (o Git guarda a história, módulo 02, então apagar é seguro). Código morto confunde a banca e confunde você.

7.2 Um toque de orientação a objetos (UC4)

A UC4 é sobre código orientado a objetos, e dá pra pontuar com pouco: agrupar o que anda junto. Em vez de funções soltas de validação espalhadas, uma peça com responsabilidade clara. Pode ser uma classe ou um módulo, o que a stack pedir:

// validadorConvidado.js — encapsula as regras de uma convidada num lugar só
class ValidadorConvidado {
  constructor(dados) { this.dados = dados; this.erros = []; }

  validar() {
    if (!this.dados.nome?.trim())        this.erros.push('O nome é obrigatório.');
    if (this.dados.email && !pareceEmail(this.dados.email))
      this.erros.push('E-mail inválido.');
    if (this.dados.acompanhantes > 5)    this.erros.push('Máximo de 5 acompanhantes.');
    return this.erros;
  }
}
// uso: const erros = new ValidadorConvidado(payload).validar();

Ganhou três coisas: as regras da convidada moram num lugar (encapsulamento), o resto do código não precisa saber como valida (só chama .validar()), e pra mudar uma regra você sabe exatamente onde ir. Isso é OO servindo a lapidação, não OO por enfeite.

🛠️Gambiarra boa

🛠️ Gambiarra boa: o padrão de fábrica pra mensagem de erro. Se aparece muito { erro: '...', detalhes: [...] } (o formato do módulo 04), uma funçãozinha erroDeValidacao(detalhes) que monta o objeto padroniza a resposta inteira do back. É o embrião do padrão Factory do Gang of Four, na dose que a prova aproveita: uma função que fabrica o objeto sempre igual. Padrão de projeto na competição é isso, a versão de bolso que economiza linha e mantém consistência, não o diagrama de vinte classes.

7.3 Refatorar com segurança: passo pequeno, teste, repete

Refatoração tem uma regra de sobrevivência: um passo de cada vez, testando entre um e outro. Renomeou uma função? Roda, vê se ainda funciona, commita (módulo 02). Só então extrai a próxima. Assim, se quebrou, você sabe exatamente o que quebrou (foi a última coisa) e o git te devolve pra antes num comando.

⚠️Pega-ratão

⚠️ Pega-ratão que já custou competição. Refatorar tudo de uma vez na véspera da entrega, sem testar, e quebrar o que funcionava. Refatoração é atividade de quando sobra tempo e serve pra melhorar com segurança, testando a cada passo e commitando. Nunca a última coisa antes de entregar, com pressa e sem rede. Se faltam 20 minutos e está funcionando, a refatoração fica pra vida; entrega o que roda.

🎯 Como a banca lê isso. Organização e legibilidade são critério explícito de julgamento. Abrir o arquivo e ver nomes que explicam, funções curtas e nenhum trecho repetido cinco vezes é o que separa o 2 do 3 em "qualidade de código". E tem o bônus escondido: código limpo é código onde você acha o próprio bug rápido. Na prova, isso é tempo, e tempo é mais ponto.


8. Padronização: o sistema parecendo feito por uma pessoa só

São duas competidoras codando o mesmo sistema. Sem combinação, sai uma colcha de retalhos: uma escreve nomeConvidado, a outra guest_name; uma indenta com dois espaços, a outra com quatro; um botão de ação é roxo numa tela e azul na outra. A banca percebe na hora, e isso derruba a nota de consistência (a heurística de consistência do Nielsen, módulo 06, agora aplicada ao código inteiro).

Consistência de ponta a ponta é combinar antes:

🛠️Gambiarra boa

🛠️ Gambiarra boa: deixa a máquina padronizar. Um formatador automático (Prettier no Node, ou o formatador do próprio VS Code) alinha indentação e aspas com um atalho, sem discussão. E um arquivo .editorconfig na raiz faz as duas máquinas usarem a mesma configuração. Combina isso na primeira semana de treino, não na última: aí o código já nasce padronizado e a lapidação de outubro nem precisa olhar pra isso.

⚠️ Pega-ratão de calendário (de novo, porque é o que mais mata). Padronizar no fim é retrabalho caro: renomear variável em quinze arquivos na véspera é pedir bug. Padronizar no começo é de graça, porque já sai certo. Essa é a decisão mais barata do treino inteiro e a que mais rende em outubro. Combina o padrão em agosto e cumpre.


9. Mensagens de erro: a passada que mais paga

O módulo 06 já bateu nisso e a lapidação fecha de vez: passar o sistema inteiro trocando todo "deu erro" genérico por uma mensagem que diz o que aconteceu e o que fazer. É o ganho de nível 3 mais barato que existe, porque não muda lógica nenhuma, só troca texto, e sobe a percepção de qualidade da tela toda.

Genérico (nível 2) Útil (nível 3)
"Erro ao salvar" "O nome da convidada é obrigatório."
"Falha" "Esse código de convite não existe."
"Não permitido" "Essa convidada já fez check-in às 19h32."
"Erro 500" "Não deu pra salvar agora. Tenta de novo em instantes."
"Campo inválido" "A data do evento precisa ser futura."

O segredo pra isso ser rápido é o back já mandar a mensagem pronta no formato único do módulo 04 ({ erro: 'texto pra pessoa', detalhes: [...] }) e o front só exibir o que chegou (o avisar() do módulo 05), em vez de cada tela inventar seu próprio texto. Mensagem boa nasce no back, viaja no formato padrão, aparece no componente padrão. Uma passada final só pra revisar as mensagens é das coisas de melhor retorno na reta final.

🎯Foco de prova

🎯 Onde a banca sente. Ela vai errar de propósito só pra ler a mensagem. "Esse código de convite não existe" diz que o sistema entende o que houve. "Erro" diz que o sistema não faz ideia. Mesma falha por baixo, percepção de qualidade oposta. É o critério de julgamento mais fácil de subir com o menor esforço, e o que mais gente esquece porque está ocupada com o caminho feliz.


10. Testar como a banca vai testar (UC11)

O plano de curso tem uma UC inteira de testes (UC11), e ela não pede teste automatizado sofisticado na prova. Pede a mentalidade de teste: antes de dizer "pronto", usar o sistema tentando quebrar. Testar é procurar defeito de propósito, não confirmar que funciona.

10.1 O vocabulário que a banca (e a prova escrita) usa

Vale saber os nomes, porque a Prova Objetiva (módulo 01) cobra e falar a língua sobe a percepção na apresentação (módulo 10):

Termo O que é No Wedding Pass
Caso de teste uma entrada + o resultado esperado "convidada sem nome → mensagem 'nome obrigatório'"
Plano de teste a lista de casos que vou rodar o checklist do sabotador (abaixo)
Caixa-preta testar pela interface, sem olhar o código clicar na tela como a banca
Caixa-branca testar sabendo o código por dentro forçar o catch do check-in duplo
Teste de fronteira os limites de um valor capacidade 0, 1, e a máxima; 5 e 6 acompanhantes
Teste de regressão reconferir o que já funcionava depois de mexer apagar convidada e ver se a lista não quebrou
Smoke test a passada rápida "acende sem soltar fumaça?" login → cadastra → confirma → check-in, ponta a ponta
🎯Foco de prova

🎯 Teste de fronteira é ouro barato. A banca ama o limite: se a mesa é CHECK BETWEEN 1 AND 50, ela testa 0, testa 50, testa 51. Se acompanhantes vai até 5, ela testa 5 e 6. Quem validou a fronteira certa (o "até 5" aceita 5 e barra 6) ganha; quem errou o <= pro < perde. Testar os próprios limites antes é achar esse bug antes da banca achar.

10.2 O checklist do sabotador (o plano de teste da prova)

Esse é o instrumento central da UC11 na prática. Na reta final, uma das duas usa o sistema de propósito errado enquanto a outra anota o que quebra. É simular a banca antes da banca. Cada linha que quebrou vira item pra lapidar:

# Sabotagem Deveria acontecer Quebrou?
1 Enviar formulário todo vazio mensagens de campo obrigatório, nada some
2 Clicar "Salvar" duas vezes rápido salva uma vez só (botão desabilita, módulo 05)
3 Digitar letra no telefone máscara não deixa
4 Fazer check-in da mesma convidada duas vezes "já fez check-in às HH:MM" (corrida, módulo 04/07)
5 Apagar sem confirmar pede confirmação (§5)
6 Abrir uma tela interna sem logar manda pro login (guarda de rota, módulo 05)
7 Login com senha errada "e-mail ou senha inválidos", sem vazar qual
8 Recepção tentando abrir tela de admin 403, escondido no front e barrado no back (módulo 07)
9 Apertar a janela pra celular layout responsivo aguenta (módulo 05/06)
10 Buscar um nome que não existe estado vazio "nenhuma encontrada", não tela branca
11 Confirmar RSVP depois do prazo recusa educada (módulo 07)
12 Cadastrar convidada além da capacidade barra com aviso (regra do módulo 07)
🛠️Gambiarra boa

🛠️ Gambiarra boa: essa tabela vira um .md no repositório e a dupla passa nela antes de qualquer entrega parcial (os TAs) e antes da final. É o plano de teste da UC11 sem nenhuma ferramenta, só disciplina. Testar o próprio sistema com má vontade é o melhor treino de nível 3 que existe, e é reusável: a mesma tabela roda em agosto, em outubro e em dezembro.

🔗 Continuidade. No começo do treino o checklist do sabotador era um parágrafo solto. Aqui ele fecha como o instrumento da UC11: um plano de teste de verdade, com coluna de "quebrou?", que conversa com cada módulo anterior (a corrida do 04, a guarda do 05, os perfis do 07). Ele é o resumo executável do treino inteiro: se tudo passa nessa tabela, o sistema está no nível 3.


11. Como o CIS enxerga a lapidação

Lapidação não tem uma linha própria na planilha CIS. Ela é transversal: sobe as notas de julgamento espalhadas por vários critérios ao mesmo tempo. É por isso que rende tanto. Onde cada frente desse módulo aparece na hora da correção:

O que a banca vê na tela/código Frente deste módulo Critério que sobe
campo com máscara, dado limpo no banco §2 Front + Banco
formulário que barra entrada inválida nas três camadas §3, §4 Front + Back
confirmação antes de apagar, modal acessível §5 UX
busca/filtro/ordenação no servidor §6 Back + Banco
código com nomes claros, funções curtas, sem repetição §7 Config/Qualidade
visual e código consistentes de ponta a ponta §8 UX + Qualidade
mensagem de erro que diz o que fazer §9 UX + Front
sistema que aguenta o uso errado §10 Robustez (todos)
🎯Foco de prova

🎯 A conta que fecha a competição. Repara que a coluna da direita repete Front, UX, Back, Qualidade. É o mesmo acabamento marcando ponto em critérios diferentes de uma vez. Uma máscara bem-feita conta em Front e em Banco. Uma mensagem de erro boa conta em UX e em Front. É por isso que o módulo 01 diz que a diferença entre 2 e 3 é o jogo inteiro: a lapidação é o multiplicador que pega uma hora de trabalho e espalha em pontos por toda a planilha. Nenhuma feature nova rende assim.


12. Autoavaliação do módulo

Responder em voz alta, como se explicasse pra banca. Onde travar, o número da seção diz pra onde voltar.

  1. Qual a diferença entre lapidar e adicionar funcionalidade? Por que a lapidação decide a competição mesmo sem ter peso próprio? (§1, §11)
  2. Explica o padrão "formata pra mostrar, guarda limpo". Por que mandar "(51) 99999-9999" pro banco é erro? (§2)
  3. Escreve de cabeça a máscara genérica por molde de #. Pra que campos ela serve? (§2.1)
  4. Por que /^\d{11}$/ não valida um CPF? O que falta? (§3.1)
  5. Por que um regex de e-mail simples ganha do "perfeito" na prova? (§3)
  6. Preenche a tabela "vazio / errado / absurdo" pro campo capacidade da mesa. (§4)
  7. Onde moram as três camadas de validação e qual o papel de cada uma? Por que validar só no front é armadilha? (§4)
  8. Por que o componente de confirmação devolve uma Promise? Cita os três detalhes de acessibilidade que ele precisa ter. (§5)
  9. Por que busca e filtro devem acontecer no servidor e não no front? (§6)
  10. O que é a lista branca de colunas no ORDER BY e qual ataque ela previne? (§6.2)
  11. O que é debounce e que problema ele resolve na busca? (§6.3)
  12. Cita os quatro movimentos de código limpo que mais pagam na prova, com um exemplo de cada. (§7.1)
  13. Como um pouco de OO (uma classe validadora) ajuda a lapidação? (§7.2)
  14. Qual a regra de ouro pra refatorar sem quebrar? Por que nunca refatorar na véspera sem testar? (§7.3)
  15. Reescreve três mensagens de erro genéricas pra mensagens úteis do Wedding Pass. (§9)
  16. Diferencia caso de teste, teste de fronteira e teste de regressão com um exemplo do Wedding Pass em cada. (§10.1)
  17. Como funciona o checklist do sabotador e por que ele é o plano de teste da UC11? (§10.2)
  18. Por que uma máscara bem-feita rende ponto em dois critérios do CIS ao mesmo tempo? (§11)

Referências do módulo

Do plano de curso (bibliografia das UCs):

Complementares de mercado (código limpo e refino):

💡Nota

Sistema lapidado? Falta a competência que corre por baixo do treino inteiro e vale 15% da prova: 09 — Inglês técnico. Depois dela, o módulo que transforma tudo isso em nota na frente da banca: 10 — Apresentação.

Seletiva 26 · Desenvolvimento de Sistemas · Senac RS