1 puntos por GN⁺ 2025-09-09 | 4 comentarios | Compartir por WhatsApp
  • En el pasado, el entorno de desarrollo Ada ya había resuelto el problema del formateo de código
  • Los desarrolladores trabajaban con el código en forma de representación intermedia (IR) DIANA, y cada quien lo visualizaba con la configuración de pretty-printing que prefería
  • Incluso hoy siguen existiendo ineficiencias y discusiones repetitivas por políticas de linter o formateo
  • La workstation Rational R1000 ofrecía un entorno y funciones de desarrollo innovadores
  • Tomando como referencia un enfoque de hace una generación para el problema del formateo de código, se proponen ideas para cambiar las prácticas de desarrollo actuales

El debate sobre el formateo de código: una solución de los años 80

  • El autor menciona su experiencia con Mr. Paige, un profesor de ciencias de la computación que participó en el trabajo de un compilador de Ada cuando él estaba en la preparatoria
  • Cuando en 2016 se quejó de lo incómodo que era configurar herramientas de linter y preguntó: “¿por qué seguimos teniendo que pasar por esto?”, le contaron que este problema ya se había resuelto hace más de 40 años

La llegada de Ada y DIANA

  • En lugar de guardar código fuente en texto, los desarrolladores de Ada usaban una representación intermedia llamada DIANA (Descriptive Intermediate Attributed Notation for Ada)
  • Cada desarrollador podía consultar el código de la forma que quisiera, con su propia configuración de pretty-printing
  • No existían debates de formateo ni problemas con linters, y en el editor se podía modificar directamente el árbol del programa (similar al projectional editing moderno)

Rational R1000: un entorno de desarrollo pionero

  • La workstation Rational R1000 integraba varias funciones avanzadas como compilación incremental, análisis estático, control de versiones y depuración
  • Se utilizó en desarrollos de software críticos como proyectos gubernamentales del DoD, la Estación Espacial Internacional (ISS) y el caza F-22, y también contribuyó al nacimiento de UML
  • Según Grady Booch, la R1000 era una máquina basada en DIANA que no almacenaba código fuente, sino que solo usaba pretty-printing del árbol DIANA

Ventajas del desarrollo basado en DIANA

  • No hacían falta discusiones sobre formateo, configuración de linters ni unificación del entorno de edición
  • Gracias a la aceleración por hardware, ofrecía una experiencia de desarrollo innovadora con compilación incremental, refactorización sencilla y una integración rápida
  • Tuvo un impacto importante en la eficiencia del desarrollo y en el trabajo sobre sistemas grandes

Lo que esto sugiere hoy

  • La compilación acelerada por hardware es menos importante, pero la solución al problema del formateo sigue siendo insuficiente
  • Aunque el enfoque dominante no sea el projectional editing ni los entornos en vivo, quizá ya sea momento de considerar, como en el pasado, la adopción de prácticas de desarrollo más eficientes y con menos discusiones

Material de referencia

  • El autor cita diversos documentos e informes técnicos sobre la R1000 mientras investigaba este tema

4 comentarios

 
ndrgrd 2025-09-10

Según entiendo, el código compartido ya tiene una función para formatearse automáticamente con una configuración unificada, y sé que muchas empresas la usan.

 
euphcat 2025-09-10

Parece que el punto no es el formateo automático, sino que es innecesario tanto asumir que cierto formato es superior como pasar por el proceso de abandonar mi propio formato y adaptarme a uno ajeno. La lógica es que se almacene una representación intermedia que no dependa del formato y luego, según cada usuario, se haga pretty-printing de la manera que le resulte más cómoda.

 
ndrgrd 2025-09-10

Quería decir que, con el formateo automático, se puede lograr lo mismo en los lenguajes existentes sin necesidad de una representación intermedia, pero no lo expliqué lo suficiente.

 
GN⁺ 2025-09-09
Opiniones de Hacker News
  • No entiendo por qué a la gente le importa tanto la configuración del linter; claramente es una discusión inútil. Basta con decidir una sola opción, correr el linter automáticamente y ya. Siento que es más importante tener tiempo para hacer ingeniería de software real. Sea cual sea el formato que el equipo decida usar, uno se acostumbra en una semana.
    • Vale la pena señalar que un formateador de código fuente y un programa de lint no son lo mismo: el formateador solo ajusta el layout del código, mientras que el linter encuentra posibles bugs o errores en el código. Algunas herramientas soportan ambas cosas, pero eso es un detalle de implementación. Sobre qué es lint, se puede consultar https://en.wikipedia.org/wiki/Lint_(software).
    • Yo paso la mayor parte del tiempo "leyendo" código, y el layout del código está relacionado con la velocidad a la que lo leo. Uno puede acostumbrarse a distintos layouts, pero no todos son igual de fáciles de leer. Yo recuerdo el código por patrones visuales, y lo leo dividiéndolo en bloques según el layout. Paradójicamente, aunque tengo aphantasia y no puedo visualizar con el ojo de la mente, recuerdo mejor usando pistas visuales.
    • Algunas configuraciones de linter claramente tienen ventajas. Por ejemplo, usar trailing commas en tablas hace que al agregar un nuevo elemento a la última línea solo tengas que editar una línea, y el diff también queda más simple. Así que una mala elección me complica la vida. Lo mismo pasa con listas ordenadas o include: si no están ordenadas, siempre se agrega al final y eso genera muchos merge conflicts. Igual que con la ventaja de los autoformateadores, también se debería ahorrar tiempo ordenando. Y además soy sensible a los estilos inconsistentes: lo más importante es unificar todo en un solo estilo.
    • No me importan tanto las opciones detalladas, pero sí creo que una configuración común del linter es esencial para reducir ruido innecesario al revisar PRs. Siento que no es elegante que git o varios lenguajes de programación solo trabajen por líneas. En 20 años de carrera solo he visto una vez un ejemplo más sofisticado, como el del lenguaje Ada. Es difícil construir algo realmente elegante y eficiente, y más difícil aún que se difunda ampliamente cuando ya existen alternativas suficientemente buenas.
    • Yo también caí en esta discusión cuando era joven. ¿Por qué? Porque creía equivocadamente que era el más inteligente del equipo. Pensaba que mi opinión era la más importante. Creía que todos debían escucharme. Pero estaba equivocado.
  • El trade-off que hay que considerar aquí es que, si se usan formatos distintos al texto, se reduce la utilidad de herramientas genéricas como grep, diff, sed y el control de versiones. Al final aumenta la dependencia de herramientas o formatos especiales, o de extensiones del IDE. La fortaleza de la filosofía Unix está en la composabilidad mediante texto plano. Hay una pregunta que corta esta discusión de raíz: si en el editor solo se pudiera configurar libremente el ancho del primer espacio, ¿seguirían existiendo más argumentos por parte de quienes prefieren tabs?
    • El enfoque basado en texto tiene ventajas, pero siento que apenas bajamos un poco y no logramos subir a una montaña más alta: la edición projectional. Todos los ejemplos funcionan mejor con código que tiene información estructural. Por ejemplo, para búsqueda de símbolos —que uso mucho más seguido que grep de texto— se puede usar ast-grep; para diff, algo como semanticdiff.com puede soportar movimientos estructurales o ignorar cambios sin significado; como alternativa a sed, se puede usar @codemod/cli. También se ha experimentado bastante en control de versiones en lenguajes como Unison para evitar automáticamente conflictos causados por cambios no semánticos, como orden o espacios en blanco.
    • Esta idea aparece una y otra vez, pero la relación costo-beneficio no es muy buena si para reemplazar una simple pasada de formateador hacen falta herramientas tremendamente complejas. Que cada desarrollador vea las cosas a su gusto en realidad no aporta mucho. En todos los equipos con los que he trabajado, todos acuerdan reglas comunes y las aplican con un formateador; aunque al principio no estén de acuerdo, pronto se acostumbran. Las discusiones sobre formateo son un ejemplo clásico de pérdida de tiempo.
    • grep, diff, sed y los merges por línea en realidad son malas herramientas para manipular código. Creo que, más que debatir esto, deberíamos pensar en herramientas mejores.
    • Si la representación intermedia (Intermediate Representation) se deja como texto, entonces grep/diff/sed pueden seguir funcionando. Si solo se usaran formateadores basados en AST, el código se guardaría en una forma normalizada ajustada a un AST específico, y el editor lo mostraría en el formato que prefiera el usuario después de parsear el AST; al guardar, simplemente se convertiría otra vez a la forma normalizada.
    • Todo el sistema operativo fue construido alrededor de este tipo de archivos fuente. La filosofía Unix solo realmente brilla cuando todas las herramientas piensan en texto plano y saben parsearlo.
  • Creo que definitivamente hay un aspecto tipográfico en el formateo del código fuente. No estoy de acuerdo con la idea de que sea pura preferencia personal. Ciertos formateos son herramientas para transmitir eficazmente significado y estructura. Si una herramienta automatizada solo serializa los tokens mínimos y luego los reconstruye, ese valor se pierde. https://naildrivin5.com/blog/2013/05/17/source-code-typograp...
    • Los profesionales de la impresión han dedicado durante mucho tiempo mucho cuidado al espaciado y alineación de tablas o fórmulas. A alguien de fuera puede no parecerle evidente, pero estas cosas eran muy importantes. Creo que también es posible avanzar hacia un formateo más refinado del código fuente.
    • Por ejemplo, hay quejas de que el formateador black de Python divide consultas de SQLAlchemy en demasiadas líneas y termina reduciendo la legibilidad.
    • Siempre me ha parecido extraño que tanta gente no perciba la importancia de la tipografía del código.
    • Siento que preocuparse por la tipografía y, al mismo tiempo, seguir acríticamente las convenciones de los lenguajes de programación es el peor camino. Por ejemplo, existe una cultura que usa sin problema palabras como register, mientras que los punteros se representan con un asterisco (*). Cada notación podría ser más intuitiva y clara, pero aun así se insiste en formas complejas y difíciles de leer. Los símbolos o palabras reservadas también podrían reemplazarse por términos más familiares y naturales, pero se sacrifica legibilidad por apego excesivo a convenciones tradicionales o existentes. Como ejemplo, se explica que la función strcpy de C podría reconstruirse por completo con términos y sintaxis más claros y fáciles de leer.
    • Después de explicar que las declaraciones de parámetros en C se componen de modifiers, tipo de dato y nombre, critica el problema de legibilidad de declaraciones complejas como char *argv[]. Y también cree que el estilo de formateo tipo C++ —por ejemplo char* a, b— puede inducir a confusión, así que debería evitarse.
  • No estoy de acuerdo con la premisa de esta discusión. El formateo del código es un medio de comunicación muy importante. Un buen formateo funciona como señal de que el desarrollador (1) reconoce la importancia del formateo, (2) sigue bien las reglas, (3) tiene buen gusto y (4) cuenta con el criterio para responder bien a situaciones excepcionales. Estas cuatro cosas son capacidades importantes que influyen en la habilidad general del desarrollador, más allá del simple formateo. Pero los autoformateadores o las reglas de linter debilitan esta señal y, como en la ley de Goodhart, hacen que se pierda la intención original.
    • Recomienda leer directamente el blog porque es corto y simple; no quedarse en una reacción inmediata basada solo en el título, y entender el contexto de las palabras "should" y "unnecessary".
    • Piensa que a quien le da igual el formateo probablemente (1) nunca ha editado un archivo entre varias personas, (2) nunca ha hecho un merge de ramas, (3) no tiene experiencia manteniendo codebases grandes, (4) no tiene experiencia en refactors a gran escala, (5) nunca ha descubierto historial de código ni usado herramientas de diff/comparación, (6) no tiene experiencia desarrollando herramientas de automatización para codebases, o (7) tiene un estilo centrado solo en sí mismo y sin conciencia de colaboración.
    • Si la respuesta a todos esos puntos es "no", entonces cree que basta con automatizar el formateo al hacer pass/commit, y que si no pasa por la automatización, CI falle con error. Más que obsesionarse con los detalles, cree que ajustarse a los valores por defecto y renunciar al estilo personal también tiene ventajas. Si se dejan los valores por defecto, el código se siente familiar para cualquiera que lo vea. Además, aunque formateo y lint son cosas distintas, ambas pueden resolverse de una vez con herramientas automatizadas.
    • Bromea diciendo que esa misma evaluación podría aprenderse también con caligrafía a mano, así que a partir de ahora habría que pedir que los PR se envíen en cursiva.
    • Depender del "gusto para elegir buenas reglas de formateo" tiende más bien a provocar discusiones y pérdida de tiempo. Mejor dejarlo en manos de formateadores integrados, como en Go o Rust.
  • Ha habido intentos, desde hace 20 o 25 años, de almacenar el código basándose en árboles sintácticos y tratar lo que ven los humanos como una renderización de eso. Es una corriente que viene desde los años 90, cuando el refactoring empezó a ponerse de moda. IDEs como Visual Age introdujeron modelos donde el código se guardaba en bases de datos en lugar de en el sistema de archivos. El intentional programming y el desarrollo basado en modelos también forman parte de ese ciclo. La esencia del refactoring es transformar el AST; renombrar símbolos se vuelve muy fácil y no hace falta buscar y reemplazar directamente en el código fuente. Pero la gente sigue acostumbrada a editar archivos, y hay resistencia y fricción para popularizar un enfoque que almacene la estructura misma. Que sigamos discutiendo sobre formateo con el paso del tiempo muestra justamente la necesidad de esta alternativa. Es común encontrarse con casos donde ni siquiera se ofrece de forma adecuada un robust symbol rename a nivel de lenguaje o editor.
    • La opinión es que aquí se están mezclando capas de abstracción. El AST también tiene que guardarse al final en archivos, así que no es muy distinto de correr herramientas conscientes del AST sobre archivos legibles por humanos. El formato de almacenamiento no es tan importante; lo central es tener herramientas más inteligentes. Como ejemplos se mencionan casos como Roslyn de Microsoft o la dirección en la que compiladores modernos intentan interactuar con codebases mediante APIs.
  • Da un ejemplo directo de que ciertos formateos no pueden derivarse solo del AST. Por ejemplo, cuando hay varias líneas de assignment, puede variar si tres líneas están alineadas entre sí, alineadas según =, o si se agregan tabs para hacer más visible la profundidad de la estructura mediante indentación. Si se quiere destacar el valor mismo, se podrían alinear los números a la derecha; si se quiere destacar la estructura, se podrían alinear las variables miembro para que sean más fáciles de ver. Dependiendo del aspecto del código que el autor quiera enfatizar, el resultado cambia. Se argumenta que esta información no se puede extraer del AST sin metadata adicional.
    • Entiendo el punto, pero en la práctica solo se usan las dos primeras formas. El objetivo real no era tanto el énfasis como la legibilidad. Hay cosas que se pierden al convertir a AST, pero en un caso como este se gana mucho más. Además, también es totalmente posible dejar variantes de énfasis dentro del AST. Por ejemplo, con setValue([bar, glob], 1) o con una sintaxis de comentarios para overrides de estilo, entre varias otras opciones.
    • El "formateo de código deseable" al final es subjetivo; como en el ejemplo, algunas personas pueden preferir 2/4/8 espacios o alineación por columnas. El AST no contiene información de formateo del código fuente, así que no puede derivarse automáticamente.
    • Los ejemplos segundo y tercero de formateo en realidad son un problema de diseño estructural —violación de la Ley de Demeter—, no una cuestión de formateo.
  • Projectional Editing también puede aplicarse a código fuente textual. Existe incluso un video de ejemplo en JetBrains MPS donde se renderizan tablas dentro del código https://www.youtube.com/watch?v=XolJx4GfMmg&t=63s. Desearía una función en el IDE que renderice diccionarios como tablas. Incluso hoy ya existen algunas funciones parecidas, como code folding, inlay hints o docstrings renderizados como HTML https://x.com/efortis/status/1922427544470438381.
  • Se plantea que tenemos una limitación para aceptar de inmediato algo más abstracto que el plain text. Incluso cuando se intenta, al final se degrada a una proyección en plain text. Se considera que la "enorme lookup table (GLUT)" acumulada desde Morse Code hasta Unicode dio origen a la cultura actual de decodificación de símbolos. Si se eleva el nivel de abstracción, se crea un set de símbolos más adecuado para las aplicaciones, pero no aparecen herramientas correspondientes. Al final todo se transforma otra vez en transmisión de texto para luego parsearlo, como CSV o Markdown. También aparecen editores dedicados para XML, pero la gente igual quiere editarlo como plain text. Aun así, esto no es completamente positivo por problemas de encoding de caracteres o caracteres especiales.
  • A veces surge la duda de por qué el artifact almacenado y la proyección de código que realmente vemos tienen que ser lo mismo. Incluso git diff sería mejor si pudiera revisarse como una proyección de IR (representación intermedia). Gracias a la aparición de herramientas AST como treesitter, uno empieza a imaginar interfaces donde las personas puedan manipular AST o IR de forma más eficiente. Como ejemplo, la estructura de compilación ordenada de f# ayuda a simplificar code reviews. En cambio, en lenguajes o estructuras que permiten orden libre, para revisar un diff pequeño hay que ir y venir por varios lugares para entender el contexto completo del cambio, y eso resulta engorroso.
  • Comparte una incomodidad respecto de eslint-config-airbnb. Los problemas representativos son #1271, #1122. Perdió más de una hora peleando para aplicar la config de Airbnb a un proyecto existente, y sintió que era improductivo por reglas innecesarias aunque el código ya estaba perfecto. Al final desactivó localmente solo esas reglas, y después nunca volvió a usarlo en ningún proyecto. Este ejemplo muestra cuánto pueden arruinar la productividad las reglas de lint equivocadas.