Prompts para explicar código: do regex ao stack trace

Dezesseis perguntas prontas para a ferramenta que explica código. Cada uma já traz um trecho, um padrão, um comando ou um rastro de erro, e já diz para quem é a explicação e até onde ela deve ir. Troque pelo seu código, toque em «Aplicar» e leia.

Perceber o código dos outros

Entender o código de outra pessoa quando ninguém sobrou para perguntar: uma função sem um comentário, um trecho numa linguagem que você não escreve, a leitura linha a linha e o motivo de estar assim.

Uma função sem comentários

Transforma o código herdado numa frase sobre aquilo que ele calcula de facto.

Prompt

def recalc(items, rate): total = 0 for it in items: if it.status != "cancelled": total += it.price * it.qty if total > 10000: total = total * (1 - rate) return round(total, 2)# herdei isto do programador anterior, sem um único comentário — explica o que esta função calcula e em que condições

Resultado

A função calcula o total da encomenda já com desconto.1. Multiplica preço por quantidade em todas as linhas e salta as canceladas: uma linha com status "cancelled" nunca entra no total.2. Quando o total passa de 10000, aplica-se-lhe rate: um valor de 0.1 significa dez por cento de desconto.3. O resultado é arredondado a duas casas, ou seja, aos cêntimos.O pormenor que só o código conta: o desconto incide sobre o total inteiro e não sobre a parte acima do limite. Uma encomenda de 10001 acaba por ficar mais barata do que uma de 9999.

Código numa linguagem que não conhece

Explica uma construção desconhecida através daquela que já usa na sua própria linguagem.

Prompt

import functoolsdef retry(times): def decorator(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): for attempt in range(times): try: return fn(*args, **kwargs) except TimeoutError: continue raise RuntimeError("all attempts failed") return wrapper return decorator# eu escrevo PHP e vejo um decorador pela primeira vez — explica com conceitos que eu já conheça

Resultado

É um decorador com parâmetro. O que mais se aproxima do que já conhece em PHP é uma função que devolve um closure a envolver outra função.Lê-se de dentro para fora:- retry(3) devolve decorator;- decorator recebe a sua função e devolve wrapper;- a linha @retry(3) por cima de uma função significa exactamente fn = retry(3)(fn) — a partir daí o nome aponta para wrapper.O wrapper chama o original até três vezes e engole TimeoutError sem dizer nada; esgotadas as tentativas, lança RuntimeError. O functools.wraps está ali para que o invólucro conserve o nome e a docstring do original: sem ele, o depurador e qualquer rasto mostrariam sempre wrapper.

Leitura linha a linha

Percorre o trecho linha a linha, para quando não é o conjunto que baralha mas um ponto concreto.

Prompt

def top_customers(orders, n): totals = dict() for order in orders: totals[order.customer_id] = totals.get(order.customer_id, 0) + order.total ranked = sorted(totals.items(), key=lambda pair: -pair[1]) return ranked[:n]# explica linha a linha, sobretudo a do sorted e o sinal menos à frente de pair

Resultado

1. totals = dict() — um dicionário vazio onde se juntam os valores por cliente.2. O ciclo percorre as encomendas. totals.get(id, 0) devolve o acumulado ou zero quando o cliente aparece pela primeira vez, e por isso não é preciso testar à parte a primeira encomenda.3. sorted sobre items() ordena pares de cliente e valor. O menos à frente de pair[1] inverte a ordem: decrescente em vez de crescente, pelo que os maiores clientes ficam em cima.4. ranked[:n] fica apenas com os primeiros n pares.Devolver os pares e não só os identificadores é de propósito: quem chama precisa do valor para o mostrar e assim não tem de o somar outra vez.

Porque está escrito assim

Explica não o que o código faz, mas porque é que o autor escolheu esta forma.

Prompt

_cache = None_lock = threading.Lock()def get_config(): global _cache if _cache is None: with _lock: if _cache is None: _cache = load_config_from_disk() return _cache# porque é que a verificação de None aparece duas vezes, antes do bloqueio e dentro dele

Resultado

É o padrão double-checked locking, e cada uma das verificações existe por uma razão diferente.A primeira é o caminho rápido. Depois de a configuração ter sido carregada uma vez, todas as chamadas seguintes vêem o valor pronto e regressam de imediato sem tocar no bloqueio. Pegar num bloqueio custa tempo em cada chamada, e é mesmo preciso uma única vez na vida do processo.A segunda tem a ver com a corrida. Enquanto a primeira tarefa segura o bloqueio e lê o ficheiro, uma segunda já passou a verificação exterior e espera atrás. Sem a verificação interior voltaria a carregar a configuração e escreveria por cima do resultado alheio.A linha global é o que faz com que a atribuição mude a variável do módulo em vez de criar uma nova variável local.

Erros e stack traces

O texto vermelho que se lê antes de consertar qualquer coisa: a mensagem de erro do aplicativo que caiu com o stack trace inteiro, a instalação de dependências que não passa e um registro de produção.

A aplicação foi abaixo

Lê o stack trace: onde se interrompeu, que linha é a culpada e o que significa o erro em si.

Prompt

Traceback (most recent call last): File "app/orders.py", line 42, in create_order total = calc_total(items) File "app/pricing.py", line 17, in calc_total return sum(item.price * item.qty for item in items)AttributeError: 'dict' object has no attribute 'price'o que aconteceu aqui e em que linha devo procurar a causa

Resultado

AttributeError quer dizer que o objecto não tem nenhum atributo chamado price.Um rasto lê-se de cima para baixo para seguir as chamadas e de baixo para cima para encontrar a causa:- create_order, em orders.py linha 42, chamou calc_total;- calc_total, em pricing.py linha 17, foi buscar item.price e rebentou.Quem falha é a linha 17, mas o problema não está ali. A mensagem diz que items contém dicionários e não objectos, e num dicionário o acesso por ponto não funciona. Ou seja, quem chamou passou os dados tal como vieram do JSON, sem construir um modelo. Corrija-se onde items é montado, ou leia-se o campo como item["price"].

A instalação não passa

Traduz o registo de instalação da língua do gestor de pacotes para a nossa.

Prompt

ERROR: Could not find a version that satisfies the requirement pandas==2.9.1 (from versions: 2.0.0, 2.1.4, 2.2.2)ERROR: No matching distribution found for pandas==2.9.1não consigo instalar as dependências, o que significa esta mensagem

Resultado

Essa versão da biblioteca não existe.O pip foi ao índice de pacotes, encontrou o pandas e listou as versões disponíveis: a mais recente é a 2.2.2. Está a pedir a 2.9.1, que ninguém publica, daí o «no matching distribution».Na prática é quase sempre uma de três coisas: uma gralha no número da versão, um requirements.txt trazido de um projecto mais recente, ou uma versão publicada só para outra versão do Python. A segunda linha não é um problema à parte: é a consequência da primeira.

Uma linha do registo de produção

Explica o que o servidor está mesmo a comunicar quando no registo aparecem um tempo esgotado e três números.

Prompt

2026-09-12T03:14:07Z ERROR db pool: timeout acquiring connection after 30s, active=20 idle=0 waiting=1372026-09-12T03:14:07Z ERROR api POST /orders 503 in 30012ms request_id=8c1f2ao que é que o servidor me está a tentar dizer com estas duas linhas

Resultado

Acabaram as ligações à base de dados.Primeira linha: a pool esperou 30 segundos por uma ligação livre e desistiu. active=20 quer dizer que as vinte estão ocupadas, idle=0 que não sobra nenhuma livre, waiting=137 que há cento e trinta e sete pedidos na fila atrás.A segunda linha é a consequência: o POST /orders ficou pendurado esses mesmos 30 segundos e devolveu 503.Lidas em conjunto: chegam pedidos mais depressa do que a base liberta ligações. A pool não está avariada, está a avisar honestamente que bateu no tecto. O passo seguinte é olhar para as consultas longas que prendem uma ligação e para o tamanho da pool face ao número de processos.

Expressões regulares e comandos

Linhas que funcionam enquanto ninguém encosta nelas: entender uma expressão regular campo a campo, decifrar a regex que veio no arquivo de configuração e o comando de terminal copiado de um tutorial.

O que a expressão regular verifica

Desmonta o padrão em partes e diz que endereços passam e quais são recusados sem razão.

Prompt

^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z][A-Za-z]+$isto está na validação do nosso formulário — o que verifica ao certo e o que deixa passar a mais

Resultado

O padrão verifica a forma de um endereço, não a sua existência.- ^ e $ exigem que corresponda a cadeia inteira e não um pedaço lá dentro.- A parte antes da arroba: letras, algarismos, ponto, sublinhado, percentagem, mais e hífen, pelo menos um carácter.- @ — exactamente uma arroba.- O domínio: letras, algarismos, pontos e hífenes.- No fim, um ponto e uma extensão com duas letras no mínimo.O que passa a mais: dois pontos seguidos, um ponto no início do nome, um domínio que não existe. O que é recusado sem razão: endereços internacionalizados e extensões em alfabetos não latinos. Para um formulário costuma chegar, porque a existência é confirmada de qualquer forma pelo email de verificação.

Uma expressão tirada da configuração

Mostra que partes da linha de registo caem em cada grupo e por que ordem.

Prompt

^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) - - \[([^\]]+)\] "([A-Z]+) ([^ ]+) [^"]+" ([0-9]+) ([0-9]+)$isto parte uma linha do access.log — o que fica em cada grupo

Resultado

A expressão parte uma linha do registo do servidor web em seis grupos.1. O endereço IP: quatro números separados por pontos. O ponto com barra à frente significa um ponto mesmo e não «qualquer carácter».2. A data entre parênteses rectos: lá dentro é admitido qualquer carácter excepto o parêntesis de fecho, e é justamente isso que impede o grupo de engolir o resto da linha.3. O método: letras maiúsculas.4. O caminho: tudo até ao primeiro espaço.5. O código de resposta.6. O tamanho da resposta em bytes.A ordem dos grupos é a ordem dos campos no resultado: o primeiro é o endereço, o quinto é o estado. Uma linha cujo caminho tenha um espaço não corresponde por inteiro e é simplesmente ignorada.

Um comando copiado de um manual

Desmonta a cadeia do terminal antes de carregar em Enter num servidor de produção.

Prompt

tar -czf - ./data | ssh backup@nas "cat > /backups/data-$(date +%F).tar.gz"# encontrei no nosso manual de cópias de segurança — explica o que vai acontecer antes de eu correr isto

Resultado

O comando copia a pasta data para outra máquina sem guardar nada nesta.- tar -czf - monta o arquivo e comprime-o, mas escreve-o na saída padrão em vez de um ficheiro: é isso que o hífen solto quer dizer.- A barra vertical entrega esse fluxo ao comando seguinte.- ssh liga-se à máquina nas com o utilizador backup e arranca lá o cat, que pousa o fluxo num ficheiro.- $(date +%F) mete a data de hoje no nome: sai data-2026-09-13.tar.gz.O arquivo nunca aterra inteiro em lado nenhum: viaja pela rede à medida que é criado, por isso não é preciso espaço livre no disco de origem. Há uma aresta perigosa: se já existir um ficheiro com esse nome, é escrito por cima sem uma palavra.

SQL e consultas à base de dados

Entender uma consulta SQL que ocupa meia tela: o SELECT de relatório com vários JOIN aberto passo a passo, a função de janela explicada pelo que calcula e o ponto exato em que a busca tropeça.

Uma consulta de relatório

Traduz um SELECT comprido numa frase sobre as linhas que vão acabar no relatório.

Prompt

SELECT c.name, date_trunc('month', o.created_at) AS month, count(*) AS orders, sum(o.total) AS revenueFROM orders oJOIN customers c ON c.id = o.customer_idWHERE o.created_at >= now() - interval '1 year'GROUP BY 1, 2HAVING sum(o.total) > 1000ORDER BY revenue DESC-- explica o que devolve esta consulta e porque é que parte dos clientes nunca aparece no relatório

Resultado

A consulta calcula a facturação por cliente, repartida por meses, do último ano.- JOIN liga a encomenda ao cliente. É uma junção normal e não LEFT, por isso os clientes sem uma única encomenda não aparecem de todo.- WHERE corta tudo o que é mais antigo do que um ano.- date_trunc encurta a data até ao dia um do mês: é assim que as encomendas soltas se juntam em grupos mensais.- GROUP BY 1, 2 agrupa pelas duas primeiras expressões da lista: nome do cliente e mês.- HAVING deita fora os grupos abaixo de 1000. Ao contrário de WHERE, este filtro entra depois de a soma estar feita.Uma linha do resultado é um cliente num mês, e não um cliente no conjunto.

Função de janela

Explica o que o OVER calcula e em que é que uma janela difere de um agrupamento normal.

Prompt

SELECT customer_id, created_at, total, sum(total) OVER (PARTITION BY customer_id ORDER BY created_at) AS running_total, row_number() OVER (PARTITION BY customer_id ORDER BY total DESC) AS rnFROM orders-- explica o que calculam estes dois OVER e porque é que as linhas não são juntadas

Resultado

Uma função de janela calcula sobre um grupo sem juntar as linhas: saem tantas quantas entraram, cada uma apenas ganha duas colunas novas.- PARTITION BY customer_id parte a tabela em montes por cliente. Cada monte é a janela.- Na primeira coluna a soma é acumulada: ORDER BY created_at fixa a ordem dentro da janela e, por omissão, contam-se as linhas desde o início da janela até à actual. Na terceira encomenda de um cliente é a soma das três primeiras.- O segundo OVER ordena de outra maneira, por valor decrescente, e row_number distribui as posições: o 1 vai para a maior encomenda desse cliente.É exactamente isto que separa uma janela do GROUP BY: o agrupamento teria deixado uma linha por cliente e perdido as encomendas uma a uma.

Onde a consulta tropeça

Explica a consulta e aponta de caminho aquilo em que ela vai bater quando houver dados a sério.

Prompt

SELECT *FROM orders oLEFT JOIN order_items i ON i.order_id = o.idWHERE DATE(o.created_at) = '2026-09-01' AND o.status != 'cancelled'ORDER BY o.created_at DESC-- na base de testes é instantânea, em produção fica pendurada — explica a consulta e aquilo em que ela bate aqui

Resultado

A consulta vai buscar as encomendas do dia um de Setembro juntamente com todas as suas linhas. LEFT JOIN quer dizer que uma encomenda sem linhas também sai, com as colunas de order_items vazias.Onde tropeça com dados a sério:- DATE(o.created_at) é uma função posta por cima da coluna, e com isso o índice de created_at deixa de servir: a base tem de calcular uma data para cada linha da tabela. Em cem linhas de teste não se nota, em milhões é uma leitura completa.- SELECT * sobre uma relação de um para muitos devolve cada encomenda tantas vezes quantas as linhas que tem. Saem bastantes mais linhas do que encomendas, e a aplicação tem de as voltar a juntar.- As duas tabelas têm colunas com o mesmo nome (id, created_at); no resultado ficam misturadas e pelo nome já não se sabe de que tabela vem cada campo.

Explicar a um principiante e em entrevista

Quando a explicação pesa mais do que o código: explicar para um iniciante o primeiro programa do curso, destrinchar um exercício de entrevista e contar o que o script faz para quem não é da área.

O primeiro programa

Uma explicação para quem escreve código há duas semanas: o que faz cada linha e porque está assim.

Prompt

nums = [4, 8, 15, 16, 23, 42]total = 0for n in nums: if n % 2 == 0: total += nprint(total / len(nums))# comecei agora a aprender Python — explica passo a passo o que acontece aqui

Resultado

O programa soma os números pares da lista e divide o resultado pela quantidade de TODOS os números.1. nums é uma lista de seis números.2. total = 0 é a caixa onde vamos somando. O zero está lá para haver um ponto de partida.3. for n in nums é o ciclo: n passa a ser, à vez, cada número da lista.4. n % 2 == 0 quer dizer que o resto da divisão por dois é zero, ou seja, que o número é par.5. total += n junta o que foi encontrado ao que já estava acumulado.6. A divisão por len(nums) é a divisão pelo comprimento da lista, isto é, por seis.Isto parece um erro de intenção: a lista tem quatro números pares que somam 70, mas divide-se por seis. Se o que se quer é a média dos pares, tem de se dividir pela quantidade de pares.

Um exercício de entrevista

O algoritmo desmontado de modo a poder ser contado em voz alta diante de um quadro.

Prompt

def search(nums, target): lo, hi = 0, len(nums) - 1 while hi >= lo: mid = (lo + hi) // 2 if nums[mid] == target: return mid if nums[mid] > target: hi = mid - 1 else: lo = mid + 1 return -1# perguntaram-me isto numa entrevista — explica a ideia do algoritmo e porque é rápido

Resultado

É uma pesquisa binária num vector ordenado. A ideia é uma só: em cada passo deita-se fora metade do que resta.- lo e hi são os limites do troço onde a resposta ainda pode estar.- mid é o meio. A barra dupla é divisão inteira, por isso sai um índice e não uma fracção.- Se no meio estiver o valor procurado, está feito.- Se o meio for maior, a resposta está à esquerda e hi desloca-se.- Caso contrário está à direita e lo desloca-se.- O ciclo acaba quando os limites se cruzam: o valor não existe e devolve-se -1.O custo é logarítmico: um milhão de elementos são umas vinte passagens em vez de um milhão. A condição obrigatória é que o vector já esteja ordenado, senão o algoritmo devolve calado uma resposta errada.A pergunta seguinte típica numa entrevista: em linguagens com inteiros de tamanho fixo lo + hi pode transbordar, e por isso aí escreve-se lo + (hi - lo) // 2.

Explicar à chefia

O script contado sem um único termo técnico, para falar com quem não lê código.

Prompt

import csv, smtplibrows = list(csv.DictReader(open("orders.csv")))late = [r for r in rows if r["shipped_at"] == ""]body = "Pedidos ainda não enviados: " + str(len(late))smtplib.SMTP("mail.local").sendmail("bot@shop.local", "ops@shop.local", body)# preciso de explicar a uma chefia não técnica o que faz este script

Resultado

O script verifica que encomendas ainda não saíram e comunica o número por email.Passo a passo, sem termos técnicos:1. Abre a tabela de encomendas: um ficheiro vulgar, que o Excel também abriria.2. Fica com as linhas em que a data de envio está vazia, ou seja, encomendas feitas mas não expedidas.3. Conta quantas linhas desse tipo há.4. Envia um email para o endereço do departamento com esse único número no texto.O que a chefia deve saber: o script não altera nada nem atrapalha ninguém, limita-se a ler e a avisar, por isso pode correr de hora a hora. O seu ponto fraco é um: lê um ficheiro, logo mostra o retrato da última exportação e não o deste momento.

Prompts para explicar código: 16 análises prontas | iBro