A arte da Programação Orientada à Gambiarra
O guia definitivo para escrever código que funciona... até funcionar
O que você vai aprender
Não existe problema de software que não possa ser resolvido com mais uma camada de abstração... exceto o problema de ter camadas demais de abstração.
Lei de Hoare (adaptada para POG)
O que é POG?
Programação Orientada à Gambiarra
Uma filosofia de desenvolvimento de software baseada em soluções improvisadas, remendos temporários que viram permanentes e código que funciona por razões que ninguém entende.
Histórico da POG
- 1582 d.C.
- Primeira POG documentada: Gregório XIII corrige o bug do Calendário Juliano com um hotfix de 10 dias
- 2001
- Manifesto Ágil: POG ganha nome científico e metodologia
- Hoje
- POG é patrimônio imaterial da humanidade
Requisitos para se usar POG
- Prazo ontem
- Orçamento de R$ 0,00
- Especificação "flexível" (inexistente)
- Cliente que muda de ideia a cada reunião
- Legado sem documentação
- Time reduzido pela metade no meio do projeto
- Deploy na sexta às 17h
Princípios da POG
- 1. Funciona na minha máquina
- O ambiente de produção é problema do DevOps
- 2. Não mexa no que está funcionando
- Código que funciona é código sagrado, independente de como está escrito
- 3. TODO: refatorar depois
- "Depois" é um país fictício onde ninguém mora
- 4. Copiei do Stack Overflow
- Se funciona lá, funciona aqui. Talvez.
- 5. Comentários são luxo
- O código é autodocumentado. Trust me, bro.
- 6. Versionamento é opcional
- Pastas com nomes como "projeto_v2_FINAL_definitivo_agora_vai" contam como versionamento
Princípios da POG
- 7. Gambiarra funcional > solução elegante
- Se o cliente não reclama, tá bom
- 8. Testes são para fracos
- O usuário é o melhor QA, de graça!
- 9. Documentação é perda de tempo
- Quem vai ler isso mesmo?
- 10. Nunca delete código antigo
- Comenta e deixa lá. Pode ser útil um dia.
- 11. Magic numbers são amigos
- 42, 666, 9999 — cada um tem seu motivo misterioso
- 12. Reinvente a roda
- Mas faça a roda quadrada por economia de material
Técnicas de POG
- Ctrl+C / Ctrl+V Driven Development
- Comentar e reescrever (nunca deletar)
- Try/Catch sem tratamento
- Variáveis globais como arquitetura
- Condicionais aninhadas até o infinito
- Consultas SQL dentro de loop
- Segredos hardcoded no código
Técnica 1: Ctrl+C / Ctrl+V Driven Development
A técnica mais antiga e honrada da POG
// Função para calcular imposto sobre produto A
function calcularImpostoA(valor) {
return valor * 0.15 + valor * 0.07 - valor * 0.02;
}
// Função para calcular imposto sobre produto B
// TODO: refatorar (há 3 anos)
function calcularImpostoB(valor) {
return valor * 0.15 + valor * 0.07 - valor * 0.02; // igual ao A mas deixa separado
}
// Função para calcular imposto sobre produto C
// Cópia do B com ajuste urgente de sexta-feira
function calcularImpostoC(valor) {
return valor * 0.15 + valor * 0.07 - valor * 0.02; // alguém sabe o que é esse 0.02?
} Técnica 2: Comentar e Nunca Deletar
Código comentado é histórico vivo
def processar_pedido(pedido):
# VERSÃO 1 - não apagar, pode precisar
# resultado = calcular_v1(pedido)
# VERSÃO 2 - funcionou por 2 semanas
# resultado = calcular_v2(pedido)
# VERSÃO 3 - o Marcos disse pra não usar
# resultado = calcular_v3(pedido)
# VERSÃO FINAL (por enquanto)
resultado = calcular_v4(pedido)
# if resultado < 0: # isso não pode acontecer... né?
# resultado = 0 # hotfix do cliente VIP
return resultado Técnica 3: Try/Catch Filosófico
Se der erro, fingir que não deu
public void processar() {
try {
// 300 linhas de lógica crítica aqui
fazerCoisaImportante();
salvarNoBancoDeDados();
enviarEmailParaCliente();
debitarContaBancaria();
} catch (Exception e) {
// TODO: tratar isso depois
// System.out.println(e.getMessage()); // comentado pra não sujar o log
// logger.error("Erro: " + e); // muito verboso
}
// Se chegou aqui, tá ótimo. Ou não. Quem sabe.
} Técnica 4: Arquitetura Global
Por que passar parâmetros quando você pode usar global?
<?php
// Configurações globais da aplicação
$usuario_atual = null;
$conexao_db = null;
$ultimo_erro = "";
$flag_debug = true; // não esquecer de mudar pra produção
$contador_tentativas = 0;
$dados_cache = array(); // invalidar manualmente quando necessário
$modo_manutencao = false; // NUNCA setar isso como true em prod
function fazerLogin($user, $pass) {
global $usuario_atual, $conexao_db, $ultimo_erro, $flag_debug;
// acessa globais sem pudor
} Técnica 5: Pirâmide da Vergonha
Também conhecida como "Callback Hell" ou "Arrow Anti-Pattern"
function validarPedido(pedido) {
if (pedido) {
if (pedido.cliente) {
if (pedido.cliente.ativo) {
if (pedido.itens && pedido.itens.length > 0) {
if (pedido.total > 0) {
if (pedido.formaPagamento) {
if (pedido.formaPagamento !== 'boleto') {
if (estoqueDisponivel(pedido)) {
return processarPedido(pedido);
}
}
}
}
}
}
}
}
return false; // alguma coisa deu errado, não sei o quê
} Técnica 6: SQL no Loop
O problema N+1 como feature, não como bug
def listar_pedidos_com_produtos():
pedidos = db.query("SELECT * FROM pedidos") # 1 query
resultado = []
for pedido in pedidos: # loop para cada pedido
# 1 query para cada pedido = N queries
produtos = db.query(
f"SELECT * FROM produtos WHERE pedido_id = {pedido['id']}"
)
for produto in produtos:
# mais 1 query por produto = N*M queries no total
estoque = db.query(
f"SELECT qtd FROM estoque WHERE produto_id = {produto['id']}"
)
resultado.append({**pedido, **produto, 'estoque': estoque})
return resultado # com 1000 pedidos e 10 produtos cada: 11001 queries Técnica 7: Segredos em Evidência
Por que usar variáveis de ambiente se você pode hardcodar tudo?
// config.js - commitado no repositório público
const config = {
db: {
host: 'prod-db.empresa.com.br',
user: 'admin',
password: 'Senha@123', // trocar depois do deploy
database: 'producao'
},
api: {
key: 'sk-1234567890abcdef', // chave de prod
secret: 'super_secret_key_nao_compartilhar'
},
jwt: {
secret: 'minha_chave_jwt_secreta_123' // TODO: usar env var
}
}; Gambi Design Patterns
Os padrões que os livros não ensinam (mas todo dev conhece)
- 🔥 Apagador de Incêndio — resolve sem entender
- 👻 Código Fantasma — comentado mas presente
- 🪄 Número Mágico — constantes sem nome
- 🍝 Código Espaguete — fluxo indecifrável
- 🧱 Gambiarra Estrutural — ifs que sustentam tudo
- 🚀 Gambiarra Rocket — funciona e ninguém sabe por quê
- ⚰️ Código Zumbi — morto mas ainda executa
🔥 Apagador de Incêndio
Resolver sem entender o problema
// Bug: aplicação trava às 3h da manhã
// Solução POG encontrada às 3h15
// ANTES
function sincronizarDados() {
processarTudo();
}
// DEPOIS (funciona, não pergunte por quê)
function sincronizarDados() {
setTimeout(() => {
processarTudo();
}, 1000); // magia
}
👻 Código Fantasma
O código que não vive, mas também não morre
class ProcessadorPedidos:
def processar(self, pedido):
resultado = self._calcular(pedido)
# versão antiga - não apagar, o João disse que pode precisar
# def processar_legado(self, pedido):
# r = calcular_legado(pedido)
# if r.status == 'ok': # isso não funciona mais
# return r
# return None
# gambiarra emergencial de 2019 - remover quando refatorar
# if resultado and hasattr(resultado, 'valor'):
# resultado.valor = resultado.valor * 1.1 # ajuste fiscal temporário
return resultado 🪄 Número Mágico
Constantes sem nome, razão ou documentação
public class CalculadoraFiscal {
public double calcularImposto(double valor, String estado) {
if (estado.equals("SP")) {
return valor * 0.18 + 3.50 + (valor > 1000 ? 15.75 : 0);
} else if (estado.equals("RJ")) {
return valor * 0.20 - 2.30;
} else if (estado.equals("MG")) {
return valor * 0.17 + (valor > 500 ? 8.90 : 4.45);
}
// Para outros estados: fórmula descoberta por tentativa e erro
return valor * 0.19 + 7.77; // esse 7.77 funciona, não questione
}
} 🍝 Código Espaguete
Fluxo de controle que parece um prato de macarrão
function validar(x) {
inicio:
if (!x) goto fim;
if (x.tipo === 'A') {
if (x.valor > 100) {
x.status = 'ok';
goto salvar;
} else {
goto inicio; // tenta de novo? por quê?
}
}
if (x.tipo === 'B') goto processar_b;
goto fim;
processar_b: x.status = 'pendente'; // nunca chega aqui
salvar: salvarNoDB(x);
fim: return x;
} 🧱 Gambiarra Estrutural
O IF que sustenta toda a aplicação
def processar_tudo(dados, modo, versao, cliente_id, flag1, flag2, flag3):
# Esse if não pode ser removido. Sério.
# Tentamos em 2021 e o sistema caiu.
# Tentamos de novo em 2022. Caiu de novo.
# Ninguém sabe por quê funciona assim.
if cliente_id == 42:
dados['prioridade'] = True
dados['timeout'] = 99999
if flag1 and not flag2:
if versao > 3 or (versao == 3 and modo == 'legacy'):
if not (flag3 and cliente_id % 2 == 0):
return processar_modo_especial(dados)
return processar_normal(dados) 🚀 Gambiarra Rocket
Funciona e ninguém sabe por quê. Não toque.
// NÃO MEXA NESSE CÓDIGO
// Ele funciona desde 2015
// Ninguém sabe por quê
// O dev original saiu da empresa
// Os testes passam
// Os clientes estão felizes
// DEIXA ASSIM
function misterio(a, b) {
return ((a ^ b) +
(a & b) * 2 +
~(a | b) + 1)
>>> 0;
// É soma. Acredite.
}
⚰️ Código Zumbi
Morto mas ainda caminha entre nós
public class FeatureAntiga {
@Deprecated // desde 2018
@SuppressWarnings("all") // cala a boca, compilador
public static String processar(String dados) {
// TODO: migrar para FeatureNova (nunca vai acontecer)
// Essa classe não deveria mais existir
// Mas 3 sistemas legados ainda dependem dela
// E ninguém sabe quais são
if (dados == null) return ""; // gambiarra adicionada em 2019
return dados.trim().toLowerCase()
.replace("ã", "a") // normalização "especial"
.replace("ç", "c"); // para o banco de dados de 1999
}
} 🔒 Singleton POG
Uma instância global mal implementada
<?php
class Database {
private static $instance = null;
private $connection;
private function __construct() {
// credenciais hardcoded (ver Técnica 7)
$this->connection = new PDO(
'mysql:host=prod-server;dbname=app',
'root', // usuário root em prod, claro
'root' // senha root também
);
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self(); // thread-safe? não é PHP, vai
}
return self::$instance;
}
} 📢 Observer POG
Eventos sem controle nem documentação
// Sistema de eventos globais
// Ninguém sabe quem emite o quê nem quem escuta o quê
window.addEventListener('dados-salvos', handleDadosSalvos);
window.addEventListener('dados-salvos', atualizarCache);
window.addEventListener('dados-salvos', enviarMetrica);
window.addEventListener('dados-salvos', (e) => {
// adicionado às pressas em produção
if (e.detail && e.detail.tipo === 'urgente') {
alert('ATENÇÃO: Dados urgentes salvos!'); // alert em prod
}
});
// Em algum lugar do código:
window.dispatchEvent(new CustomEvent('dados-salvos', {
detail: { tipo: Math.random() > 0.5 ? 'urgente' : 'normal' }
})); 🏭 Factory POG
Criação de objetos sem estratégia
def criar_processador(tipo, versao, cliente, modo, flag_legacy=False):
# Factory Method POG: if/elif/elif/elif...
if tipo == 'A' and versao == 1:
return ProcessadorAv1()
elif tipo == 'A' and versao == 2:
return ProcessadorAv2()
elif tipo == 'A' and versao == 2 and cliente == 'especial':
# nunca vai chegar aqui por causa do elif anterior
return ProcessadorAv2Especial() # código morto
elif tipo == 'B':
if flag_legacy:
return ProcessadorBLegado()
return ProcessadorB()
elif tipo == 'C' or tipo == 'D':
return ProcessadorCD() # mesmo processador, different names
else:
return ProcessadorA() # fallback: sempre tipo A 🎯 Strategy POG
Estratégias hardcoded em vez de polimorfismo
public class Calculadora {
private String estrategia; // "simples", "complexa", "nova", "legado"
public double calcular(double valor) {
// Strategy pattern POG: switch gigante
switch (estrategia) {
case "simples": return valor * 1.1;
case "complexa": return valor * 1.1 * 1.05 - 3.50;
case "nova": return valor * 1.15; // igual à complexa, mas nova
case "legado": return valor * 1.08 + 2.00; // não mexer
case "cliente_vip": return valor; // desconto 100%? intencionas?
case "teste": return 0; // esqueceram de remover
default: return valor * 1.1; // igual à "simples"
}
}
} 🎨 Decorator POG
Adicionar comportamento sem refatorar
// Classe original (não pode ser modificada, "funciona")
class Servico {
processar(dados) {
return api.call(dados);
}
}
// POG Decorator: herança para adicionar log
class ServicoComLog extends Servico {
processar(dados) {
console.log('Início'); // log super detalhado
const r = super.processar(dados);
console.log('Fim'); // útil né?
return r;
}
}
// POG Decorator 2: herança para adicionar cache
class ServicoComLogECache extends ServicoComLog {
processar(dados) {
if (window._cache) return window._cache; // cache global, claro
const r = super.processar(dados);
window._cache = r; // nunca invalida
return r;
}
} 🗄️ Repository POG
Acesso a dados sem abstração
class UsuarioRepository:
def buscar(self, id):
# Query direta com SQL concatenado (não, não é seguro)
sql = "SELECT * FROM usuarios WHERE id = " + str(id)
result = db.execute(sql)
# Lógica de negócio dentro do repository
if result and result['ativo'] == 0:
# manda email aqui mesmo, por quê não?
email.send(result['email'], 'Sua conta está inativa')
return None
# Formata dados no repository (responsabilidade errada)
if result:
result['nome'] = result['nome'].title()
result['cpf'] = formatar_cpf(result['cpf'])
return result 📝 Command POG
Histórico de comandos sem undo/redo de verdade
public class GerenciadorAcoes {
private List<String> historico = new ArrayList<>(); // só string, não objeto
public void executar(String acao, Object dados) {
// executa e guarda só o nome (como vai fazer undo?)
if (acao.equals("salvar")) salvarDados(dados);
else if (acao.equals("deletar")) deletarDados(dados);
else if (acao.equals("atualizar")) atualizarDados(dados);
historico.add(acao + " - " + new Date()); // log bonito
}
public void desfazer() {
// TODO: implementar (há 2 anos)
System.out.println("Desfazer não disponível. Contate o suporte.");
// spoiler: o suporte também não sabe como desfazer
}
} 🚦 State POG
Máquina de estados com booleanos avulsos
// Estado do pedido: 4 booleanos em vez de 1 enum
let pedidoCriado = false;
let pedidoPago = false;
let pedidoEnviado = false;
let pedidoCancelado = false;
let pedidoEmAnalise = false; // adicionado depois, não estava no design
function atualizarStatus() {
// Combinações impossíveis mas possíveis no código:
if (pedidoPago && pedidoCancelado) {
// isso não deveria acontecer
pedidoCancelado = false; // workaround
}
if (pedidoEnviado && !pedidoPago) {
pedidoPago = true; // assume que pagou se enviou
}
// 31 combinações possíveis de 5 booleanos. Boa sorte.
} 🪞 Proxy POG
Intermediário que faz mais do que devia
class ServicoProxy:
def __init__(self):
self.servico_real = ServicoReal()
self.tentativas = 0
def chamar(self, dados):
try:
return self.servico_real.executar(dados)
except Exception as e:
self.tentativas += 1
if self.tentativas < 3:
time.sleep(self.tentativas * 5) # backoff manual
return self.chamar(dados) # recursão sem limite
else:
self.tentativas = 0
# falhou 3x? retorna dados fictícios
return {"status": "ok", "dados": [], "gambiarra": True}
# o cliente nunca vai saber 📋 Template Method POG
Template com exceções para cada cliente
public abstract class RelatorioBase {
// Template Method POG: cheia de ifs para casos especiais
public final String gerar(String clienteId) {
String cabecalho = gerarCabecalho();
// exceção cliente A (pediu personalização urgente)
if (clienteId.equals("CLIENTE_A")) cabecalho += "\n[CONFIDENCIAL]";
String corpo = gerarCorpo();
// exceção cliente B (bug reportado, fix rápido)
if (clienteId.equals("CLIENTE_B")) corpo = corpo.replace(",", ".");
String rodape = gerarRodape();
// exceção legada (não sei por quê, mas precisa)
if (clienteId.startsWith("OLD_")) rodape = "";
return cabecalho + corpo + rodape;
}
} 🔄 Iterator POG
Iteração com side effects e surpresas
// Iterator POG: modifica a coleção enquanto itera
const pedidos = obterPedidos();
for (let i = 0; i < pedidos.length; i++) {
const pedido = pedidos[i];
if (pedido.status === 'pendente') {
processar(pedido);
// ops, processarPedido adiciona novos pedidos à lista
// agora o loop nunca termina (às vezes)
}
if (pedido.valor < 0) {
pedidos.splice(i, 1); // remove durante iteração
i--; // "correção" que às vezes funciona
}
if (i > 10000) break; // segurança: se entrar em loop infinito, para
} Conclusões
A POG é inevitável.
Todo sistema tem gambiarra. A questão é se você admite ou não.
O bom desenvolvedor não é o que nunca faz gambiarra — é o que sabe quando fazer e quando refatorar.
A Escala da Honestidade POG
- Nível 1 — Negação
- "Meu código não tem gambiarra, é uma solução criativa"
- Nível 2 — Barganha
- "É temporário, vou refatorar na próxima sprint" (há 3 anos)
- Nível 3 — Aceitação
- "Sim, é uma gambiarra. Funciona. Segue o jogo."
- Nível 4 — Iluminação
- "Documenta a gambiarra, escreve teste pra ela e faz um TODO com data"
- Nível 5 — Mestria POG
- "Transforma a gambiarra em um design pattern e dá uma palestra sobre isso"
Como Conviver com a POG
- Documente — se fez gambiarra, explica por quê
- Isole — gambiarra bem isolada é menos danosa
- Teste — sim, teste sua gambiarra. Ela vai mudar.
- Rastreie — use TODOs com data e nome de quem fez
- Refatore — quando puder, pague a dívida técnica
- Compartilhe — conte pros colegas. Sem vergonha.
- Aprenda — cada gambiarra é uma lição de design
A POG Perfeita
O melhor código é aquele que resolve o problema do cliente hoje, pode ser entendido amanhã e refatorado semana que vem.
Todo dev que já precisou fazer uma gambiarra
Às vezes, a gambiarra certa no momento certo é exatamente o que o projeto precisa.
Créditos
Baseado na apresentação original de
Josenaldo de Oliveira Matos Filho (abre em nova aba)
Reimplementado completamente por
Kevin Barreto (abre em nova aba)
Com acessibilidade completa, responsividade, dark mode,
exemplos modernos e novos design patterns.
Utilizando POG, é claro! 😄
O código legado é a POG de ontem que ainda funciona hoje!
Desenvolvedor mantendo código de 10 anos atrás