Aprender arquitectura de software
(matklad.github.io)- El diseño de software se aprende más a fondo en proyectos reales, cuando asumes responsabilidad y el problema se vuelve asunto tuyo, que en clases teóricas
- Conway’s Law plantea que el software refleja la estructura social de la organización, y la diferencia entre código científico y código industrial también puede surgir de los incentivos
- rust-analyzer facilita que los colaboradores de alta eficiencia se concentren gracias a compilaciones rápidas, soporte para stable, eliminación de dependencias en C y pruebas que duran apenas unos segundos
- rust-analyzer protegió funciones independientes con
catch_unwindy bajó el umbral para los PR, pero aplicó estándares de calidad mucho más estrictos al spine central - Una estructura experimental puede convertirse en una realidad de largo plazo, y rust-analyzer también pasó de ser un prototipo de arquitectura LSP a mantener otro compilador más
El diseño de software se aprende mejor en la práctica
- El diseño de software se aprende mejor al asumir responsabilidad en proyectos reales y resolver los problemas directamente que en cursos formales
- Más que cuando tomó el rol de “arquitecto” en clases universitarias de diseño y proyectos de curso, el aprendizaje despegó de verdad en IntelliJ Rust, su segundo proyecto real, cuando los problemas de diseño se volvieron su responsabilidad
- En IntelliJ Rust hubo algunos errores, pero no fueron fatales, y en ese proceso pudo aprender mucho
- La ingeniería de software también tiene un lado lo bastante simple como para que una persona curiosa pueda aprenderla pensando desde los principios y leyendo distintos textos
Estructura de incentivos y Conway’s Law
- Conway’s Law es la idea de que el software refleja la estructura social de la organización que lo crea
- La diferencia entre el software industrial y el código científico puede surgir no tanto del conocimiento para construir software en sí, sino de la estructura de incentivos que lleva a las personas a hacerlo
- Situaciones como la de “un PhD que tiene que publicar un paper en 3 meses” pueden ser un factor importante que moldea la forma del código científico
- Hay, en términos generales, dos maneras de responder a una estructura de incentivos
-
Diseñar o mover los incentivos del proyecto
- Rara vez se tiene la oportunidad de diseñar o ajustar la estructura de incentivos de un proyecto, pero cuando aparece, su impacto es grande
- Lo central de TIGER_STYLE no es la lista de reglas en sí, sino el contexto social que hace que esas reglas sean buenas decisiones
-
Adaptarse a las restricciones si no se pueden cambiar
- Casi nunca se recibe la estructura de incentivos exactamente como uno quisiera, y si no se puede cambiar, hay que adaptarse a ella
- Incluso en proyectos de software industrial casi nunca hay “tiempo para hacerlo bien”, y hay que hacer lo mejor posible dentro de las restricciones dadas
Cómo rust-analyzer alineó estructura y participantes
- rust-analyzer es un proyecto con profundidad y amplitud a la vez
- En su lado profundo, por su naturaleza de compilador, puede atraer colaboradores sobresalientes y muy comprometidos
- En su lado amplio, como un IDE clásico tiene muchas funciones especializadas por propósito, así que resulta adecuado para quienes aprenden Rust o para colaboradores ocasionales de fin de semana que quieren dedicar una o dos horas a resolver una molestia propia
- La razón por la que rust-analyzer insistió en no requerir una compilación de
rustc, poder compilar en stable, no tener dependencias en C y hacer que toda la suite de pruebas termine en segundos fue atraer colaboradores de alta eficiencia - Se buscó pulir el sistema de compilación para que la gente pudiera concentrarse en trabajar con el borrow checker sin distraerse con otras cosas
- Para atraer colaboradores de fin de semana, el interior de rust-analyzer se dividió en varias funciones independientes, y cada una quedó protegida en tiempo de ejecución con
catch_unwind - El criterio para aceptar PR de funciones se bajó a “el happy path funciona y hay pruebas”, y se consideró aceptable incluso si ese código llegaba a fallar
- Aun así, hacían falta dos condiciones
- Los problemas de calidad debían quedar aislados dentro de cada función individual y no propagarse al resto
- Los fallos en tiempo de ejecución no debían ser visibles para el usuario; para eso, las funciones de rust-analyzer operan sobre snapshots inmutables y no pueden contaminar los datos
- En cambio, al spine central que sostiene las funciones se le aplicaron estándares de calidad mucho más estrictos
El riesgo de que una estructura experimental se vuelva la realidad a largo plazo
- Cuando en vez de corregir la estructura de incentivos uno se adapta a ella, hay que tener cuidado con que el futuro es incierto y, por lo general, puede materializarse de la forma más incómoda posible
- La motivación original de rust-analyzer era evitar escribir otro compilador paralelo dentro de IntelliJ Rust, validar como prototipo una mejor arquitectura para LSP y devolver ese aprendizaje a
rustc - Por eso, incluso el núcleo del código era muy experimental
- Como resultado, terminaron manteniendo otro compilador más
- De forma parecida, el proyecto uutils comenzó como un destino principal para quienes querían aprender Rust y terminó convirtiéndose en la implementación de coreutils en Ubuntu
Materiales y libros recomendables
- No existe un solo libro con la respuesta correcta, y la práctica real parece ser un elemento indispensable
- Boundaries by Gary Bernhardt
- Tiene consejos concretos muy sólidos, y fue un material que impulsó una exploración de nivel más alto
- How to Test
- La importancia de las pruebas se entendió de inmediato, pero tomó mucho tiempo reconocer que muchos consejos sobre testing muy citados no eran prácticos y llegar a conceptualizar una forma que sí funcionara en la práctica
- ∅MQ guide y textos de Pieter Hintjens
- Fueron materiales que lo llevaron a conocer una forma de pensar al estilo de Conway’s Law
- La arquitectura de desarrollo de funciones de rust-analyzer es una aplicación de optimistic merging
- Reflections on a decade of coding by Jamii
- Trata temas muy meta, y es tan bueno que lo colocó como el primer elemento de su colección de enlaces
- Ted Kaminski blog
- Con el formato de notas para un libro que no existe, es lo más cercano a una teoría coherente sobre el desarrollo de software
- Software Engineering at Google y The Philosophy of Software Design de Ousterhout
- Son buenos libros que se recomiendan con frecuencia, y en especial Software Engineering at Google ayudó a organizar nombres importantes sobre unit test e integration test
- Aun así, personalmente no le parecieron libros revolucionarios
1 comentarios
Comentarios de Hacker News
Si se resume en una hoja de referencia, un buen diseño debería ir en la dirección de que una sola idea impregne todo el sistema y minimice las sorpresas
Si el sistema lo permite, la gente al final lo usará así, y una solución que empieza con “si todos simplemente hicieran X” no es una solución
Hay que separar la parte que transforma los datos de la parte que los usa, los modelos de datos duran más que el código, y el acoplamiento es la raíz de muchos problemas
El versionado es inevitable, el estado debe hacerse explícito y cada información debe tener una única fuente de verdad
Hay que dedicar más tiempo a poner nombres, si es difícil probar algo entonces el diseño está mal, y las decisiones no documentadas terminan causando arrepentimiento
La comunicación tiene un costo, así que debe justificarse antes de pagarla, y el trabajo del ingeniero es resolver problemas con reglas prácticas en medio de información incompleta
El texto en sí tampoco era muy consistente desde la perspectiva de arquitectura de software
La vista arquitectónica 4+1, quitando UML, es un buen marco conceptual para pensar en el panorama general, y la serie Pattern-Oriented Software Architecture también organiza bien las distintas arquitecturas a las que llega la gente
Grady Booch también estuvo haciendo un manual de arquitectura de software, pero parece que ahora está casi detenido, y en aquella lista de correo solían documentar grandes arquitecturas de sistemas de empresas o de proyectos grandes de código abierto
Si uno profundiza en ese tipo de materiales, puede ver que la arquitectura se construye según distintos enfoques como escala, seguridad, rendimiento, interoperabilidad o fail-safe, y que cada objetivo implica trade-offs realistas
Si algo es mejor según ese criterio, entonces aunque parezca un mal diseño en realidad es un buen diseño
Las interfaces deben hacer que usarlas correctamente sea fácil y usarlas mal sea difícil, y hay que pensar cómo las usaría alguien que no conoce el proyecto
Escribir código correcto debería ser fácil, el código sospechoso debería destacar y los bugs deberían moverse lo más posible hacia la izquierda
Es mejor eliminar una clase de bugs que arreglar un solo bug, y como las interfaces son más difíciles de cambiar que la implementación, si la interfaz es correcta una implementación fea está bien
Los comentarios y la documentación deben explicar por qué el código tiene esa forma, y aunque parezca haber una forma más simple, si no sirve por alguna restricción eso debe quedar registrado
Desde la perspectiva de los datos, no hay que repetir; si el mismo hecho se almacena en varios lugares, tarde o temprano se desalineará y aparecerán bugs
Salirse del camino ya pavimentado tiene un costo; puede valer la pena cuando realmente importa, pero no hay que subestimar ese costo
Muchas veces una tecnología aburrida que parece peor termina siendo mejor, y en vez de preguntar “¿vale la pena?” hay que evaluar el valor esperado como “¿vale la pena comparado con hacer otra cosa?”
Aunque uno crea ser más inteligente que los demás, hay problemas para los que la inteligencia sola no alcanza, y algunos problemas no se pueden descubrir hasta que explotan de verdad, así que hay que aprender de los errores ajenos
La fricción es un asesino silencioso
Planear está bien, pero a veces hay que probar directamente, y todo cuesta dinero
Si diseñas sin considerar el costo, después te verás forzado a tomar decisiones difíciles
Incluso en un proyecto hecho por una sola persona, las restricciones del motor sirven como guía para “agregar esta extraña función nueva que apareció durante las pruebas de esta manera”
Ya no hace falta cargar siempre con una documentación enorme del tipo “si quieres crear un nuevo efecto de sonido que se ejecute varias veces en una transición de estado específica, hazlo así”
El diseñador del sistema debe entender la industria; no necesita adoptar por completo su terminología ni sus hábitos de modelado, pero sí debe comprender las razones y la perspectiva con la que miran los datasets
También hemos simplificado deliberadamente la complejidad del mercado de salud para eliminar sobredefiniciones innecesarias y ofrecer un modelo más integrado, pero esos cambios solo pudimos hacerlos con confianza porque entendíamos bien el dominio del problema
En particular, los nombres casi nunca mueren
A veces sí, pero cambiar un nombre requiere un esfuerzo extremo, así que realmente vale la pena invertir mucho tiempo en que los expertos del dominio revisen las propuestas de nombres para verificar que no falte nada
Algunas ideas se pueden imponer, pero las áreas de negocio como ventas y marketing siguen exigiendo la terminología de la industria y presionan para que el modelo refleje la visión actual del sector
Si decidiste romper con esa corriente, entonces esa ruptura debe tener una intención y un propósito muy claros
La propiedad que más debe enfatizarse en el software es la mantenibilidad
Cuánto cuesta construirlo es una pregunta, pero el costo de operarlo tiene un impacto mucho mayor, porque no solo incluye infraestructura, sino también solicitudes de nuevas funciones acumuladas, refactorización de código y mantenimiento de versiones de software de terceros
Las listas de recomendación suelen ser buenas, como A Philosophy of Software Design de Ousterhout, pero en general están más cerca de una teoría general del desarrollo de software que de la arquitectura de software en sí
Si quieres ver arquitectura, recomiendo clásicos como Software Architecture: Perspectives on an Emerging Discipline de Shaw/Garlan y los textos de Mary Shaw
También son buenos artículos recientes como Myths and Mythconceptions: What Does It Mean to Be a Programming Language, Anyhow? o Revisiting Abstractions for Software Architecture and Tools to Support Them, que exploran por qué el campo de la arquitectura de software no evolucionó como se esperaba
En lo práctico, conviene ver por qué los pipes y filtros de Unix y REST tuvieron éxito, y dónde y por qué se rompen; la arquitectura hexagonal también es clave
En lo personal, también está Beyond Procedure Calls as Component Glue: Connectors Deserve Metaclass Status, que intenta conectar arquitectura de software y protocolos metaobjeto para verlos como una nueva base de los lenguajes de programación y de la programación misma
Es una respuesta al texto de Mary Shaw Procedure Calls Are the Assembly Language of Software Interconnection: Connectors Deserve First-Class Status, y plantea la pregunta de cómo sería un lenguaje de alto nivel si las llamadas a procedimiento fueran ensamblador
Quizá la arquitectura de software todavía tenga un futuro más brillante y práctico
Si tienes unas cuantas estructuras de datos y tipos, y un pequeño conjunto de funciones básicas, basta con combinarlas
Una cosa que me gusta de Lisp es que los tipos más complejos, especialmente los que vienen por FFI, siempre son opacos
Me gustaría ver una implementación de CLOS en un lenguaje parecido a C donde al definir un struct obtengas un conjunto estándar de funciones
La mejor manera de aprender arquitectura no es hacer proyectos suficientemente grandes, sino darles mantenimiento
Y eso debería hacerse en al menos dos o tres proyectos
Si el proyecto es demasiado pequeño, cualquier arquitectura funciona bien, y es mejor juzgar si un proyecto es “grande” no por líneas de código, sino por la cantidad de personas que han trabajado en él, o mejor aún, por la cantidad de equipos
Debe haber al menos dos proyectos distintos para poder comparar, y he visto gente que pasó décadas atrapada en un solo proyecto y desconoce formas modernas de resolver problemas
Pero en la práctica, muchas veces quien construyó el proyecto es quien asciende a arquitecto, y rara vez quien lo mantuvo
En Google se nota aún más: para ascender hay que lanzar cosas nuevas, no se asciende por mantenimiento, y conviene salirte apenas después del lanzamiento si es posible
Paradójicamente, la mejor posición para convertirse en arquitecto podría ser la de un contratista externo al que dentro de la empresa le asignan el mantenimiento de proyectos existentes que nadie quiere tocar
Esa gente tiene que preservar la arquitectura y además pasa por varios proyectos, así que puede comparar
Claro, si cobra por hora también existe el riesgo de que complique la arquitectura de más para poder facturar más tiempo
En este contexto, de verdad recomiendo Architecture of Open Source Applications
Es una serie de libros en la que cada capítulo está escrito por un mantenedor del proyecto correspondiente, así que se puede aprender arquitectura a partir de ejemplos
No solo se entiende qué es la arquitectura, sino también las restricciones que la formaron, normalmente la historia y la visión cambiante del proyecto
Por las limitaciones de un libro con muchos autores, no todos los capítulos son igual de buenos o interesantes, y todos son antiguos, pero aun así valen la pena
http://aosabook.org/
Me gustaría dedicar más tiempo a obtener un mejor modelo mental del proyecto en el que trabajo, pero cuando empiezo a fastidiarme por el lenguaje de programación o por elecciones arquitectónicas específicas, o por partes que se volvieron tan complejas que ya no parecen valer el tiempo invertido, la motivación me cae muchísimo
Depende del proyecto, pero trabajar como “full-stack developer” se siente como una forma de quitarle la diversión a programar
Ya paso 40 horas a la semana viendo el proyecto más aburrido que uno pueda imaginar
No existe un solo proyecto impecable
Y si el lenguaje de programación es un problema tan grande, mejor cambiarse de barco
Creo que todo el mundo debería poder manejar varios lenguajes, pero al final la elección es tuya
Hay que asegurarse de que las decisiones a las que dedicas más tiempo sean realmente las más importantes
Una estructura de datos bien diseñada tiene un impacto mucho mayor en el rendimiento y la mantenibilidad que el framework, el lenguaje o la plataforma
En lo personal, trabajo todos los días con ADHD, así que tengo que seguir empujando para que haya avance; eso significa elegir decisiones poco importantes, cerrarlas y así concentrarme en el espacio restante de problemas que sí requiere reflexión cuidadosa
Frases como “código limpio” o “código hermoso” no ayudan mucho a que un junior aprenda buenas prácticas de arquitectura de software
Si un junior pregunta por qué se usa un ORM y un senior responde “porque es más limpio”, lo único que queda es confusión
Es mejor definir una lista clara de objetivos
Criterios como mantenibilidad, rendimiento y escalabilidad, eficiencia, resiliencia, observabilidad, capacidad de prueba y que esté probado, seguridad, y facilidad de lectura para un desarrollador nuevo se equilibran entre sí
Cuantos más criterios sumes, más fácil será tomar mejores decisiones cuando dudes, y además tienen sentido para gente fuera del equipo de desarrollo, así que se puede acordar con el cliente por qué está pagando
Un proyecto pasa la mayor parte de su vida en modo mantenimiento, y eso es una buena señal de que tuvo éxito, así que también se puede definir la mantenibilidad
Un buen punto de partida es la capacidad de agregar nuevas funciones sin romper la arquitectura, e incluso mejor, sin romper ni una sola firma de método
Hay que tener muchísimo cuidado con la abstracción
Es verdad eso de que “la abstracción muchas veces oculta lo simple que es lo que quieres”, y el ORM es un ejemplo perfecto
En la mayoría de los casos, los datos deben tratarse como ciudadanos de primera clase, y eso también mejora la colaboración con el DBA
También es un reto pensar fuera del camino feliz sin caer en optimización prematura, y eso evita lanzarse a implementar una buena idea sin haber visto sus pros y contras
Si imaginas que la persona que va a tocar tu código está teniendo un día terrible, eso ayuda a hacerlo agradable de leer
Ahí entran los comentarios por todas partes, variables locales que dejas aunque podrían omitirse y nombres de variables
Los frameworks deben elegirse con cuidado: son buenos sirvientes, pero malos amos
La frase del texto “no seas un frameworker, sé un ingeniero” da en el clavo
No me molestan las opiniones fuertes en sí, y en una librería o una herramienta incluso pueden ser una gran cualidad
Es muy probable que esa librería o herramienta tenga mucha experiencia de dominio en su área
Pero en un framework, una opinión fuerte puede deformarse fácilmente en “si tienes un martillo, todo te parece un clavo”
No pasa siempre ni siempre es un problema, pero cuando la ortodoxia de un dominio se aplica de forma amplia, existe el riesgo de que su justificación haya sido específica del dominio y se rompa en un contexto más amplio
Por eso prefiero frameworks con modularidad, donde puedas conectar otro ORM u otra capa de integración de persistencia, y reemplazar el router, el validador u otros componentes que no encajen con el problema
Esto es especialmente valioso cuando dentro del ecosistema del framework hay alternativas de varios paradigmas
Porque así puedes encontrar una herramienta ya hecha que casi encaje, con desventajas claras y que pueda complementarse cuando haga falta
Para la mantenibilidad es muy importante luchar contra el síndrome NIH
Es un riesgo constante que absorbe recursos a una velocidad sorprendente
Al mismo tiempo, algunos componentes sí pueden dar grandes beneficios cuando los ajustas tú mismo o los controlas por completo, y lo difícil es decidir qué cae de qué lado
Coincido totalmente con poner la mantenibilidad al principio de la lista
Desde una perspectiva de ingeniería de sistemas, la arquitectura de software se parece al diseño de tuberías
Es muy importante, pero la gente no vive dentro de las tuberías, sino en la casa que las tiene
Si las tuberías no consideran el resto de la casa, arreglarlas puede salir carísimo
Buen texto
Aprender arquitectura de software es entender que no existe una sola respuesta correcta
Esto es arte y ciencia a la vez
Como lectura, recomiendo Simplify IT - The art and science towards simpler IT solution
https://nocomplexity.com/documents/reports/SimplifyIT.pdf
La charla de Gary Bernhardt es realmente especial
Tiene muchísimas ideas que pueden llevarte a otros lugares interesantes