Software architecture (is) for developers (part2)
For a long time, the feeling was that the architecture wasn’t something that teams should worry about. This happened a lot at the beginning of considering the importance of architecture in a product’s lifecycle. Once the agile way of doing things gained popularity, so did the way we shaped the structure of a system. If in the first place, architecture was something that, in most cases, was discussed from the start and followed carefully during development, things evolved in a more organized direction. The need for change was felt more and more; products gain shape while being developed. There were two main reasons for this:
- The tech stack changes “terribly” fast. Approaches and tools evolve so quickly, and we need to keep up to create both the best user experience and solid systems for our stakeholders.
- Alongside the evolution of the tech stack, so did requirements. Users change their minds, the competition in the same market is big, and new features appear constantly.
Considering this, we can’t discuss an upfront architecture that is blindly followed. Creating a system’s architecture is an ongoing process, and, in order to have the best results, this process needs to be transparent for all the stakeholders. Generally speaking, stakeholders come in three categories:
- The ones that develop and work for the product
- The ones that use the product
- Even though they are not using the product or developing it, they are the ones facilitating some processes that help the development.
In this discussion, we will focus on the first category, and more precisely: architects and the development team.
Let me tell you a story. As a software developer, and at the beginning of my career, I wasn’t paying attention to the system’s architecture I was working on. I wasn’t even aware of how things were supposed to work or why we needed architecture for a long time. Even though I was able to finish my tasks and new functionalities were successfully available to the users, I always had the feeling that I was missing something. I wasn’t satisfied with not understanding the whole picture. Being able to touch and work only with the standard components — because yes, when you don’t know the system, you are not confident in experimenting, mainly because you don’t want to break something — gives this feeling of uncertainty. It was uncertainty and the willingness to be more confident in my work that made me dig deeper, try to explain and analyze all the systems that I had the chance to work on, to get their specific features.
In time, even though it was not my role, I made efforts to look at the application from an architect’s point of view while staying faithful to my perspective, the developer involved in the code. After practicing and taking on challenging tasks, I started noticing the effects of this mindset change on my way of approaching my work, even in the small tasks.
Another matter to discuss when considering architecture and an architect’s role is the difference between software architecture and software design. There is confusion between the two, and I think that is because we have this tendency to give abstract definitions.
As already discussed in the previous article, architecture is a blueprint that indicates the high-level units of our system: the way they interact, work and evolve concerning one another. At the same time, the design is more focused at the code level.
Although different, we can not say that one is more important than another. Every developer must know both architecture and design.
Why is it helpful to know how to work with design?
- Enforces code quality standards — when you have a solid system design, it is easier to respect code quality rules and guidelines that the team agreed upon.
- Keeps the system modular: The more we keep the system organized in modules, the easier it is for developers to understand and modify it. When you have a modular system, you can learn about its inner workings step by step. As a result, new developers won’t have to deal with the entire complexity of the system all at once. Although independent modules can’t survive in isolation, they need to interact with other modules. So, besides the modularity, we have to take into consideration dependencies.
- Minimizes complexity — Complexity is everything about your software that makes it hard to understand, modify or extend. Complexity usually comes with a cost that you want to avoid as much as possible. The bigger the system, the harder it is to deal with complexity. To avoid complexity, pay attention always to coupling and cohesion.
Coupling indicates how dependent or independent of one another are components.
Cohesion is the degree of readability and understandability of components. Our components need to have only one responsibility because this makes them easy to read and change without having a high impact.
- Enhances maintainability & extensibility — Modifying existing code can become a developer’s nightmare if the system rotates. Just as architecture, the design should evolve also. It’s preposterous to expect the design of a system to be perfect from the start and suit any scenario. Changes influence design, so the design should incrementally evolve with the overall system.
Very popular and successful metrics are code smells identified by Uncle Bob that can give us a good direction and control on development.
- Rigidity — if you find it hard to make changes, your code is rigid. When the system becomes hard to change or extend, it is probably because our components are very coupled, and your components are not self-explanatory.
- Fragility — if you make a change in one place and, as a result, you get bugs in places that seem unrelated, your system is fragile. Code becomes very hard to maintain in this situation, technical debt is inevitable, and the team will gather frustration.
- Immobility — a system is immobile when you can’t reuse parts from it. Trying to extract a common component becomes both risky and time-consuming.
- Viscosity — When doing a workaround in your code seems less complex than following good practices, you know your system is viscous. Discuss best practices from the beginning and stick to them.
- Needless complexity — this is self-explanatory in the idea that the system is more complex than it should be.
- Needless repetition — This is the consequence of immobility. When you find it hard to extract a shared component, the instinct is to copy code and make necessary adjustments to work with the new context.
It is easy to say, “It’s not my job to know that. It’s not my responsibility to take care of that”. Along the way, I had the chance to interact with teams where it was obvious that the architect was too focused on his “part” and had no idea what was happening in the application. At the same time, as I was in this situation, I could clearly see when a developer has no knowledge about architecture or why some decisions were made.
Suppose you are in the beginning and are not sure how to start in the process of differentiating good architecture from lousy architecture. In that case, you can start by paying attention to some of these characteristics:
- Understandability: a system should be clean and organized. The more coherent the system is, the easier it is to extend and maintain it.
- Usability: is it easy for your users to use the application? Is your application accessible and visible in terms of features? The answers to these questions and some research on your technology stack capabilities will tell you where you are.
- Testability: this one is self-explanatory. Is your system easy to test? Can you ensure high coverage?
- Scalability: your application is scalable if, as the load grows, there is no impact on performance.
- Reliability: a system is reliable if it ensures integrity and consistency in all the common scenarios.
- Performance: we as users have so many expectations in terms of performance when it comes to the apps we use day by day. Performance is an essential characteristic of software architecture.
- Separation of concerns: this principle is tightly linked to the ‘Single responsibility principle’ and “Don’t repeat yourself” principles. Ensure that all of your modules have a clear responsibility and do just one thing. Also, reuse code. Small, straightforward modules make it easier to control dependencies and reuse them.
Disclaimer:
After all, we’ve discussed, a thing is clear: it is crucial to know and use best practices. It is essential to use them in your products and create some rules. But please don’t obsess over them. Keep in mind that you work in real-life, with diverse situations. I’ve been there, doing my best to keep my team engaged and following coding guidelines, until we prioritized functionality and the business was ok with skipping for an increment our technical best practices in order to deliver asap. It was not ok, and I’m not happy with the compromise, but looking back, it was a matter of balance because we found a way of having good code after all. Don’t focus obsessively on aesthetics. Focus on functionality and quality attributes. Remember that you can have the best product from a technical perspective, strictly following all the rules and best practices, but if that slows you down or becomes a blocker, you might want to reconsider your way of doing things.
Want to become an architect? Consider this. Just looking to be a better professional? Also, consider this:
If you want to start to grow towards an architect career or try to understand the architectural perspective and take your skills to the next level, you can start by taking on more challenging and diverse projects. If you don’t know where to start, seek a mentor, someone that can help you shape a direction for your professional goals. Create from every task a learning opportunity and extend your technical skills in different areas. To be a good architect and make the right decision, you need an overview.
In the meantime, don’t forget about soft skills. There is no use in being the best architect if your team does not understand your perspective. Take responsibility and learn to work with people.
There is no exact path to becoming an architect or to understanding architecture. The secret is that there is no secret way of becoming good. It is about taking on challenges, investing in yourself, keeping close to people you want to be like and inspire you, and putting in the extra effort to achieve your goals.