Blogg

Här finns tekniska artiklar, presentationer och nyheter om arkitektur och systemutveckling. Håll dig uppdaterad, följ oss på LinkedIn

Callista medarbetare David Ström

The black t-shirt architect, part 3: Evolution is inevitable

// David Ström

This is the third and final part of my blog series The black t-shirt architect. Thus far in this series we have concluded that there are no perfect solutions in part 1 so that any architectural decisions becomes a trade-off. We followed up by looking at a way to document architectural decisions in a clear and concise way in part 2. It is finally time for Evolution is inevitable in which we look at how we can balance the need for up-front design without falling into the trap of overengineering the solution.

Introduction

First off I need to say that I have been looking forward to this, mostly because I think the title for this part is the coolest of the lot: “Evolution is inevitable”. It sounds almost a bit omnious, but maybe that’s just me being old and afraid of being replaced? If you share in this apprehension however, let me just say right away that there is nothing to worry about.

In this blog we will again take a deep-dive into one of the most prominent challenges of software architecture design. This is the challenge of designing software for a future that we know almost nothing about, except that it will be different. We will consider some strategies that we can employ to be better prepared for the unknown. We will also try to create a complete picture of how the three rules of architecture covered in this blog series work together to guide in designing our software.

Evolution Evolution: For me the development of waxless skis must really be considered the pinacle of technical evolution.

The challenge

As software architects we need to balance a lot of different needs. One such need is to develop an architecture that will remain mostly stable over time while also concrete and detailed enough to give useful guidance to developers. We need to do this while at the same time taking into account that business requirements, external dependencies as well as organisational structure can change at any time.

A natural response to this need is to attempt to predict some future state, often the furtherest we feel confident enough to predict, and then target the design for that predicted future state. We have all been there, trying to predict future load on our APIs or upcoming feature requests from the business people. What this can lead to is a sense of preparing for the future when in fact we are only preparing for a future. The key here is that evolution is not only inevitable, it is also constant. It is therefore necessary to design for evolution, rather than a particular future state.

Design for evolution

Architecture is not a one-shot deal, rather it needs to be constantly updated to realign with changes in requirements, technical infrastructure or the organisation itself. However, in order to be able to do that we have some prerequisites. We need strategies for future changes to the application, such as API versioning and tools to handle database schema migrations. Alternatively we can use NoSQL schemaless persistance and very open API definitions like GraphQL, but both of these options come with their own trade-offs. Example of trade-offs in these cases could be difficulty in maintaining data consistency in a NoSQL database or difficulty implementing fine-grained authorization control on a GraphQL API. Consider the different options and evaluate the trade-offs between each, then document the decision and the reasoning why a particular alternative was selected, see the first and second rules of architecture.

Design for fitness

A strategy well worth considering when looking for ways to manage change is the use of a fitness function for your code. What I mean by that is to use a set of metrics that together tell you something about how clean, neat or fit your code is and by extension your application. By defining a set of rules such as “a class should not exceed 300 lines of code” we can then measure how well we live up to those rules and get a combined measure of the fitness of our code. I will simply make the argument that a clean, well-structured code base is easier to adapt to new demands than a messy, clutted one. Based on that I will further argue that to keep our code neat and tidy is not just a matter of aesthetics, it is a central part in designing for evolution.

Know thyself

A second strategy to employ when dealing with evolving needs is to keep a very close look at what’s running in your production environment right now. Do you know what it is doing? Ok, fine, but do you know why it is doing those things? What I am talking about is of course observability. My colleague Martin Holt has already written a great blog series about Observability with OpenTelemetry, plus he is also giving a presentation on the subject in the upcoming Cadec, so I won’t get into any details here. What I will say though is that with any modern observability tools you should be able to understand not only what is happening but finally why a particular downstream API is getting so much load.

By combining logs, traces and metrics under one umbrella we can gain a much clearer picture of what our application is doing and why it is behaving the way it does. This clearly indicates how our application is performing on a number of non-functional characteristics and the only part that remains is the hard one: dealing with problems. The first phase of solving any problem though is to know you have a problem. After that you also need to understand the problem. By configuring your application for observability you should be well on your way to solve those first two steps.

Using the three rules of architecture

We have now looked at three fundamental rules of architecture. I think we can look at these rules as a system to guide the architectural design forward, the first rule makes us consider and evaluate various alternatives. The second rule makes us document our decision and the third rule drive the overall design of the application to be adaptable to future changes. Since things will constantly change our solution will never be complete or fixed, but by being able to clearly observe how our system performs we can take actions to adapt.

These adaptions will involve some trade-offs however as new tools, patterns or frameworks might be introduced, and those trade-offs will need to be documented. The circle is closed, and this blog series is coming to an end.

Conclusions

Just as there are no perfect solutions there are no final state to which we should aim our design. Change is constant and that means our applications need to constantly evolve. This was the third fundamental rule of software architecture. We can measure our code for fitness, we can think of this as a measure of how easy the code will be to adapt to new requirements. We can also observe how our application is actually performing in a production environment, this should give us a fair indication of when architectural measures are required. By combining the three rules of software architecture we should have a framework to deal with many of the most common challenges faced by software architects. Finally, a big thank you is owed to Victor Rentea whose course on Clean Architecture was a big inspiration for this blog series, thank you! And also a big thank you to anyone who has followed along in this blog series, thank you too! Merry Christmas and a Happy New Year, everyone!

Tack för att du läser Callistas blogg.
Hjälp oss att nå ut med information genom att dela nyheter och artiklar i ditt nätverk.

Kommentarer