"Faltaram colunas" — a melhor e ao mesmo tempo pior base de código
(jimmyhmiller.github.io)"A tabela merchants2? Sim, criamos a merchants2 porque faltavam colunas na merchants."
- Quando comecei a programar, eu não sabia que as pessoas ganhavam dinheiro com programação
- Aprendi muita coisa no meu primeiro emprego com software, mas a base de código de lá era a pior e ao mesmo tempo a melhor
Bancos de dados vivem para sempre
- Em sistemas legados, o banco de dados faz mais do que apenas armazenar dados. Ele define as restrições do sistema inteiro e é o ponto onde todo o código se encontra
- O SQL Server tem um limite para o número de colunas em uma tabela. Na época eram 1024, hoje são 4096. Como faltaram colunas na tabela Merchants, criaram a tabela Merchants2 (com mais de 500 colunas)
- Merchants e Merchants2 eram o núcleo do sistema. Tudo se conectava à Merchants. Havia outras tabelas normalizadas, mas elas tinham relacionamento de chave estrangeira com Merchants
SequenceKey
- SequenceKey é uma tabela simples com uma única coluna e um único valor
- Ela era usada para gerar IDs. Presume-se que foi criada porque o SQL Server não suportava IDs com incremento automático
- Em todos os stored procedures, buscavam uma chave em SequenceKey, incrementavam e a usavam como ID em várias tabelas. Ela funcionava como um join implícito
Calendar
- Calendar era uma tabela de calendário preenchida manualmente. Quando a Calendar expirava, não era possível fazer login no sistema
- Isso aconteceu alguns anos atrás, e um estagiário preencheu mais 5 anos. Ninguém sabe qual sistema usa isso
Employees
- Todos os dias às 7h15, a tabela employees era apagada e preenchida de novo com um CSV recebido da ADP
- Durante esse processo, não era possível fazer login no sistema. Às vezes esse processo falhava
- Como os dados precisavam ser replicados para a matriz, eles eram enviados por e-mail a uma pessoa que apertava um botão todos os dias para copiar os dados
Banco de dados de substituição
- Você pode pensar que dava para organizar o banco de dados. A empresa também pensou isso
- Havia uma cópia do banco de dados, mas os dados tinham um atraso de cerca de 10 minutos. A sincronização acontecia só em uma direção
- Esse banco de dados era normalizado, então para encontrar um número de telefone em merchants eram necessários 7 joins
Resultados de vendas
- Todo vendedor tinha uma meta mensal chamada "win"
- A tabela que gerenciava isso era extremamente complexa. Um job rodava todos os dias para encontrar linhas adicionadas/modificadas e sincronizá-las com o sistema da matriz
- O problema começou quando um vendedor pediu para alterarem manualmente um registro
- Esse vendedor já tinha atingido seu win e ainda fez uma grande venda naquele mês, então queria mover isso para o mês seguinte
- Um estagiário ficou responsável por isso, e à medida que o boato se espalhou, os pedidos cresceram exponencialmente ao longo de 3 anos
- Em certo momento, havia 3 estagiários cujo trabalho inteiro era escrever instruções SQL. Consideravam difícil demais criar uma aplicação para isso
A base de código
- A primeira base de código com que tive contato estava no Team Foundation Server, um sistema centralizado de controle de versão
- A base em que trabalhei principalmente era metade VB, metade C#. Rodava no IIS e usava session state em todo lugar
- Na prática, isso significava que, se você acessasse uma página pelo Caminho A ou pelo Caminho B, veria coisas bem diferentes naquela mesma página
- Todo framework JavaScript que existia na época estava comitado nesse repositório, geralmente com modificações personalizadas que o autor achava necessárias. knockout, backbone e marionette se destacavam; também havia jquery e plugins de jquery
- Além dessa base de código, havia cerca de 12 serviços SOAP e vários aplicativos nativos para Windows
O disco rígido de Gilfoyle
- Gilfoyle era conhecido como um programador absurdamente rápido. Nunca o conheci, mas o conhecia pelo código dele e pelo código que restou em seu disco rígido
- Munch mantinha o disco rígido de Gilfoyle em RAID em cima da mesa, mesmo anos depois de ele ter saído da empresa
- Isso porque Gilfoyle era famoso por não fazer check-in do código e por criar aplicativos aleatórios e descartáveis para Windows destinados a um único usuário
- Não era raro que um usuário aparecesse com um relatório de bug de um aplicativo que só existia no disco rígido de Gilfoyle
O bug de envio
- Grande parte do meu trabalho era rastrear bugs que o time não queria atribuir a nenhum projeto
- Havia um bug especialmente irritante que aparecia a cada poucos meses: pedidos ficavam presos na fila de envio depois de já terem sido enviados, aparecendo como se ainda não tivessem sido despachados
- Tentamos várias soluções para corrigir isso (scripts SQL, aplicativos Windows etc.). Aconselharam-me a não rastrear a causa raiz, mas eu não consegui evitar
- No processo, aprendi como Gilfoyle pensava. O app de envio carregava o banco de dados inteiro e depois filtrava por data, mantendo todos os pedidos desde a data de início da aplicação
- O app dependia de um serviço SOAP, mas não para fazer coisas típicas de serviço: ele era uma função pura. O cliente causava todos os efeitos colaterais
- Nesse cliente, encontrei uma hierarquia gigantesca de classes, com 120 classes, cada uma com vários métodos, e herança descendo até 10 níveis
- O único problema era que todos os métodos estavam vazios
- Isso servia para criar uma estrutura em que era possível usar reflection. Essa reflection montava uma string separada por pipes (com uma estrutura baseada no banco de dados, mas totalmente estática) e a enviava por socket
- No fim, isso era enviado para o Kewill, um serviço que se comunicava com as transportadoras. O bug acontecia porque o Kewill reutilizava números de 9 dígitos todo mês, e alguém desativou um cron job que apagava pedidos antigos
Caos bonito
- Há muito mais a dizer sobre essa base de código: a equipe de desenvolvedores sênior que passou 5 anos reescrevendo tudo sem lançar nada, ou os consultores da Red Hat que queriam construir um banco de dados único para controlar tudo
- Essa base tinha muitos cantos malucos e muitas razões que justificariam um time dedicado só para recomeçar uma funcionalidade do zero
- Mas a história mais importante é a de Justin melhorando a página Merchants Search. Essa página era a porta de entrada da aplicação inteira
- Todos os atendentes de suporte ao cliente digitavam um ID ou nome enquanto falavam com o lojista ao telefone para encontrar informações. Então iam para uma página gigantesca com todas as informações
- Essa página era densamente informativa da melhor forma possível, cheia de tudo o que era necessário e de todos os links que alguém poderia querer visitar. Mas era extremamente lenta
- Justin era o único desenvolvedor sênior do meu grupo. Ele era inteligente, sarcástico e não se importava muito com o negócio
- Ele falava a verdade, não media palavras e sempre conseguia resolver problemas sozinho mais rápido do que as equipes ao redor
- Um dia, Justin cansou de ouvir o quanto a página de busca de lojistas era lenta e foi lá consertar
- Cada caixa da tela virou seu próprio endpoint. No carregamento, tudo que estava acima da dobra começava a ser buscado e, quando uma parte carregava, mais requisições eram disparadas
- O tempo de carregamento da página caiu de minutos para menos de 1 segundo
Duas formas de desacoplamento
- Justin só conseguiu fazer isso porque não havia um plano mestre para essa base de código
- Não existia um blueprint aproximado ao qual o sistema precisasse se encaixar, nem um formato esperado de API, nem um design system documentado, nem um comitê de revisão de arquitetura para garantir consistência
- O app era uma completa bagunça. Como ninguém conseguia consertá-lo, ninguém tentava. Em vez disso, criávamos nossos próprios pequenos mundos de sanidade
- Esse app monolítico cresceu, puramente por necessidade, nas bordas como um microcosmo de bons e pequenos aplicativos
- Cada pessoa encarregada de melhorar uma parte do app inevitavelmente desistia de tentar desfazer aquela teia e procurava um pequeno canto bom para construir algo novo. Depois, aos poucos, atualizava os links para apontarem para a coisa nova e boa, deixando a antiga órfã
- Isso pode soar como bagunça. Mas era surpreendentemente prazeroso trabalhar assim. A preocupação com duplicação de código desaparecia, a preocupação com consistência desaparecia e a preocupação com escalabilidade também
- O código era escrito para ser usado, para mexer o mínimo possível na área ao redor e para ser facilmente substituível. Nosso código era desacoplado porque simplesmente era mais difícil acoplá-lo
Depois disso
- Desde então, nunca mais tive o privilégio de trabalhar em uma base de código tão impressionantemente feia
- Todas as bases de código feias que encontrei depois disso não conseguiram superar a necessidade de consistência
- Talvez porque os desenvolvedores "sérios" tenham abandonado aquela base de código muito antes. Só sobraram estagiários bagunceiros e desenvolvedores júnior
- Ou talvez porque não existia nenhuma camada intermediária entre os desenvolvedores e os usuários. Não havia tradução, levantamento de requisitos nem cards. Só você parado ao lado da mesa do atendente de suporte perguntando como poderia melhorar a vida dele
- Sinto falta dessa conexão direta. Do feedback rápido, da ausência da necessidade de fazer grandes planos, da ligação simples entre problema e código
- Talvez seja apenas nostalgia ingênua. Mas, assim como às vezes quero voltar para alguns dos piores anos da minha juventude, toda vez que me deparo com "padrões de design enterprise", minha mente volta para aquela base de código bela e terrível
Opinião do GN⁺
- Este texto pode trazer empatia e conforto para desenvolvedores que lidam com sistemas legados. Ele mostra que mesmo uma base de código imperfeita pode ter valor e oferecer muito aprendizado
- Mas os problemas dessa base de código não devem ser romantizados. Quando a dívida técnica se acumula, ela reduz bastante a velocidade de desenvolvimento e dificulta a manutenção. No longo prazo, custa mais caro
- Desistir de melhorar a base de código e continuar acumulando soluções improvisadas não é a resposta. É preciso ter uma estratégia para melhorar gradualmente ou migrar sistemas legados
- A cultura e os processos de desenvolvimento também importam. Seguir boas práticas de engenharia, como code review, projeto de arquitetura e documentação, ajuda a criar bases de código melhores
- A comunicação próxima com os usuários e o feedback rápido são pontos positivos. O ideal é incentivar isso com metodologias como Agile, sem deixar de cuidar da qualidade do código
- No fim, tudo é uma questão de equilíbrio. Em vez de buscar perfeição, o importante é atender às necessidades dos usuários de forma sustentável e gerenciar a dívida técnica
3 comentários
No meu primeiro emprego, trabalhando como desenvolvedor de firmware, a missão que recebi foi analisar o código assembly extraído do hex de um MCU 8051 de um produto sem desenvolvedor nem código-fonte, e reimplementá-lo em C…
Pelo menos existia um produto funcionando, então fui olhando o código, testando o produto e, de um jeito ou de outro, consegui dar conta…
Também já ouvi ameaça de que, numa viagem de trabalho para o interior, eu consertaria aquilo ou iria embora sem um dedo.
Teve vez em que um bug inexplicável era, na verdade, causado pelo elevador atrás da parede onde o equipamento estava instalado.
Também me lembro de ter entrado no local da conferência da APEC em Dongbaekseom, Busan, antes da inauguração oficial, para instalar várias coisas haha
Opiniões no Hacker News
Na primeira empresa, mantinha um aplicativo VB complexo
No primeiro emprego, fez manutenção de um produto legado escrito em COBOL e Java
Refatorou um script Perl com mais de 12.000 linhas
Percebeu a diferença entre teoria e prática
A codebase do cliente Android do Telegram era extremamente complexa
No primeiro emprego, resolveu um problema de memória em um trabalho de relatórios
Foi boa a experiência de resolver problemas falando diretamente com os clientes
Criou um sistema para dar suporte a vários mercados
No primeiro emprego, havia muitas pessoas sem experiência
Explicou como resolver problemas em SQL Server moderno
Tabela de numeração (
SequenceKey) e tabela de dias úteis (Calendar)trazem lembranças. Não sei como fazem hoje, mas antigamente eram tabelas usadas com frequência. Em projetos de SI, a parte comum de negócios implementava essas funcionalidades relacionadas.