Ferramenta de linha de comando em Go para sondagem de schema PostgreSQL. Descobre e valida relacionamentos que existem nos dados mas nunca foram declarados no catálogo.
A especificação completa do produto está em docs/PGFATHOM.md. Este arquivo é o resumo operacional para quem for implementar. Em caso de divergência, docs/PGFATHOM.md vence.
Violação de qualquer uma destas é bug de severidade máxima, não questão de estilo. Toda change precisa ser avaliada contra elas.
- Read-only absoluto. Nenhum código pode emitir statement que altere o banco analisado. A ferramenta gera
.sqlpara o usuário revisar e executar. - Dado do usuário nunca sai. A ferramenta lê valores para comparar chaves. O que sai são contagens, proporções e nomes de objetos. Nenhum valor de tabela pode chegar a saída, log, JSON ou mensagem de erro. Atenção especial a
pg_stats:most_common_valsehistogram_boundssão dados do usuário. - Nenhuma afirmação sem evidência. Toda inferência sai com veredito e a métrica que o sustenta.
- Silêncio nunca é ausência de problema. Tabela pulada por privilégio, candidato que estourou timeout, schema não coberto — tudo aparece no bloco de cobertura.
- Nenhum falso positivo confirmado. Falso negativo é aceitável. Na dúvida entre recuperar mais e nunca errar, nunca errar vence.
| Linguagem | Go, piso 1.25, build na estável corrente |
| Driver | github.com/jackc/pgx/v5 + pgxpool |
| CLI | github.com/spf13/cobra |
| Config | github.com/pelletier/go-toml/v2 |
| Concorrência | golang.org/x/sync/errgroup com SetLimit |
| Log | log/slog (stdlib) |
| Tabela no terminal | text/tabwriter (stdlib) |
| Guia interativo | bubbletea + lipgloss, só no subcomando setup |
| Teste | testing + github.com/google/go-cmp |
| Integração | testcontainers-go, atrás de //go:build integration |
| Release | goreleaser |
| Lint | golangci-lint |
Sem cgo. Cross-compile precisa ser trivial. É por isso que a mineração de SQL usa extrator próprio em vez de pg_query_go.
Sem viper, sem testify. A superfície de configuração é flag, env e um TOML. go-cmp dá diff melhor nas structs do modelo.
A árvore de dependências pequena é requisito de produto, não gosto: o DBA que autoriza rodar isso contra produção abre o go.mod antes de decidir. Dependência nova no binário precisa de justificativa na proposal.
bubbletea e lipgloss são a única exceção aberta até agora, e o custo dela está medido no binário que se publica. Ele sai de 9 para 25 módulos e de 10,4 para 11,2 MB — 16 módulos e 800 KB. É muito, e foi aceito por uma razão específica: o discover tem vinte e uma flags, e a primeira execução de alguém acontece contra um banco que não é dele, com outra pessoa olhando. Um guia que pergunta escopo, modo e destino — e que termina imprimindo o comando que compôs — é o que separa uma ferramenta poderosa de uma ferramenta usável na primeira tentativa.
A primeira estimativa deste custo foi feita com arquivo sintético e build sem strip, e deu 28 módulos e 15,2 MB. Está registrado porque o erro foi na direção alarmista, e porque a lição vale: número de dependência se mede no artefato que sai, não numa aproximação.
bubbles foi recusado apesar de vir da mesma família e custar só 3 módulos a mais. Ele traz atotto/clipboard por causa do campo de texto, e "por que esta ferramenta lê minha área de transferência" é uma pergunta que não se quer responder num issue — a resposta honesta, que é dependência transitiva, não convence quem está decidindo se aponta a ferramenta para produção. Lista e campo de texto são escritos aqui, sobre os eventos de tecla que o bubbletea já entrega.
Nada disso vale para o resto do binário: as camadas que leem catálogo, inferem, validam e reportam continuam sem dependência de interface, e pgfathom discover funciona igual num terminal e num pipe.
Dependência em sentido único. Cada camada testável isolada.
cmd/pgfathom entrada CLI, flags, orquestração
internal/db conexão, pool, políticas de segurança e timeout
internal/catalog leitura de pg_catalog e information_schema
internal/sqlprobe extração de predicados de junção de view e função
internal/model modelo interno, tipos puros, sem I/O
internal/profile perfis de nomenclatura, carregados de arquivo
internal/infer geração e pontuação de candidatos (só metadados)
internal/stats pré-filtro por estatística do planner
internal/validate validação contra dados, amostragem, anti-join
internal/report renderização em terminal, JSON, SQL
internal/model não importa nada das outras camadas. internal/infer, internal/profile e internal/sqlprobe são determinísticos e não acessam banco. internal/validate é a única camada que lê dados de tabela do usuário.
Repositório, module path, binário, pacotes, imagem Docker: tudo minúsculo, pgfathom, sem underscore. Underscore com prefixo pg_ é convenção de extensão que roda dentro do servidor; isto é binário externo.
Module path: github.com/lvcas-dotcom/pgfathom.
Tudo que está na árvore do repositório é em inglês — código, comentários, arquivos de build, workflows de CI e os documentos da raiz. Se um contribuidor consegue abrir, está em inglês.
Duas exceções, ambas deliberadas: as mensagens de commit e o registro de design (docs/PGFATHOM.md e openspec/) são em português. São a história do projeto e o raciocínio dele, não a interface.
Commits em Conventional Commits, em português, sem Co-Authored-By.
As fases estão detalhadas em docs/ROADMAP.md. Uma change do OpenSpec por fase.