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.

Pogramador - mascote da POG, um desenvolvedor com expressão cansada

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
Papa Gregório XIII, pioneiro da POG histórica com o Calendário Gregoriano

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
}
Meme 'This is Fine' - cachorro sentado tomando café com a sala pegando fogo

👻 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.
}
Meme Nick Young confuso com ponto de interrogação, representando desenvolvedor confuso com código misterioso

⚰️ 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.

Homer Simpson se escondendo em um arbusto, representando o desenvolvedor que fez uma gambiarra e está se escondendo

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.

Meme Sneak 100 - personagem furtivo com barra de stealth cheia, representando o desenvolvedor que passou a gambiarra em code review

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