1 pontos por GN⁺ 2025-09-09 | 4 comentários | Compartilhar no WhatsApp
  • No passado, os ambientes de desenvolvimento Ada já haviam resolvido o problema da formatação de código
  • Os desenvolvedores lidavam com o código na forma de uma representação intermediária (IR) DIANA e cada um o visualizava com as configurações de pretty-printing que preferisse
  • Ainda hoje existem ineficiências e discussões recorrentes por causa de linters e políticas de formatação
  • A workstation Rational R1000 oferecia um ambiente e recursos de desenvolvimento inovadores
  • Ao olhar para a forma como essa questão era tratada uma geração atrás, o texto propõe ideias para mudar as práticas de desenvolvimento de hoje

O debate sobre formatação de código – a solução dos anos 1980

  • O autor menciona sua experiência com o Sr. Paige, professor de ciência da computação que participou do trabalho em um compilador Ada quando o autor estava no ensino médio
  • Em 2016, ao reclamar do incômodo de configurar ferramentas de linter e perguntar “por que ainda temos que passar por esse tipo de problema?”, ouviu falar de uma experiência em que isso já havia sido resolvido há mais de 40 anos

O surgimento de Ada e DIANA

  • Em vez de armazenar código-fonte em texto, os desenvolvedores Ada usavam uma representação intermediária chamada DIANA (Descriptive Intermediate Attributed Notation for Ada)
  • Cada desenvolvedor podia consultar o código do jeito que quisesse, com suas próprias configurações de pretty-printing
  • Não existiam discussões sobre formatação nem problemas com linter, e no editor era possível modificar diretamente a árvore do programa, algo semelhante ao projectional editing moderno

Rational R1000 – um ambiente de desenvolvimento pioneiro

  • A workstation Rational R1000 incorporava vários recursos avançados, como compilação incremental, análise estática, controle de versão e depuração
  • Foi usada em projetos governamentais do DoD, na Estação Espacial Internacional (ISS), no caça F-22 e em outros desenvolvimentos de software importantes, além de ter contribuído para o surgimento da UML
  • Segundo Grady Booch, o R1000 era uma máquina baseada em DIANA: ela não armazenava o código-fonte, usando apenas o pretty-printing da árvore DIANA

Vantagens do desenvolvimento baseado em DIANA

  • Não havia necessidade de debates sobre formatação, configuração de linter ou padronização do ambiente de editor
  • Com aceleração por hardware, oferecia uma experiência de desenvolvimento inovadora, com compilação incremental, refatoração fácil, integração rápida e outros ganhos
  • Isso teve impacto importante na eficiência do desenvolvimento e no trabalho com sistemas de grande porte

O que isso sugere hoje

  • A compilação acelerada por hardware é menos importante hoje, mas o problema da formatação continua longe de estar resolvido
  • O modelo dominante não é o projectional editing nem ambientes live, mas já passou da hora de pensar na adoção de práticas de desenvolvimento mais eficientes e com menos atrito, inspiradas em abordagens do passado

Materiais de referência

  • Ao pesquisar esse tema, o autor cita diversos documentos e relatórios técnicos relacionados ao R1000

4 comentários

 
ndrgrd 2025-09-10

Pelo que sei, já existe a funcionalidade de formatar automaticamente o código compartilhado com configurações padronizadas, e entendo que muitas empresas a utilizam.

 
euphcat 2025-09-10

Parece que a questão não é a formatação automática em si, mas sim que é desnecessário partir da ideia de que uma formatação específica é superior ou passar pelo processo de abandonar a minha formatação e me adaptar a uma formatação estranha. Como a lógica é salvar uma representação intermediária que não fique presa à formatação e depois fazer pretty-printing do jeito que for mais confortável para cada usuário.

 
ndrgrd 2025-09-10

A ideia era dizer que, com formatação automática, dá para fazer a mesma coisa nas linguagens existentes sem recorrer a representações intermediárias, mas a explicação ficou insuficiente.

 
GN⁺ 2025-09-09
Comentários no Hacker News
  • Não entendo por que as pessoas se importam tanto com configuração de linter; isso é claramente uma discussão inútil. Basta decidir um padrão, rodar o linter automaticamente e pronto. Sinto que é mais importante ter tempo para fazer engenharia de software de verdade. Qualquer formato que o time escolher acaba ficando natural em uma semana.
    • Vale lembrar que formatadores de código-fonte e programas de lint são coisas diferentes: o formatador só ajusta o layout do código, enquanto o linter encontra possíveis bugs ou erros no código. Algumas ferramentas fazem os dois, mas isso é detalhe de implementação. Para entender o que é lint, dá para consultar https://en.wikipedia.org/wiki/Lint_(software).
    • Eu passo a maior parte do tempo "lendo" código, e o layout do código afeta a velocidade com que eu leio. Posso me acostumar com layouts diferentes, mas isso não significa que todos sejam igualmente fáceis de ler. Eu memorizo código por padrões visuais e leio o código em blocos com base no layout. Ironicamente, mesmo tendo aphantasia e não conseguindo visualizar coisas com o olho da mente, eu ainda memorizo melhor com pistas visuais.
    • Algumas configurações de linter claramente têm vantagens. Por exemplo, usar trailing comma em uma tabela faz com que, ao adicionar um novo item na última linha, você precise editar só uma linha, e o diff também fica mais simples. Então uma escolha ruim torna minha vida mais difícil. O mesmo vale para listas ou includes ordenados: se não estiverem ordenados, todo mundo vai adicionar no fim e isso gera muitos conflitos de merge. Assim como o autoformatter economiza tempo, a ordenação também deveria economizar. E eu sou sensível a estilos inconsistentes. O mais importante é padronizar em um único estilo.
    • Não ligo tanto para opções específicas, mas acho essencial ter uma configuração comum de linter para reduzir ruído inútil ao revisar PRs. Acho pouco elegante que git e várias PLs trabalhem só em nível de linha. Em 20 anos de carreira, só vi uma vez um exemplo de abordagem mais sofisticada, como na linguagem Ada. É difícil criar algo realmente elegante e eficiente, e quando já existem alternativas boas o suficiente, fica ainda mais difícil isso se espalhar.
    • Eu também já me envolvi nessa discussão quando era mais novo. A razão era simples: eu achava que era a pessoa mais inteligente do time. Achava que minha opinião era a mais importante. Acreditava que todos precisavam ouvir. Mas eu estava errado.
  • O trade-off aqui é que, se você usa um formato diferente de texto, perde usabilidade com ferramentas genéricas como grep, diff, sed e controle de versão. No fim, a dependência de ferramentas, formatos ou extensões de IDE especializadas aumenta. A força da filosofia Unix está na composabilidade via texto puro (plain text). Tem uma pergunta que corta essa discussão com facilidade: se no editor fosse possível configurar livremente só a largura do primeiro nível de indentação, ainda haveria tantos argumentos a favor de tabs?
    • A abordagem baseada em texto tem vantagens, mas sinto que descemos um pouco e deixamos de escalar uma montanha maior, a de projectional editing, e agora estamos perdidos no chão. Todos esses exemplos funcionam melhor em código com informação estrutural. Por exemplo, para busca de símbolos, que uso muito mais do que grep de texto, dá para usar ast-grep; para diff, dá para usar algo como semanticdiff.com, que suporta movimentos estruturais e ignora mudanças sem significado; como alternativa ao sed, dá para usar @codemod/cli. Até em controle de versão há muitos experimentos, como em linguagens tipo Unison, para evitar automaticamente conflitos causados por mudanças semântica-mente irrelevantes, como ordem ou espaços em branco.
    • Essa ideia reaparece várias vezes, mas a relação custo-benefício é ruim, porque em vez de rodar um formatador simples uma vez, você passa a precisar de ferramentas extremamente complexas. No fim, cada desenvolvedor visualizar o código do seu jeito não tem tanta importância assim. Em todos os times por onde passei, definimos regras em conjunto e aplicamos com formatador; no começo nem todo mundo concorda, mas logo se acostuma. Discussão sobre formatação é um desperdício clássico de tempo.
    • grep, diff, sed e merge baseado em linha são, na prática, ferramentas ruins para manipular código. Em vez de ficar preso a essa discussão, deveríamos pensar em ferramentas melhores.
    • Se a representação intermediária (Intermediate Representation) for textual, grep/diff/sed continuam funcionando. Se você usar apenas um formatador baseado em AST, o código pode ser salvo em uma forma normalizada para aquela AST, e o editor pode fazer o parse da AST, mostrar no formato que o usuário quiser e, ao salvar, converter de volta para a forma normalizada.
    • Todo o sistema operacional foi moldado em torno desse tipo de arquivo-fonte. A própria filosofia Unix só realmente brilha quando todas as ferramentas pressupõem texto puro legível e sabem fazer parse dele.
  • Acho que formatação de código-fonte também tem um componente tipográfico claro. Não concordo com a ideia de que seja pura preferência pessoal. Certas formatações são ferramentas para transmitir significado e estrutura de forma eficaz. Se uma ferramenta automática serializa apenas os tokens mínimos e depois reconstrói tudo, esse valor se perde. https://naildrivin5.com/blog/2013/05/17/source-code-typograp...
    • Profissionais de impressão passaram muito tempo aperfeiçoando espaçamento e alinhamento em tabelas e fórmulas. Quem está de fora nem sempre percebe, mas isso era extremamente importante. Acho que ainda existe espaço para evoluir a formatação de código-fonte de forma mais refinada.
    • Por exemplo, há quem reclame que o formatter black do Python quebra consultas do SQLAlchemy em linhas demais, o que acaba piorando a legibilidade.
    • Sempre achei curioso que tanta gente não perceba a importância da tipografia no código.
    • Preocupar-se com tipografia e, ao mesmo tempo, seguir sem senso crítico as convenções das linguagens de programação me parece o pior caminho. Por exemplo, existe uma cultura que aceita tranquilamente palavras como register, enquanto ponteiros continuam sendo representados por asterisco (*). Cada notação poderia ser mais intuitiva e clara, mas insiste-se em formas complexas e difíceis de ler. Símbolos e palavras reservadas também poderiam ser trocados por termos mais familiares e naturais, mas há apego excessivo à tradição e às convenções antigas, sacrificando a legibilidade. Como exemplo, explicam que até a função strcpy em C poderia ser completamente reconstruída com termos e sintaxe mais claros e fáceis de ler.
    • Depois de explicar que em C uma declaração de parâmetro é composta por modificadores, tipo de dado e nome, a pessoa critica a legibilidade de declarações complexas como char *argv[]. Também argumenta que o estilo de formatação à la C++ — por exemplo, char* a, b — pode induzir ao erro, então esse estilo deveria ser evitado.
  • Não concordo com a premissa dessa discussão. Formatação de código é um meio de comunicação extremamente importante. Uma boa formatação sinaliza que o desenvolvedor (1) entende a importância da formatação, (2) segue bem as regras, (3) tem bom gosto e (4) possui discernimento para lidar com exceções. Essas quatro qualidades afetam muito mais do que apenas a formatação; impactam a capacidade geral de desenvolvimento. O problema é que autoformatters e regras de linter enfraquecem esse sinal e, como na lei de Goodhart, acabam esvaziando o propósito original.
    • O post é curto e simples, então vale a pena ler diretamente. Em vez de reagir de imediato só pelo título, seria bom entender o contexto das palavras "should" e "unnecessary".
    • Acho que quem é indiferente à formatação provavelmente ou (1) nunca editou um mesmo arquivo com várias pessoas, ou (2) nunca fez merge de branch, ou (3) nunca manteve uma base de código grande, ou (4) nunca fez refatorações em larga escala, ou (5) nunca precisou descobrir histórico de código nem usar ferramentas de diff/comparação, ou (6) nunca desenvolveu ferramentas de automação para codebase, ou (7) é simplesmente alguém que só pensa em si e não em colaboração.
    • Se a resposta a todos esses itens for "não", então basta automatizar a formatação no pass/commit e deixar o CI falhar se a automação não tiver sido aplicada. Em vez de ficar obcecado com detalhes, é melhor seguir o padrão e abrir mão do estilo pessoal; isso inclusive tem vantagens. Se você deixa o padrão default, o código parece familiar para qualquer pessoa que o leia. Além disso, embora formatação e lint sejam coisas diferentes, os dois podem ser resolvidos com ferramentas de automação de uma vez só.
    • Dizem, em tom de brincadeira, que a mesma avaliação poderia ser aprendida com caligrafia manual, então daqui para frente os PRs deveriam ser enviados em letra cursiva.
    • Depender de "bom gosto para escolher boas regras de formatação" tende a trazer mais discussão e perda de tempo. É melhor deixar isso para formatadores embutidos, como em Go e Rust.
  • Já existem tentativas, desde 20 a 25 anos atrás, de armazenar código com base em árvore sintática e tratar o que o ser humano vê como uma renderização disso. Essa linha vem desde os anos 90, quando refactoring começou a ganhar força. IDEs como o Visual Age chegaram a adotar um modelo em que o código era salvo em banco de dados em vez de sistema de arquivos. Intentional programming e model-driven development também fazem parte desse ciclo. A essência do refactoring é transformar ASTs; renomear símbolos fica trivial, sem precisar fazer localizar/substituir no código-fonte. Mesmo assim, as pessoas continuam acostumadas a editar arquivos, e há resistência e atrito para popularizar a ideia de armazenar a estrutura em si. O fato de ainda estarmos discutindo formatação depois de tanto tempo mostra justamente a necessidade dessa alternativa. É comum que nem mesmo renomeação robusta de símbolos seja bem suportada no nível da linguagem ou do editor.
    • Essa opinião mistura camadas de abstração. No fim, a AST também precisa ser salva em arquivo, então isso não é tão diferente de rodar ferramentas conscientes de AST sobre arquivos legíveis por humanos. O formato de armazenamento não é tão importante; o essencial é ter ferramentas mais inteligentes. Citam casos como o Roslyn da Microsoft e a direção de compiladores modernos que tentam interagir com codebases por meio de APIs.
  • Alguém dá um exemplo direto de como certos tipos de formatação não podem ser derivados apenas da AST. Por exemplo, quando há várias atribuições, é diferente alinhar três linhas em coluna, alinhar pelo =, ou usar tabs para evidenciar mais a profundidade estrutural. Dependendo do que o autor quiser enfatizar, pode preferir alinhar números à direita para destacar valores, ou alinhar membros para destacar estrutura. Sem metadados adicionais, argumenta-se que esse tipo de informação não pode ser extraído da AST.
    • Entendo o ponto, mas na prática só uso as duas primeiras formas. O objetivo real era legibilidade, não ênfase. Há perdas na conversão para AST, mas neste caso o ganho é muito maior. Além disso, também dá para preservar esse tipo de ênfase na própria AST. Por exemplo, com algo como setValue([bar, glob], 1) ou uma sintaxe de comentário para sobrescrever estilo, entre outras opções.
    • "Formatação de código desejável" no fim é subjetiva. Como no exemplo, algumas pessoas preferem 2/4/8 espaços, outras preferem alinhamento por colunas. A AST não contém informação de formatação do código-fonte, então não dá para derivar isso automaticamente.
    • O segundo e o terceiro exemplos de formatação, na verdade, seriam problemas de projeto estrutural, como violação da Law of Demeter, e não uma questão de formatação.
  • Projectional Editing também pode ser aplicado a código-fonte textual. Existe até um vídeo mostrando o JetBrains MPS renderizando tabelas dentro do código https://www.youtube.com/watch?v=XolJx4GfMmg&t=63s. Gostaria que a IDE renderizasse dicionários como tabelas. Já existem hoje algumas funções parecidas, como code folding, inlay hints e docstrings renderizadas em HTML https://x.com/efortis/status/1922427544470438381.
  • Há quem defina isso como uma limitação nossa de, neste momento, não conseguirmos aceitar algo mais abstrato do que plain text. Mesmo quando tentamos, acabamos fazendo downgrade para uma projeção em texto puro. Da transmissão em Morse ao Unicode, acumulou-se uma espécie de "gigantesca tabela de consulta (GLUT)", e isso teria moldado a cultura atual de decodificação de símbolos. Se elevamos o nível de abstração, seria possível criar conjuntos de símbolos mais adequados às aplicações, mas faltam ferramentas compatíveis. No fim, tudo é convertido em texto e depois parseado, como em CSV ou Markdown. Até o XML ganhou editores próprios, mas as pessoas ainda preferem editá-lo como plain text. Ainda assim, isso não é totalmente positivo, por causa de problemas de encoding e caracteres especiais.
  • Às vezes surge a dúvida de por que o artefato armazenado precisa ser igual à projeção de código que realmente vemos. Seria ótimo se até o git diff pudesse ser visto como uma projeção de IR (representação intermediária). Com o surgimento de ferramentas AST como treesitter, fica mais fácil imaginar interfaces em que humanos lidem de maneira mais eficiente com AST ou IR. Como exemplo, a estrutura de compilação ordenada do f# ajuda a simplificar code review. Já em linguagens ou estruturas que permitem ordem livre, até para verificar um diff pequeno é preciso ficar navegando por vários lugares para entender o contexto completo da mudança.
  • Compartilham uma frustração com o eslint-config-airbnb. Os problemas mais representativos são #1271 e #1122. A pessoa perdeu mais de uma hora tentando aplicar a configuração do Airbnb a um projeto existente, e sentiu que isso foi improdutivo por causa de regras desnecessárias, embora o código já estivesse perfeito. No fim, desativou essas regras localmente e decidiu nunca mais usar isso em projetos futuros. Esse exemplo mostra como regras ruins de lint podem destruir a produtividade.