Book review: "Fundamentals of Software Architecture: an Engineering Approach", de Mark Richards y Neal Ford.
Developers are drawn to complexity like moths to a flame — frequently with the same result
Me gustan muchísimo los libros "taxonómicos", tipo "survey"... y creo que no hay suficientes en ingeniería del software (un poco casi con la excusa de que la tecnología avanza a una velocidad endiablada). La sensación cuando lo lees (casi directamente cuando lo adquieres) es como que te regalen una caja de herramientas, que todo esté en su sitio y que tú sepas dónde está cada cosa. La realidad luego siempre es más compleja, pero al menos tienes un punto de partida, un mapa de navegación.
En software, ¿qué es arquitectura? ¿Y tú me lo preguntas?
La definición precisamente no es muy... precisa
Architecture is about the important stuff… whatever that is - Ralph Johnson.
Vendría a estar compuesta de estos cuatro grandes conjuntos:
La estructura es simplemente los componentes del sistema y cómo se relacionan.
Algunas de las características son:
Availability
Scalability
Elasticity: no confundir con escalabilidad. La elasticidad es la capacidad de aguantar picos de utilización. La escalabilidad es aguantar un mayor uso sostenido en el tiempo.
Fault tolerance
Performance
Security
Deployability
Testability
Agility
Recoverability: en caso de desastre, ¿cuánto perdemos? ¿cómo de rápido puede el sistema estar on-line de nuevo?
Learnability: facilidad con la que los usuarios entenderán como funciona el sistema.
Las architecture decisions son reglas duras de como debería construirse y evolucionar el sistema (e.g., en un sistema dividido en capas, que la capa de presentación no pueda comunicarse directamente con la base de datos).
Mientras que los design principles son reglas blandas, filosofías acerca de cómo debería construirse el sistema (e.g., usemos un sistema reactivo para hacer el frontend).
La arquitectura es, en esencia, más una guía, que una especificación detallada:
Guide is the key operative word in this first expectation. An architect should guide rather than specify technology choices. For example, an architect might make a decision to use React.js for frontend development. In this case, the architect is making a technical decision rather than an architectural decision or design principle that will help the development team make choices. An architect should instead instruct development teams to use a reactive-based framework for frontend web development, hence guiding the development team in making the choice between Angular, Elm, React.js, Vue, or any of the other reactive-based web frameworks.
De hecho, que la arquitectura sirva de manera natural como guía, es la prueba de que se ha desarrollado una buena arquitectura:
The key to making effective architectural decisions is asking whether the architecture decision is helping to guide teams in making the right technical choice or whether the architecture decision makes the technical choice for them.
Lo que no es arquitectura: conflictos de responsabilidad
Los autores hacen mucho hincapié en delimitar las responsabilidades de un arquitecto. Este tipo de delimitaciones me dan bastante pereza y me resultan ¿paternalistas?. ¿Qué más da de quién sea la responsabilidad (mientras sea de alguien, claro, porque la responsabilidad distribuida... es la no-responsabilidad)? La clave es que son decisiones diferentes que alguien debe tomar.
Una de las distinciones que primero marcan es la de arquitectura software frente al diseño de software que haría un developer:
El arquitecto piensa en bloques macro (componentes), el developer debería hacerlo en bloques micro (diagramas de clase). Es decir:
Por otro lado, entiendo y sí que me gusta que las decisiones arquitecturales vayan a ser cuestionadas por prácticamente toda la organización en la que trabajamos. La arquitectura de un software son sus cimientos, sus vigas... es crítico para su éxito. Que te cuestionen (dentro de un proceso bien diseñado) es bueno: siempre va a mejorar el resultado.
The main point is that almost every decision an architect makes will be challenged. Architectural decisions will be challenged by product owners, project managers, and business stakeholders due to increased costs or increased effort (time) involved. Architectural decisions will also be challenged by developers who feel their approach is better.
Precisamente gestionar las expectativas de los developers en cuanto a decisiones es uno de los que más me cuesta gestionar como manager. Aquí los autores recomiendan:
Always remember that demonstration defeats discussion (...) By running a comparison between the two options in a production-like environment and showing the other architect the results, the argument would likely be avoided.
If a developer disagrees with a decision, have them arrive at the solution on their own.
Estos consejos, sin embargo, en la práctica creo que tienen una serie de problemas:
A menudo, ese "deja que el developer llegue a la solución por si mismo" tienen un coste en tiempo nada despreciable. El criterio para la elección en estos casos no es data-centric, sino basado en teoría o experiencia previa.
He visto muchos developers que, dados el tiempo para defender su idea, construían una defensa sin una base ni teórica ni práctica sólida: basado en cherry-picking de artículos de dudosa calidad, buzz-words... (MongoDB is web scale). Es decir, estos consejos de los autores presuponen que el developer es capaz de hacer una buena exposición de sus argumentos... que es una cosa que el average developer en España no está nada acostumbrado a hacer.
Y por último (lo que explica el anterior punto en muchas ocasiones), muchas veces hay una cuestión de preferencia personal por parte del developer, de tecnologías que quiere aplicar (porque le apetecen, porque van bien en el CV...). Es muy complicado luchar contra ese sesgo.
Esta historia que cuentan... suena a fantasía.
Mark told the Scala enthusiast that he would support the use of Scala within the project, but the Scala enthusiast would have to provide a business justification for the use of Scala because of the training costs and rewriting effort involved. The Scala enthusiast was ecstatic and said he would get right on it, and he left the meeting yelling, “Thank you — you’re the best!” The next day the Scala enthusiast came into the office completely transformed. He immediately approached Mark and asked to speak with him. They both went into the conference room, and the Scala enthusiast immediately (and humbly) said, “Thank you.” The Scala enthusiast explained to Mark that he could come up with all the technical reasons in the world to use Scala, but none of those technical advantages had any sort of business value in terms of the architecture characteristics needed (“-ilities”): cost, budget, and timeline. In fact, the Scala enthusiast realized that the increase in cost, budget, and timeline would provide no benefit whatsoever. Realizing what a disruption he was, the Scala enthusiast quickly transformed himself into one of the best and most helpful members on the team.
La buena arquitecta
How do we get great designers? Great designers design, of course - Fred Brooks.
So how are we supposed to get great architects, if they only get the chance to architect fewer than a half-dozen times in their career? - Ted Neward.
Completando "architecture katas", ejercicios en los que nos dan una especificación de un sistema y pensamos en qué arquitectura viene bien, ¡pero no hay soluciones!
No matter what they tell you, it's always a people problem
En torno al 50% del libro está dedicado a lo que tradicionalmente llamamos #[[side skills]]. Aquí, como en tantísimos otros lugares, se menciona el “no matter what they tell you, it’s always a people problem" de [[Gerald Weinberg]] (que, by the way tiene un libro estupendo, el primero que me leí cuando empecé a llevar equipos: [[Becoming a Technical Leader]] y que es ni más ni menos que del 86, pero está tan fresco como si fuera hoy).
The industry is flooded with software architects, all competing for a limited number of architecture positions. Having strong leadership and interpersonal skills is a good way for an architect to differentiate themselves from other architects and stand out from the crowd.
En esencia, esto es lo mejor que puedes aportar e inculcar a tu equipo (¿no es lo que deberíamos hacer todos, independientemente del lugar que ocupas en un grupo?). Es decir, convertirse en una aspiradora del caos:
La buena arquitecta ha de ser un poco paranoica de la información
Con un ojo siempre puesto en tus arquitecturas (#observability):
In our experience, not enough architects focus their energies on continually analyzing existing architectures.
Software systems model complex systems, which tend toward entropy (or disorder). Energy must be added to a physical system to preserve order. The same is true for software systems: architects must constantly expend energy to ensure good structural soundness, which won’t happen by accident.
Pero también pendiente de todo lo demás, lo cual calma mi culpabilidad por generalista (#[[Depth vs breadth knowledge]]):
An architect is expected to have exposure to multiple and diverse technologies, frameworks, platforms, and environments.
The most successful architects we know are those who have broad, hands-on technical knowledge coupled with a strong knowledge of a particular domain.
Aparece además también esta idea de que trates lo que quieres aprender como un portfolio, con apuestas muy seguras combinado con otras muy arriesgadas:
Architects should treat their technology portfolio like a financial portfolio: in many ways, they are the same thing. What does a financial planner tell people about their portfolio? Diversify!
Esta misma idea aparece en muchos otros lugares, como en [[The Pragmatic Programmer]] o en [[Antifragile]].
Por otro lado, una buena arquitecta está abierta a cuestionarlo todo:
Architects have an important responsibility to question assumptions and axioms left over from previous eras. Many of the books about software architecture were written in an era that only barely resembles the current world.
Esto, por cierto, casi parece ir en contra un poco del [[Lindy effect]] que cuenta [[Nassim Nicholas Taleb]] en [[Antifragile]] cuando habla de confiar en lo viejo porque ha sobrevivido el test del tiempo y por tanto, ha sobrevivido a más intentos de ser refutado/sustituido.
Y, maravilla, la buena arquitecta usa Twitter:
McAfee’s most interesting observation about these connections was that someone’s next job is more likely to come from a weak link than a strong one. Strongly linked people know everything within the strongly linked group — these are people who see each other all the time. Weak links, on the other hand, offer advice from outside someone’s normal experience, including new job offers.
Mención especial, "The bottleneck trap"
The bottleneck trap occurs when the architect has taken ownership of code within the critical path of a project (usually the underlying framework code) and becomes a bottleneck to the team. This happens because the architect is not a full-time developer and therefore must balance between playing the developer role (writing and testing source code) and the architect role (drawing diagrams, attending meetings, and well, attending more meetings).
El consejo de ellos para esto es típico: como arquitecto, no programes en el critical path (pero programa: necesitas seguir conociendo el sistema, lógicamente). Con esto dicen que:
1. Delegas eficientemente.
2. Das ownership.
If architects never allow other roles to make decisions of consequence, the organization will struggle with empowering the next generation of architects.
3. Programas lógica similar a la de tu equipo y sufres los mismos pains.
Sin embargo... siempre lo he vivido como algo MUY difícil de hacer (sin ser el cojo-ingeniero, que es lo que más me sorprende). ¿Puede tu equipo encargarse de los critical paths? Si no están muy definidos... puede que no. Si los tienes que definir tú... eso ya es prácticamente programarlos. ¿Será precisamente porque fallo en el papel de guía? Mi intento de salir de esta ratonera suele ser pedir que se hagan análisis en equipo de aquellos casos complejos, para poder favorecer una iteración rápida sobre el espacio de soluciones sin necesidad de llegar a escribir una línea de código. Pero me cuesta horrores que la gente profundice en ello.
Relaciones con los de arriba
Es muy importante tener claro cuáles son los objetivos de los stakeholders (esto es, ayudarles a tenerlos claros a ellos):
Leverage the “divide and conquer” rule to qualify demands or requirements.
O lo que es lo mismo, que te asegures para qué area del proyecto es ese requerimiento (lo más probable es que no sea un requerimiento global de la arquitectura).
When all else fails, state things in terms of cost and time.
Estimar es timar:
...one of the Achilles heels of software development is estimation — how much time, how many resources, how much money? Part of this difficulty lies with antiquated accounting practices that cannot accommodate the exploratory nature of software development, but another part is because we’re traditionally bad at estimation, at least in part because of unknown unknowns. All architectures become iterative because of unknown unknowns, Agile just recognizes this and does it sooner.
Y por favor, centrar el tiro:
A common anti-pattern in architecture entails trying to design a generic architecture, one that supports all the architecture characteristics.
... y con tus fellow developers
Developers are drawn to complexity like moths to a flame — frequently with the same result.
Me gusta mucho el concepto que se menciona aquí de "Elastic Leadership" de Ray Osherove y el reconocimiento, por una vez, que el trabajo de un manager no es simplemente quitarse de enmedio y facilitar...: si el equipo es más junior, necesita más control.
Checklists y usar el #[[Hawthorne Effect]] pero en este caso para algo positivo, no como lo que se describe en: I never liked technical interviews. Pero a ver, ¡que hay hasta un libro sobre las checklist! The Checklist Manifesto
La buena arquitectura
Everything in software architecture is a trade-off. First Law of Software Architecture. If an architect thinks they have discovered something that isn’t a trade-off, more likely they just haven’t identified the trade-off yet.
Esto es, no hay #[[free lunch]] tampoco en arquitectura de software.
En cuanto a lo puramente técnico, ala hora de pensar en los componentes de alto nivel, no hay un método adecuado, pero nos advierten los autores de la entity trap: dividir los componentes de la arquitectura simplemente de acuerdo con las entidades de la BBDD. Esto solo tiene sentido en apps CRUD, y para ello, ni siquiera hay que pensar en arquitectura: un monolíto en Django (que facilita esa identificación) es suficiente. Pero si la app es más compleja, entonces hay que pensar en otras formas de separar.
Lo interesante entonces es considerar los workflows de la app y componentizar la app en torno a los actores o acciones/eventos que ocurren ("actors/actions", "event storming", "workflow approach"), identificando qué partes van a necesitar distintas características.
Estilos de arquitectura
Es importante entender que varias arquitecturas pueden convivir en un sistema simultáneamente.
Layered: la típica. E.g., Django.
El monolito, la más sencilla de arrancar pero que a medida que crece se va complicando.
Pipeline: la de los ETL.
Extrañamente, el libro considera esta arquitectura como de despliegue monolítico y no termino de entender por qué, puesto que cada componente podría desplegarse por separado (quizá es porque no tiene sentido que existiera ninguno de esos componentes sin el resto… pero aún así, tampoco lo veo).
Microkernel: la de los plugins. E.g., Odoo.
De nuevo aquí dice que es monolítica, una single unit of deployment por culpa del "Core System"... pero no estoy tan seguro de eso.
Service-based: la de los no-micro servicios.
La preferida de los autores: muy versátil, con casi lo mejor de lo monolítico (sencillez, transacciones, testeabilidad...), pero también ventajas de arquitectura distribuida (servicios que se despliegan independientemente).
Event-driven: la de los eventos.
Broker-based:
Mediator-based:
The choice between the broker and mediator topology essentially comes down to a trade-off between workflow control and error handling [la del mediador] capability versus high performance and scalability [la del broker].
Este tipo de arquitectura tiene sentido cuando tienes muchas acciones y/o complejas y necesitas además responder rápido (responsiveness) y/o un rendimiento alto (performance). A cambio, la gestión de errores se complica bastante (aunque hay estrategias para mitigar eso, workflow processors que intentan, por ejemplo, reparar el mensaje y hacer resubmit).
Space-based (solo he encontrado Gigaspaces como ejemplo para Java...).
Para evitar tener que acabar escalando la DB y poder gestionar grandes concurrencias (e.g., venta de tickets), esta arquitectura mantiene unidades de procesamiento con réplicas de la DB in-memory. La desventaja claramente es la no-simplicidad y testeo por culpa de las cachés.
Se cita como ventaja de la arquitectura que las processing units las podrías tener en el cloud, pero la DB on-premises, algo que no puedes hacer con el resto de arquitecturas.
SOA
Citada como "la arquitectura con mayor particionamiento técnico que se ha intentado" y como que persigue un ideal (maximizar la reutilización de funcionalidad de negocio en una compañía), con resultados desastrosos (acoplamiento brutal).
Microservicios
Casi podría ser lo opuesto a la anterior: el objetivo aquí es que los microservicios tengan cero acoplamiento entre ellos, siguiendo el concepto de bounded-context de DDD, aunque sea a costa de duplicar funcionalidad. Con todo, funcionalidad común, como por ejemplo el logging, se puede abstraer en "sidecars":
Curiosamente, el termino microservicios lo hemos entendido mal, y especificar la granularidad correcta es vital para que esta arquitectura funcione (por ejemplo, diseñando a una granularidad que no necesite transaccionalidad a través de múltiples servicios).
The term “microservice” is a label, not a description - Martin Fowler.
In other words, the originators of the term needed to call this new style something, and they chose “microservices” to contrast it with the dominant architecture style at the time, service-oriented architecture, which could have been called “gigantic services”. However, many developers take the term “microservices” as a commandment, not a description, and create services that are too fine-grained.
En cualquier caso, en el peor de los casos podemos emular transacciones con sagas, con un servicio que actúa de mediador, llamando a los servicios y llevando el estado de cada request, haciendo rollback si fuera preciso.
A la hora de crear workflows en microservicios, las opciones son las mismas que nos encontramos en la arquitectura de eventos (broker o mediador).
Bien en elasticidad, escalabilidad, desplegabilidad... pero mal en performance (latencias, chequeo de seguridad...).
Criterio de decisión
Aparte de todos los que te cabe esperar (arquitectura de datos, factores organizacionales, etc.), quizá el más importante es el de domain/architecture isomorphism: buscar aquella arquitectura cuya topología se parece más al problema que buscas resolver (por ejemplo, microkernel si buscas un sistema muy customizable, porque las customizaciones pueden ser plugins).
Como siempre, simplicidad primero: utiliza comunicaciones síncronas al principio, y cambia a asíncronas sí y solo sí las necesitas.
Ojo al día de la marmota: si no justificas bien una decisión, te puedes pasar discutiéndola miles de veces. Lo ideal, además es reflejar esa decisión en una wiki, con un Architecture Decision Record (ADR), que tiene los siguientes puntos:
Título, status, contexto, decisión que se toma, consecuencias, cómo te vas a asegurar de que se cumpla, y metadatos.
Asegurarte de que se cumpla es una movida en lenguajes que no tienen grandes herramientas de análisis estático como Python. Lo suyo es no depender de la memoria, sino que esté automatizado.
Por último, el procedimiento colaborativo para estudiar y controlar riesgos de arquitectura que proponen tiene muy buena pinta. Se basa en puntuar, para aquellas características de la arquitectura que interese analizar, un puntaje a distintas zonas de la misma, y medidas de mitigación del riesgo para aquellas de puntaje más alto.
Valoración
Muy buen libro para tener la vista de pájaro de lo que supone la arquitectura de software. Me ha gustado mucho que se haga tanto énfasis en la parte "humana" de la misma, sin dejar de lado la parte más puramente técnica. Quizá en algunos aspectos se queda demasiado superficial, pero es lógico en libros de este tipo para mantener la concisión. También he echado de menos un "where to go from here", más curación de contenido si quieres profundizar. Pero en general, un muy buen libro para encajar las piezas del puzzle que componen la arquitectura de software.