DEV Community

Cover image for O Programador Pragmático: como aplico seus conceitos mais de duas décadas depois da primeira leitura
suissAI
suissAI

Posted on

O Programador Pragmático: como aplico seus conceitos mais de duas décadas depois da primeira leitura

Eu li O Programador Pragmático, de Andrew Hunt e David Thomas, há mais de duas décadas.

Na época, muita coisa parecia apenas “boa prática”. Hoje, depois de anos programando, quebrando sistemas, refatorando ideias, criando arquiteturas, automatizando fluxos e tentando transformar código em infraestrutura confiável, eu vejo esse livro de outra forma.

Para mim, ele não é apenas um livro sobre programação.

Ele é um livro sobre postura.

É sobre como um programador pensa quando assume responsabilidade real sobre o que constrói. É sobre parar de tratar código como uma sequência de arquivos e começar a enxergar software como um organismo que precisa ser mantido, observado, corrigido, automatizado e melhorado continuamente.

Depois de mais de 20 anos, eu não uso os conceitos do livro como uma lista de regras. Eu uso como uma lente para resolver problemas.

Responsabilidade pessoal: o código tem autor

Um dos pontos mais fortes do livro é a ideia de responsabilidade pessoal.

Programador pragmático não terceiriza culpa.

Se o sistema que eu construí falhou, eu preciso entender por quê. Não adianta colocar a culpa no framework, na linguagem, no banco, no servidor, no usuário ou no cliente antes de investigar tecnicamente o problema.

Isso não significa assumir culpa por tudo de forma ingênua. Significa assumir autoria.

Quando eu desenho uma arquitetura, escrevo uma regra, defino uma DSL, crio um fluxo de autenticação, monto um pipeline de agentes ou especifico um contrato de sistema, eu parto do princípio de que aquilo precisa ser explicável, testável e auditável.

Se algo quebra, a pergunta não é:

“De quem é a culpa?”

A pergunta é:

“Qual suposição do sistema estava errada?”

Essa mudança é brutal.

Porque a partir do momento em que você para de procurar culpados e começa a procurar suposições falsas, você começa a evoluir como arquiteto.

Teoria das Janelas Quebradas: gambiarra tolerada vira cultura

A teoria das janelas quebradas é uma das ideias mais importantes para qualquer sistema de software.

Um bug óbvio ignorado vira normalidade.

Um código ruim aceito vira padrão.

Uma variável hardcoded deixada “só por enquanto” vira dependência invisível.

Uma configuração manual vira ritual.

Uma exceção sem tratamento vira comportamento esperado.

Esse conceito me influenciou muito na forma como penso sistemas. Hoje, eu não enxergo uma gambiarra apenas como uma gambiarra. Eu enxergo como um sinal cultural dentro do código.

Quando um sistema aceita sujeira demais, ele começa a educar os próximos programadores a também aceitarem sujeira.

Por isso eu tento transformar tudo que é frágil em uma estrutura explícita: configuração, contrato, manifesto, schema, teste, regra, evento, pipeline, validação, spec executável, política de segurança ou documento técnico.

Não é perfeccionismo.

É contenção de entropia.

Aproveitar mudanças: sistemas rígidos morrem cedo

Outra ideia central do livro é não lutar contra mudanças.

Software muda.

Regra de negócio muda.

Cliente muda.

Interface muda.

Banco muda.

Framework muda.

Modelo de IA muda.

Protocolo muda.

A pior arquitetura é aquela que depende da ilusão de estabilidade eterna.

Na minha forma de programar, isso aparece na tentativa de separar responsabilidades em camadas bem definidas: intenção, validação, autenticação, transporte, persistência, evento, observabilidade, automação e política.

Eu tento evitar sistemas onde uma mudança pequena causa um efeito cascata imprevisível.

Se trocar o banco quebra a interface, tem algo errado.

Se mudar uma regra de negócio exige mexer em dez arquivos sem relação direta, tem algo errado.

Se alterar uma configuração exige recompilar código, tem algo errado.

Se uma decisão operacional está escondida dentro de uma variável no meio do código, tem algo errado.

A mudança precisa ser esperada, não tratada como acidente.

DRY: eu repito antes de abstrair

O princípio DRY — Don't Repeat Yourself — costuma ser interpretado de forma superficial como “não repita código”.

Eu não penso assim.

Para mim, o DRY verdadeiro é:

Não duplique conhecimento consolidado.

Isso é muito diferente de “nunca repetir nada”.

Na etapa inicial de uma ideia, eu repito muita coisa de propósito.

Eu repito estruturas.

Repito fluxos.

Repito validações.

Repito formatos.

Repito padrões parecidos em cenários diferentes.

Mas eu faço isso não por descuido. Eu faço para observar.

Antes de transformar algo em padrão, eu quero saber se aquilo realmente é um padrão.

Muita abstração nasce cedo demais. E abstração prematura é uma das formas mais perigosas de acoplamento.

Quando você abstrai antes de entender, você congela uma hipótese como se fosse arquitetura.

Então eu prefiro repetir no começo, comparar os casos reais, entender o que muda, entender o que permanece e só depois consolidar.

O DRY, para mim, acontece depois da validação do comportamento. Eu gosto que chamar DRYS! Quase um DRY + KISS, mas é Don´t Repeat Yourself Stupid! Mas não é amaioria dos programadores que enxerga padroes facilmente e prefere criar um padrão genérico reusável

Primeiro eu observo a repetição.

Depois eu identifico o conhecimento duplicado.

Depois eu transformo em contrato, DSL, manifesto, schema, função, componente, pipeline ou regra.

DRY não é medo de repetir.

DRY é maturidade para saber quando uma repetição virou conhecimento reutilizável.

Everything as Code extremo: desde 2025 eu não escrevo mais valor de variável no código

Um dos pontos em que mais radicalizei minha prática foi no conceito de Everything as Code.

Desde 2025, eu passei a levar isso ao extremo: não escrevo mais nenhum valor de variável diretamente no código.

Valor não pertence ao código.

Valor pertence à configuração, ao ambiente, ao manifesto, ao schema, ao contrato, ao arquivo declarativo, ao secret manager, ao pipeline ou à política.

O código deve expressar comportamento.

A configuração deve expressar contexto.

Quando você coloca valor direto no código, você mistura duas coisas que deveriam ficar separadas:

  1. O que o sistema faz.
  2. Em qual contexto ele está rodando.

Essa separação muda tudo.

Uma URL não deve estar perdida dentro de uma função.

Um token não deve estar no código.

Um nome de instância não deve estar hardcoded.

Um tempo de sessão não deve estar escondido numa constante arbitrária.

Um limite operacional não deve depender de alguém lembrar onde alterar.

Quando tudo vira código declarativo, versionável e auditável, o sistema fica mais fácil de testar, migrar, revisar e automatizar.

Esse é um dos pontos onde minha leitura do Programador Pragmático evoluiu muito. O livro fala sobre automação, ferramentas e responsabilidade. Hoje, para mim, Everything as Code é a consequência natural desses princípios.

Não é apenas infraestrutura como código.

É política como código.

Configuração como código.

Contrato como código.

Validação como código.

Documentação como código.

Teste como código.

Segurança como código.

Intenção como código.

Operação como código.

Quanto menos decisão invisível existe dentro do sistema, mais pragmático ele se torna.


Ortogonalidade: cada coisa precisa quebrar sozinha

Ortogonalidade é uma ideia simples, mas difícil de manter.

Um sistema ortogonal permite que uma parte mude sem destruir outra.

Na prática, isso significa que banco, interface, autenticação, fila, agente, regra de negócio, transporte e observabilidade não deveriam estar misturados como se fossem uma única massa.

Eu tento pensar sistemas como planos separados.

O plano de escrita não é o plano de leitura.

O plano de identidade não é o plano de transporte.

O plano de intenção não é o plano de execução.

O plano de configuração não é o plano de comportamento.

O plano de segurança não é um detalhe no final.

Quando essas coisas ficam misturadas, qualquer mudança vira medo.

Quando ficam separadas, mudança vira manutenção.

Ortogonalidade não é apenas organização bonita de pastas. É capacidade real de alterar o sistema sem gerar colapso.


Programação por coincidência: “funcionou” não é explicação técnica

Um dos vícios mais comuns na programação é aceitar que algo está certo porque funcionou uma vez.

Isso é programação por coincidência.

Rodou localmente.

Passou no teste manual.

Funcionou no meu ambiente.

O usuário clicou e não quebrou.

Mas isso não prova entendimento.

Para mim, código bom precisa ser explicável. Eu preciso saber por que funciona, quais suposições ele faz, quais entradas aceita, quais estados rejeita e quais falhas são esperadas.

Se eu não consigo explicar o comportamento, eu ainda não tenho arquitetura. Tenho sorte operacional.

Isso se tornou ainda mais importante com agentes, IA, automação e sistemas distribuídos. Quanto mais partes autônomas existem, menos aceitável é depender de coincidência.

Por isso eu gosto de contratos, eventos, logs, traces, validações, testes e formatos declarativos.

Eles reduzem a zona de magia.


Ferramentas básicas: domínio real vem do chão

O Programador Pragmático valoriza muito as ferramentas básicas.

Editor.

Terminal.

Controle de versão.

Scripts.

Debuggers.

Logs.

Testes.

Automação.

Isso continua extremamente atual.

Não importa se hoje usamos IA, agentes, copilotos ou modelos locais. Quem não entende o chão da programação fica dependente demais da camada de abstração.

Eu uso IA, mas não trato IA como substituta de fundamento.

A IA pode acelerar escrita, comparação, geração, revisão e exploração. Mas a responsabilidade técnica continua sendo minha.

O programador ainda precisa entender arquivo, processo, rede, permissão, memória, build, deploy, configuração, protocolo, teste e erro.

Ferramenta poderosa sem fundamento vira gerador de acidente sofisticado.


Automação: se repete, vira pipeline

Automação é um dos pontos mais pragmáticos do livro.

Se algo precisa ser feito mais de uma vez, eu começo a desconfiar que deveria virar script, pipeline, teste, comando, job, agente ou configuração.

Tarefa manual repetitiva é uma fonte permanente de erro humano.

E pior: tarefa manual repetitiva cria dependência tribal. Só uma pessoa sabe fazer. Só uma pessoa lembra a ordem. Só uma pessoa entende o detalhe.

Isso é frágil.

Automatizar é transformar conhecimento operacional em sistema.

Não é apenas ganhar tempo.

É reduzir variação.

É criar rastreabilidade.

É permitir repetição confiável.

É deixar o processo menos dependente de memória humana.


Prototipagem rápida: rascunho não é produto

Eu gosto muito da ideia de prototipagem rápida, mas com uma distinção importante: protótipo não é produto.

Protótipo serve para aprender.

Serve para testar uma hipótese.

Serve para descobrir se uma ideia tem forma.

Serve para identificar limites.

Serve para perceber que uma abstração ainda não deveria existir.

O erro é transformar protótipo em produção sem pagar a dívida técnica.

Eu uso protótipos para validar comportamento antes de consolidar arquitetura. Isso se conecta diretamente com minha visão de DRY: primeiro eu deixo a repetição aparecer, depois eu extraio o padrão.

O protótipo é o laboratório.

A arquitetura é o que sobra depois que a hipótese sobrevive ao teste.


Paranoia pragmática: o sistema precisa desconfiar de si mesmo

Paranoia pragmática não é pessimismo.

É engenharia.

Sistema bom não presume que tudo vai funcionar.

Ele valida entrada.

Verifica estado.

Confere permissão.

Testa contrato.

Rejeita payload inválido.

Trata erro.

Emite evento.

Registra log.

Expõe trace.

Falha de forma controlada.

Essa mentalidade influencia muito minha forma de programar. Eu não gosto de sistemas que apenas “seguem o fluxo feliz”. O fluxo feliz é só uma parte pequena da realidade.

A realidade é timeout, payload incompleto, token expirado, permissão insuficiente, estado inconsistente, retry duplicado, concorrência, ordem errada, usuário confuso, serviço fora, banco lento e integração quebrada.

Código pragmático precisa sobreviver ao mundo real.


Debugging: eliminar suspeitas, não procurar culpados

Depuração é investigação.

Não é raiva.

Não é chute.

Não é superstição.

Quando algo quebra, eu tento reduzir o espaço de incerteza.

Qual foi a entrada?

Qual era o estado anterior?

Qual contrato foi violado?

Qual evento ocorreu antes?

Qual suposição deixou de ser verdadeira?

Qual camada recebeu algo que não deveria receber?

Qual parte do sistema aceitou uma inconsistência cedo demais?

Esse método muda a forma de resolver problemas. Em vez de sair alterando coisas aleatórias até funcionar, você vai eliminando suspeitas.

Debugging pragmático é quase científico: hipótese, observação, teste, eliminação e causa raiz.


Testes ruthless: testar antes que o usuário teste

O usuário não deveria ser o primeiro testador real do sistema.

Quando o usuário encontra o erro, o erro já passou por várias camadas sem ser barrado.

Passou pelo código.

Passou pela validação.

Passou pelo teste.

Passou pelo deploy.

Passou pela observabilidade.

Passou pela revisão.

Por isso eu gosto da ideia de testar com agressividade.

Teste não é enfeite.

Teste é uma forma de definir comportamento esperado.

É uma forma de proteger decisões.

É uma forma de impedir regressão.

É uma forma de documentar o sistema executando.

E, principalmente, é uma forma de humildade técnica.

Quem testa reconhece que pode estar errado.


Melhoria contínua: estudar não é fase, é manutenção profissional

O livro também fala sobre melhoria contínua.

Isso ficou ainda mais importante hoje.

A tecnologia muda rápido demais. Linguagens, runtimes, bancos, IA, protocolos, autenticação, infraestrutura, segurança, ferramentas e formas de construir software mudam o tempo todo.

Mas melhoria contínua não é sair correndo atrás de toda novidade.

É saber estudar com critério.

Eu tento absorver novas tecnologias perguntando:

Que problema real isso resolve?

Que suposição antiga isso quebra?

O que isso torna mais simples?

O que isso torna mais perigoso?

Isso elimina complexidade ou só muda a complexidade de lugar?

Essa postura evita tanto o conservadorismo preguiçoso quanto o hype vazio.


Comunicação efetiva: código também é conversa

Programar é comunicar.

Com o computador.

Com outros programadores.

Com o usuário.

Com o cliente.

Com o futuro mantenedor.

Com você mesmo daqui a seis meses.

Por isso documentação, nomes, contratos, eventos, mensagens de erro e arquitetura importam tanto.

Um sistema mal comunicado vira um sistema difícil de manter.

Eu vejo cada nome como uma decisão semântica. Nome de função, nome de evento, nome de agente, nome de protocolo, nome de DSL, nome de entidade. Tudo comunica uma intenção.

Quando o nome é ruim, o sistema começa a mentir.

Quando o sistema mente, o programador precisa adivinhar.

E quando o programador precisa adivinhar, a programação por coincidência volta.


O que ficou depois de duas décadas

Depois de mais de 20 anos, o que ficou de O Programador Pragmático para mim não foi uma lista de dicas.

Foi uma postura.

Assumir autoria.

Não tolerar sujeira invisível.

Automatizar o repetitivo.

Prototipar antes de cristalizar.

Testar antes de confiar.

Separar comportamento de configuração.

Evitar coincidência.

Criar sistemas que aceitam mudança.

Comunicar intenção com clareza.

E, principalmente, entender que programar não é apenas escrever código.

Programar é criar estruturas que sobrevivem à mudança, ao erro, ao tempo e às pessoas.

Hoje, quando eu levo Everything as Code ao extremo, quando evito valores hardcoded, quando separo configuração de comportamento, quando repito antes de abstrair, quando transformo padrões em contratos e quando tento desenhar sistemas auditáveis, eu vejo uma linha direta com aquela leitura antiga.

O Programador Pragmático envelheceu bem porque ele não era sobre tecnologia da moda.

Era sobre maturidade técnica.

E maturidade técnica continua sendo uma das coisas mais raras e mais valiosas em software.

Top comments (0)