7 pontos por GN⁺ 2025-12-27 | 2 comentários | Compartilhar no WhatsApp
  • Vários gerenciadores de pacotes usaram o Git como se fosse um banco de dados pela conveniência do versionamento e da colaboração, mas, à medida que a escala aumenta, acabam esbarrando em problemas de desempenho e manutenção
  • Cargo, Homebrew e CocoaPods, entre outros, acabaram migrando para índices baseados em HTTP ou CDN devido ao crescimento do índice em Git, à lentidão nas atualizações e à ineficiência em ambientes de CI
  • O vcpkg ainda funciona com base em hashes de árvore do Git, e em ambientes com shallow clone surgem falhas de build e soluções alternativas complexas
  • O sistema de módulos do Go introduziu o GOPROXY e o banco de dados de checksums (sumdb) para remover a dependência de Git e melhorar segurança e velocidade
  • O Git é excelente para colaboração em código, mas volta a se mostrar inadequado para consultas de metadados de pacotes ou para gerenciar registries em grande escala

O fracasso recorrente das tentativas de usar Git como banco de dados

  • O Git é atraente por vantagens como histórico de versões, estrutura distribuída e hospedagem gratuita, mas, quando usado como banco de dados, esbarra em limites de escalabilidade
  • Vários gerenciadores de pacotes adotaram o Git como índice, mas com o tempo os problemas de desempenho e a carga de infraestrutura se agravaram

Cargo

  • O índice do crates.io começou como um repositório Git, e todos os clientes faziam clone completo do conteúdo
    • À medida que o repositório cresceu, surgiu um gargalo de desempenho no libgit2 durante a etapa de resolução de deltas
    • Em ambientes de CI, baixar o índice inteiro a cada build gerava um desperdício enorme
  • Com a RFC 2789, foi introduzido o protocolo sparse HTTP, melhorando o processo para buscar apenas os metadados necessários via HTTPS
    • Em abril de 2025, 99% das requisições já usam o modo sparse
    • O índice em Git ainda existe, mas a maioria dos usuários já não o acessa

Homebrew

  • O GitHub pediu ao Homebrew que parasse de usar shallow clone, apontando que as atualizações eram uma “operação extremamente cara”
    • A pasta .git do homebrew-core chegava perto de 1 GB, e as atualizações sofriam atrasos por causa da resolução de deltas
  • Em fevereiro de 2023, o Homebrew 4.0.0 mudou as atualizações de tap para um modelo de download em JSON
    • Com a remoção do git fetch, a velocidade de atualização melhorou, e o ciclo de atualização automática também mudou de 5 minutos para 24 horas

CocoaPods

  • O CocoaPods, gerenciador de pacotes para iOS/macOS, viu seu repositório Specs, composto por centenas de milhares de podspecs, ficar grande demais
    • Clonar e atualizar levava minutos, e a maior parte do tempo de CI era consumida por operações de Git
  • O GitHub aplicou CPU rate limit, apontando o shallow clone como causa da carga no servidor
  • A equipe adotou medidas temporárias como interromper fetch automático, migrar para clone completo e particionar o repositório
  • A partir da versão 1.8, migrou para distribuição HTTP baseada em CDN, economizando cerca de 1 GB de espaço em disco por usuário e acelerando bastante a instalação

Nixpkgs

  • O Nix já evita clones de Git no lado do cliente ao usar channels baseados em tarball
    • As expressões de pacote são fornecidas por HTTP a partir de S3 e CDN
  • Mesmo assim, a infraestrutura do GitHub sofre com um repositório de 83 GB e 20.000 forks
    • Em novembro de 2025, o GitHub relatou falhas de consenso entre réplicas e erros em tarefas de manutenção
    • O clone local tem 2,5 GB, mas toda a rede de forks pressiona o armazenamento do GitHub

vcpkg

  • O vcpkg, gerenciador de pacotes C++ da Microsoft, controla versões com hashes de árvore do Git
    • Para reproduzir as portas de um commit específico via builtin-baseline, é necessário ter todo o histórico
  • Em ambientes com shallow clone (GitHub Actions, DevContainers), ocorrem falhas de build
    • Como solução, é preciso definir fetch-depth: 0, exigindo o download do histórico completo
  • Pela própria estrutura dos hashes de árvore do Git, não é possível rastrear commits, o que torna o problema impossível de corrigir dentro desse modelo
  • Ele ainda só oferece suporte a registries baseados em repositório Git, sem alternativa em HTTP ou CDN

Sistema de módulos do Go

  • A equipe de engenharia da Grab reduziu o tempo de go get de 18 minutos para 12 segundos após introduzir um proxy de módulos
  • No modelo anterior, era preciso clonar o repositório inteiro de cada dependência para ler o go.mod
  • A equipe do Go se preocupava com a dependência de ferramentas VCS e vulnerabilidades de segurança
  • Desde o Go 1.13, o GOPROXY é o padrão, fornecendo o código-fonte dos módulos e o go.mod via HTTP
    • O sumdb (banco de dados de checksums) garante integridade e persistência dos módulos

Problemas gerais ao usar Git como banco de dados

  • A wiki baseada em Git (Gollum) fica lenta para navegar por diretórios e carregar páginas em repositórios grandes
    • O GitLab planeja descontinuar o uso do Gollum
  • O CMS baseado em Git (Decap) esbarra no limite de 5.000 requisições da API do GitHub
    • Acima de cerca de 10.000 itens, o desempenho cai, e usuários novos com cache vazio podem provocar uma avalanche de requisições
  • A ferramenta GitOps (ArgoCD) enfrenta problemas de estouro de espaço em disco ao clonar repositórios
    • Um único commit invalida todo o cache, e monorepos grandes exigem escalonamento separado

Motivos estruturais pelos quais o Git é inadequado como banco de dados

  • Limite de diretórios: quanto maior o número de arquivos, mais lento tudo fica
    • O CocoaPods gerava objetos de árvore enormes por causa de 16.000 diretórios, e resolveu isso com particionamento baseado em hash
  • Problema de diferenciação entre maiúsculas e minúsculas: o Git diferencia, mas macOS e Windows não
    • O Azure DevOps adicionou um bloqueio no lado do servidor para evitar conflitos
  • Limite de tamanho de caminho: no Windows, o limite de 260 caracteres gera erros em git status
  • Ausência de recursos de banco de dados:
    • Não há restrições CHECK/UNIQUE, bloqueios, índices nem mecanismos de migração
    • Cada gerenciador de pacotes precisa construir seu próprio sistema de validação e indexação

Conclusão

  • O Git é excelente para colaboração em código-fonte, mas inadequado para consultas de metadados de pacotes ou gerenciamento de registries em larga escala
  • A maioria dos gerenciadores de pacotes acaba migrando para índices baseados em HTTP ou bancos de dados
  • As vantagens do Git (histórico de versões, workflow com PRs) são atraentes, mas ele falha como substituto de banco de dados
  • Ao projetar um novo gerenciador de pacotes, mesmo que um índice em Git pareça atraente, ele acaba chegando às mesmas limitações vistas nos casos de Cargo, Homebrew, CocoaPods, vcpkg e Go

2 comentários

 
lamanus 2025-12-28

Em vez de criar um sistema separado para receber contribuições dos colaboradores, usam o git porque é mais simples. Dizem que isso é uma limitação, mas eu particularmente não concordo muito, e também não vejo nenhuma alternativa para os problemas práticos.

 
GN⁺ 2025-12-27
Comentários do Hacker News
  • Isso parece uma espécie de tragédia dos comuns. O GitHub é gratuito e tem muitos recursos excelentes, então todo mundo quer usar. Mas esse tipo de decisão sempre acontece quando há externalidades
    A externalidade que considero mais importante é o tempo do usuário. A maioria das empresas de software só se preocupa com o custo do tempo de engenharia e ignora o tempo do usuário. Focam em desenvolver funcionalidades, mas não otimizam o tempo de interação do usuário. Por exemplo, se eu gastar 1 hora para deixar um app 1 segundo mais rápido, um milhão de usuários economizam 277 horas por ano. Mas como o tempo do usuário é uma externalidade, esse tipo de otimização quase nunca acontece
    No fim, o usuário baixa mais dados à toa e fica esperando, e o desenvolvedor não se responsabiliza por esse desperdício

    • Não sei exatamente o que a expressão “software house” quer dizer, mas a maioria dos produtos de software para consumidores em que trabalhei acompanhava de perto métricas como tempo de inicialização e latência. Isso já é senso comum há décadas. Por exemplo, sempre se ouviu falar que a Amazon perde milhões de dólares por causa de alguns milissegundos no carregamento de página
    • Isso está na mesma linha da frase “velocidade também é uma funcionalidade(feature)”. Só que o tempo do usuário é fortemente influenciado não só pelo desempenho, mas também pelo design de UI
    • Não acho que isso seja uma “tragédia dos comuns”. O GitHub é propriedade da Microsoft, então eles julgaram que conseguem arcar com isso. Um bem comum de verdade teria que ser algo sem dono e de que todos se beneficiassem
    • Quando se pensa fundo nesse problema, vem à mente uma fala do Alan Kay — “se você realmente se importa com software, também precisa fazer o hardware”. Carregar pela rede é, por natureza, uma experiência de usuário ruim. Se você realmente respeita o usuário, precisa criar aplicações local-first(native-first). Mas são pouquíssimas as empresas que respeitam a experiência do usuário a esse ponto
    • O texto “Saving Lives”, do Andy Hertzfeld, é interessante — há uma história sobre “a inicialização do Macintosh está lenta demais. Precisamos deixá-la mais rápida!”
  • Estou criando um Cargo/UV para C. Excelente texto, me identifiquei bastante.
    No começo, operar um registro é realmente muito difícil. Além de escrever código, garantir a qualidade da ferramenta e difundir a comunidade, ainda é preciso pensar em uma infraestrutura que aguente tráfego global. Nessa situação, uma solução baseada em git é atraente
    Mas o problema é o sparse checkout. Quero versionar manifestos de pacote com git, mas é preciso rastrear commits arbitrários, o que é ineficiente. No fim, a estrutura exige empurrar dois commits, então na prática fica inviável
    Acho que a abordagem do Conan é a mais prática. Em vez de reprodutibilidade perfeita, coloca lógica condicional no manifesto. Também permite mapear manifestos por intervalo de versões. Não é perfeito, mas é um compromisso prático e útil.
    Claro, a solução de verdade é usar um banco de dados, mas como ninguém vai pagar a conta do servidor e da manutenção por você, isso é difícil na prática

    • Vendo por outro ângulo, a maioria dos gerenciadores de pacotes bem-sucedidos começou com uma base em Git e migrou para uma estrutura mais eficiente quando isso se tornou necessário
    • Também vale considerar o modelo do Arch Linux AUR. Cada pacote tem seu próprio repositório git independente, contendo só o manifesto. Assim, dá para evitar o problema do monorepo e o pesadelo de referências cruzadas
    • Operar o repositório com um backend HTTP simples como S3 também é atraente. No começo, dá para iniciar com um servidor único e, se ganhar popularidade, procurar patrocinadores e migrar para a nuvem.
      Se o problema for dinheiro e independência, um modelo P2P também é possível. Só que, sem cache de CI, o tráfego pode explodir
    • Se você ainda não tem muitos usuários, preparar desde já uma infraestrutura global é prematuro
    • Não há necessidade de mostrar ao usuário todo o histórico. Com um post-commit hook, dá para renderizar só o estado do HEAD em arquivos estáticos e servir isso como no GitHub Pages.
      A estrutura de espelhos de distribuições Linux como Debian, Fedora e openSUSE também é uma boa referência
  • Este texto está confundindo dois problemas. Um é usar git como banco de dados do índice de pacotes, o outro é buscar o código de cada pacote com git. São coisas separadas.
    O índice pode estar em git e os pacotes em zip/tar, ou o contrário. No caso do Go, a estrutura nem tem índice

    • O autor parece um pouco confuso. Concordo com a ideia de que “nem todo usuário deveria replicar o banco de dados inteiro”, mas isso não significa que não se possa usar o grafo do git para codificar dados.
      Discussões sobre o backend do GitHub ou sobre 20 mil forks não têm relação com a essência do problema. Dá para fazer consultas eficientes de chave-valor sem árvore de trabalho do git.
      A afirmação de que “reescrever o histórico do git é como uma migração de banco” também é estranha. Não seria melhor simplesmente rodar um Postgres?
    • O ponto do texto está no processo de buscar o próprio código, e sim o arquivo go.mod. Por isso a solução foi hospedar o go.mod separadamente
    • No git também é possível buscar apenas um arquivo específico, mas ainda assim isso continua estruturalmente estranho
  • A abordagem de “usar o jeito fácil enquanto funciona, e consertar quando deixar de funcionar” é realista.
    Julia também usa esse método e, como tem cerca de 1/7 da quantidade de pacotes do Rust, ainda não sofre com isso.
    Dá para melhorar baixando apenas o arquivo Registry.toml do topo e depois só os pacotes necessários. Não é um grande problema

    • Julia usa o registro em git apenas como livro-razão oficial(ledger), e os clientes reais usam o Pkg Protocol
    • Também dá para chamar essa abordagem de espírito FAFO(vamos testar e quebrar a cara). É prática, mas pessoalmente não gosto
    • Acho essa atitude antiética. A mentalidade de “vamos fazer do jeito mais fácil agora e consertar depois” acaba só aumentando a dívida técnica.
      A cultura de “Move fast and break things” produziu o software lento e cheio de bugs de hoje
    • Se você deixa para consertar o problema depois, o custo cresce exponencialmente. No fim, a conclusão vira “vamos usar assim mesmo, meio quebrado”. O caso do vcpkg no texto é um exemplo disso
    • Há um exemplo que parece mostrar alguém manipulando UUIDs de propósito, e isso me preocupa um pouco
  • Concordo com a conclusão de que “Git é um excelente banco de dados para começar um gerenciador de pacotes”

    • Mas o ideal é que o cliente não baixe o repositório inteiro e tenha uma camada de cache ou de banco de dados. Em ambientes de CI/CD, eficiência é ainda mais importante
    • O Nixpkgs também teve sucesso graças ao Git. Problemas de escala são um luxo para se preocupar depois
    • Mas antes de idolatrar o Git, seria bom ao menos dar uma olhada em pesquisa sobre bancos de dados
    • Git também pode virar um pesadelo de cadeia de suprimentos. O caso do Leftpad poderia se repetir toda semana
    • Git é péssimo como banco de dados para gerenciadores de pacotes. Só que todo mundo usa porque o GitHub hospeda de graça
  • A visão de “no fim deu certo” também faz sentido. No começo, ajudou bastante na operação, e os problemas de escala puderam ser resolvidos depois

    • Mas alguns projetos não conseguem sair do git por causa de limitações arquiteturais
    • Se você começa com um armazenamento baseado em sistema de arquivos, como o git, depois fica quase impossível trocar de protocolo. É preciso pensar em design centrado em API desde o início
    • Na verdade, ainda existe espaço para usar o git de forma mais eficiente. Defender abandonar o git sem propor uma alternativa é uma conclusão pela metade
    • Também houve a piada irônica: “não escalou de 0 para 1 trilhão de usuários, então é lixo”
  • viés do sobrevivente(survivorship bias) aqui. O índice em git do Cargo virou um problema porque o Cargo deu certo.
    A maioria dos projetos pequenos usa git muito bem como protocolo de distribuição de dados.
    Quando a escala ainda é incerta no início, faz sentido usar git e GitHub para focar no problema principal

    • É preciso tomar cuidado com otimização prematura. Cargo e Homebrew também escolheram o caminho fácil para crescer, e os problemas de escala depois foram “bons problemas” de se ter
  • Sempre fico mais humilde quando vejo na primeira página do HN um texto dizendo “o que você está fazendo agora está errado”.
    Já passei por isso algumas vezes. Desta vez foi com um texto sobre PG Notify.
    Mas no momento estou desenvolvendo sozinho e nem sei se o projeto vai dar certo, então distribuir plugins com git é a opção mais realista.
    Ainda assim, se surgirem problemas de escala depois, pretendo voltar a este texto

    • Mesmo agora já dá para evitar algumas armadilhas. Dependência do GitHub e outros tipos de vendor lock-in podem ser problemas ainda maiores
  • Eu pessoalmente hospedo meu código com Forgejo. Ele fica protegido por mTLS, sem exposição externa.
    Mas os módulos Go exigem certificado e, por isso, não reconhecem minha instância do Forgejo.
    Mesmo usando SSH, disseram que ainda seria necessário acesso por HTTPS, então acabei usando uma cópia local com replace directive. É bem incômodo

    • Se você colocar .git no fim do caminho do módulo e configurar $GOPRIVATE, dá para usar autenticação de comando git sem requisições HTTPS. Veja a documentação oficial
    • Se você adicionar o certificado TLS(CA) da instância ao armazenamento de confiança, também dá para baixar via HTTPS
    • Não é verdade que “é preciso acesso HTTP”. Isso pode ser resolvido com um proxy local
    • Com DNS e certificados do Tailscale, dá para conseguir certificados Let’s Encrypt sem exposição externa
  • Não são só gerenciadores de pacotes: muitos projetos pequenos também fazem crowdsourcing de dados em repositórios git.
    A maioria é pequena e não chega a bater nos limites técnicos.
    Mas essa estrutura aumenta a barreira de entrada para quem não é desenvolvedor. Gerenciadores de pacotes são exceção, mas em projetos gerais isso é um problema
    Para ajudar nesse tipo de situação, criei uma biblioteca open source chamada Datatig.
    Os materiais da apresentação relacionada estão aqui. Vou usar este texto como referência para acrescentar também conteúdo sobre escalabilidade no futuro