Good architecture and design
In this article, I refer to tools, meaning any combination of: operating systems, programming languages, executables (database systems, devops tools, etc), frameworks, libraries, clouds or cloud providers.
The ideas outlined below try to match the generic needs and structure of a mid-sized company and should be tailored to your organization.
Architecture is the structure in software projects that changes less often. An architecture has principles which are followed, and it uses some tools to accomplish some tasks. However, architecture is about the structure, the components, and how they communicate, it's not about the tools – which are mere details.
Design on the other hand is about the more fine-grained details: it's about the programming paradigms, the principles of this paradigm, the design patterns and the communication between small components. In OOP, these components are classes/objects for example. Design is also not about the tools.
Architecture and design go hand in hand. They're not like two sides of the same coin. Rather, they are like two blocks facing each other.
In a good architecture, these two blocks permeate into each other, becoming one.
In a bad architecture, they fight against each other.
Imagine these two blocks sitting on top of each other. The top one is the architecture, the bottom one is the design.
The architecture is in a position of power, and the design is in a position of defense. Let's look closer at the interaction between these two.
The interaction takes place on two channels:
- the technical channel
- the people channel
Keep in mind, the interactions between these two blocks through these channels can be broadly speaking of a conflictual nature, or of a cooperating one.
In a good architecture, the two blocks cooperate way more than they fight, and the people are organized in such a way to facilitate working together also on the people's channel.
The architecture has in theory the power to tell the design you're silent and do what I say! However, this is a mistake, because the design, being “the underdog”, can wreak havoc in the implementation.
The design also whispers in the ears of the technical leaders about problems, and its these people's job to listen carefully to its whispers.
If not listened to over a long period of time, the design will cause technical debt and over time even derail the whole architecture.
For this reason, we have to look at both architecture and design and their principles.
Each of the two blocks have a set of principles they should abide by.
In the context of software architecture and design, we have the following personas. Here are their main responsibilities in the context of our discussion
- the architect
- he is the owner of the architecture
- he trains the team in workshops about architecture and design
- he sits occasionally with the team in coding sessions and facilitates the flow of information; on one side, he helps the team see why the architecture is the way it is, on other side he gathers information about the problems that the team is facing with the architecture, in order to adapt it or find solutions
- watches over code reviews from the shade and provides feedback to the team lead about the design
- stays out of the critical path
- works on prototypes
- the team lead
- has a good overview of the architecture
- is more involved in the daily business
- has regular meetings with the architect about architecture, design, and the technological future
- the developers
- they make the code happen
- continuously improve the design
- provide implementations following the outlined design principles
- technical leaders; these are:
- the architect
- the team lead
- key senior programmers
As you can see, everyone works towards the same goal, at different levels of abstraction, but still everyone interacts with everyone else.
Particularly, the architect is not sitting in an ivory tower, but sits occasionally with the team and codes.
- strive for convergence
- analyze inputs, outputs and decision variables for business cases/use cases
- normalize inputs
- single source of truth
- separate reads from writes; CQS
- defer decisions; don't do the buzzword things, do things that make sense, when they make sense; and delay decisions; delay the cloud, delay the microservice
- minimize the number of tools while maximizing the number of problems solved
- put the tools behind your own abstractions or standardized communication protocols; if it's not worth abstracting away, then using the tool as a whole is not worth it
- prefer writing your own glue code to introducing a new tool
- take into account the team, where they are in their professional development and where they want to go
The technical leadership establishes the following principles and everyone works towards implementing and preserving them:
- respect as many as possible of the OOP principles, not just SOLID, there are over 20 of them
- use static analysis
- watch that cohesion vs. coupling triangle!
- be strong in code reviews, don't let things slip through
- separate the domain model from the rest
- encapsulate the rest in plug-ins, adapters and ports, etc
- often you can “cut the cake vertically or horizontally” when designing a system; there's no right or wrong here and you'll sometimes make wrong decisions in this aspect, but: think about what additions and removals of features will come in the future and cut the cake the corresponding way when possible
- for frameworks: do not let them dictate your design; frameworks are also tools; use them as libraries and hide them behind plug-ins
- watch for leaky abstractions
- do not introduce an abstraction if it would be used in only one place
- design for discoverability of the code and self-documenting code
- design for testability
- use the ubiquitous language in the domain model
This section is a WIP. Ignore
understand the needs of the business and translate them into technical needs
What is the process for architecting and designing a system? I'm a going to describe the rough ideas to consider, but keep in mind that many things can be tweaked based on the company's needs, strengths and weaknesses.
For me, the leading principle is convergence: you want the whole system to “use one thing”, act in one way, etc. BE ONE.
Think about inputs and outputs of each business process, and list the decision variables to get from input to output.
Inevitably, you'll have different inputs, but as much as possible, put abstractions in place to transform your inputs into a common format, a common abstraction. I call this “normalize the inputs”.
This is one of the strategies to achieve convergence.
Another one is “single source of truth”.
I would say, don't optimize for performance yet, but implement a process from front to back, a deliverable.
This is where software architecture and software design meet. Software design:
- the trap of design patterns
- the trap of vendors
- the trap of over-engineering
- the trap of TDD
- does not support in creating a great design
- its goal w.r.t. design: protecting inexperienced teams from making bad design decisions
- the trap of buzzwords
- tools, architectural styles (microservices, event sourcing)