Programação JavaScript, contratos e debugging
Antes de pedir a uma IA para escrever uma função, você precisa reconhecer o que essa função deveria fazer. Nesta preparação, uma loja fictícia serve de caso: a cotação recebe itens, calcula subtotal e aplica uma regra de frete. Parece pequeno, mas já envolve representação, controle, contrato, falha e evidência. Um programa útil não é somente uma sequência que termina; é uma sequência cujo resultado pode ser explicado a partir de entradas e regras verificáveis.
Use JavaScript com Node.js e arquivos locais, sem conta de provedor. A unidade prepara a leitura dos exemplos do curso principal e não pressupõe domínio da linguagem. Vamos separar dado, decisão e efeito, escrever uma função curta e aprender a investigar uma divergência. Esta base não substitui uma disciplina completa de programação: seu objetivo é tornar contratos e testes visíveis antes de introduzir modelos e agentes.
JavaScriptProgramaçãoAo terminar esta aula
- Declare unidades e domínio antes de calcular.
- Testes de limite distinguem regras próximas.
- Mantenha cálculo puro e efeitos em uma fronteira explícita.
Valores representam decisões de domínio
FundamentosA loja registra preço em centavos e quantidade em unidades inteiras. O número 5000 significa R$ 50,00 porque o contrato declara essa unidade; o JavaScript não sabe que o valor é dinheiro. Usar 50.00 sem explicar arredondamento pode produzir divergências ao combinar valores decimais. Inteiros em centavos reduzem essa ambiguidade neste domínio, mas também possuem limite de representação: multiplicar duas entradas válidas individualmente pode gerar um resultado fora do intervalo seguro. A validação do cálculo é diferente da validação de cada campo.
Um formulário costuma produzir texto. A quantidade "2" contém caracteres; o número 2 permite operações aritméticas. Aceitar conversões implícitas em todos os lugares torna o contrato dependente do operador: somar pode concatenar, multiplicar pode converter. A alternativa é uma fronteira clara que interpreta a entrada e devolve um valor validado, ou uma mensagem de erro específica. No núcleo desta aula, a string é recusada. Isso não significa que o usuário precise digitar um objeto JavaScript: a interface será responsável pela conversão consciente.
Objetos agrupam dados relacionados: sku identifica o item, quantidade informa unidades e precoCentavos informa o valor por unidade. Um array conserva uma coleção de itens. Nomear esses campos já ajuda a identificar um erro de modelagem: se o preço é total da linha, multiplicá-lo pela quantidade novamente cobra duas vezes. Antes da implementação, escreva um exemplo numérico completo. Duas unidades a 5000 centavos produzem 10000; essa frase estabelece a interpretação que o teste deverá observar.
Controle transforma regras em caminhos
FundamentosUma condição escolhe um caminho com base em uma expressão. Para a política "a partir de 10000", o valor igual ao limiar pertence ao caminho de frete grátis. Escrever somente um teste com 20000 não distingue > de >=. Os três casos 9999, 10000 e 10001 criam uma pequena vizinhança do limite e revelam a ambiguidade. O objetivo da condição é representar a regra do produto, e não escolher uma forma de código que parece familiar. Uma mudança de texto na política pode exigir mudança de teste e implementação.
O laço percorre os itens e acumula o total. Para raciocinar sobre sua correção, use uma afirmação que se mantém: antes da próxima iteração, total contém a soma dos subtotais já processados. No início nenhum item foi processado, portanto total é zero. Cada passo acrescenta exatamente o subtotal corrente; no fim todos foram incluídos. Essa explicação permite perceber o erro de reinicializar total dentro do laço ou de somar apenas o último item. Não é necessário decorar sintaxe para entender por que esses defeitos quebram a regra.
O caminho de falha também faz parte do programa. Uma quantidade negativa não é uma compra com desconto inesperado; é uma entrada fora do domínio. Recusá-la antes da acumulação evita criar um resultado enganoso. O tratamento de erro deve preservar a causa necessária para agir: campo inválido, cálculo fora do limite ou lista vazia são problemas distintos. O controle da linguagem oferece if, loops e exceções; a política da aplicação decide quando utilizá-los.
Funções e contratos permitem compor sem adivinhar
FundamentosUma função recebe argumentos, executa um procedimento e pode devolver um resultado. Ao definir cotar, declaramos um ponto de entrada e uma responsabilidade; definir a função não executa a cotação. O retorno {totalCentavos, freteCentavos} dá à camada seguinte dados explícitos, em vez de depender de uma variável global ou de texto impresso no terminal. Essa interface facilita testar e reutilizar o cálculo numa página, numa API ou numa tool de agente. O contrato precisa incluir pré-condições e resultado, não apenas o nome da função.
Uma função que depende apenas dos argumentos e não altera estado externo é mais fácil de observar: a mesma entrada produz a mesma saída sob a regra declarada. Ela ainda pode receber objetos mutáveis. Por isso, retornar um novo objeto não basta se o corpo também alterou item.quantidade. A responsabilidade pela mutação pertence ao contrato. Separar a consulta ao catálogo do cálculo permite testar o cálculo sem rede, enquanto a consulta recebe seus próprios testes de erro e autoridade.
Teste e debugging ligam hipótese a evidência
AvaliaçãoProgramaçãoUm teste transforma uma expectativa independente em comparação observável. assert.equal verifica um valor; assert.deepEqual verifica a estrutura do resultado no modo estrito; assert.throws exige que um caminho realmente lance erro. Imprimir "teste passou" sem comparação não prova nada. Também é perigoso capturar toda exceção e chamá-la de erro esperado: a exceção pode ter vindo da própria asserção. Use o teste para tornar um defeito visível e preserve a falha até corrigir a causa, sem modificar a expectativa apenas para obter saída verde.
Debugging começa com uma divergência reproduzível: para entrada conhecida, esperávamos frete zero e recebemos 1500. Formule uma hipótese pequena, como igualdade excluída no limiar, e confira o valor de total antes da decisão. Se ele é 10000, a acumulação provavelmente não explica esse caso. Se ele é 5000, investigue o laço e a interpretação dos dados. Um breakpoint ou log temporário deve responder a essa pergunta específica. Corrija a causa, preserve o teste que a revelou e remova registros desnecessários; não imprima dados pessoais para encontrar um erro aritmético.
Exercício aplicado
Uma cotação anuncia frete grátis a partir de R$ 100,00. O código antigo usa total > 10000 e soma quantidades sem validar. Uma compra de exatamente R$ 100,00 recebe frete e uma quantidade "2" chega do formulário. Defina o contrato e corrija o comportamento sem alterar a entrada.
- Antes da resolução, escreva resultados esperados para 9999, 10000 e 10001 centavos.
- Liste tipos e valores recusados; decida se o domínio aceita quantidade fracionária.
- Implemente cálculo separado de leitura de formulário e apresentação.
- Execute asserções nominais, de limite, erro, overflow e preservação da entrada.
Abrir resolução comentada
O requisito inclui o limiar: a comparação correta é >=. Quantidade é inteiro positivo seguro; a string do formulário deve ser convertida numa fronteira explícita e validada, não aceita silenciosamente pelo núcleo. Cada multiplicação e soma precisa permanecer no intervalo de inteiros seguros.
A função não envia pagamento nem altera estoque. Ela devolve fatos para uma camada de apresentação. As asserções de 9999 e 10000 detectam a troca de operador; os casos NaN, Infinity e string detectam um contrato insuficiente. O snapshot da entrada confirma que os objetos recebidos não foram modificados.
const assert=require('node:assert/strict');
function inteiroPositivo(x,nome) {
if(!Number.isSafeInteger(x)||x<=0)throw new Error(nome+' invalido');
return x;
}
function cotar(itens) {
if(!Array.isArray(itens)||itens.length===0)throw new Error('lista invalida');
let total=0;
for(const item of itens) {
if(!item||typeof item.sku!=='string'||!item.sku.trim())throw new Error('sku invalido');
const q=inteiroPositivo(item.quantidade,'quantidade');
const p=inteiroPositivo(item.precoCentavos,'preco');
const parcial=q*p;
if(!Number.isSafeInteger(parcial)||!Number.isSafeInteger(total+parcial))throw new Error('overflow');
total+=parcial;
}
return {totalCentavos:total,freteCentavos:total>=10000?0:1500};
}
const entrada=[{sku:'I2',quantidade:2,precoCentavos:5000}];
const antes=JSON.stringify(entrada);
assert.deepEqual(cotar(entrada),{totalCentavos:10000,freteCentavos:0});
assert.equal(JSON.stringify(entrada),antes);
assert.equal(cotar([{sku:'I2',quantidade:1,precoCentavos:9999}]).freteCentavos,1500);
for(const x of [0,-1,1.5,NaN,Infinity,'2'])assert.throws(()=>cotar([{sku:'I2',quantidade:x,precoCentavos:5000}]),/quantidade/);
assert.throws(()=>cotar([{sku:'I2',quantidade:2,precoCentavos:Number.MAX_SAFE_INTEGER}]),/overflow/);
assert.throws(()=>cotar([]),/lista/);
console.log('contrato, limiar, imutabilidade e entradas adversariais verificados');Como conferir seu resultado
- O contrato declara tipos, unidade monetária e limites numéricos.
- O caso de exatamente 10000 centavos recebe frete zero.
- Todas as entradas adversariais são recusadas sem efeito externo.
- A entrada permanece igual e a falha do teste não é engolida.
Teste sua compreensão
Responda com suas palavras antes de abrir o comentário. Saber explicar uma decisão é parte do domínio.
1. Por que testar 10000 além de 20000?
Porque a igualdade decide a inclusão do limiar.
Os dois operadores > e >= aceitam 20000; somente o caso de fronteira distingue a política.
2. Receber um objeto permite alterá-lo sem consequência para o chamador?
Não. Alterar suas propriedades pode ser observado fora da função.
A interface deve declarar efeitos; o exemplo evita mutação dos itens.
3. Um teste com NaN precisa de qual decisão de contrato?
Recusar o valor como quantidade inválida.
Uma comparação parcial não garante número inteiro finito e seguro; a validação estabelece o domínio.
Seu progresso fica salvo neste navegador. Concluir a leitura não substitui demonstrar o domínio nos exercícios.
Referências e aprofundamento
Documentação oficial e trabalhos originais. As referências registram o escopo e as limitações para você conferir o que sustentam.
- JavaScript Guide: Functions
MDN • consulta: 2026-10-06
JavaScriptFunções, argumentos, retorno e efeitos de mutação em objetos.
Limites: Referencia de linguagem; não define contratos comerciais.
- JavaScript Guide: Control flow and error handling
MDN • consulta: 2026-10-06
JavaScriptCondicionais, laços e exceções.
Limites: Controle sintático não demonstra correção do requisito.
- Node.js24: Assert
Node.js • consulta: 2026-10-06
FundamentosAsserções estritas de valores, estruturas e exceções.
Limites: Exemplos usam APIs estáveis e foram executados com Node24.19.0; documento versionado24.15.0.