TDD at the Architecture level
Why don’t we do TDD also at the architecture level?
Spoiler alert, I’m NOT going to explain here what TDD is and how to start working with TDD, but I have linked some links in the References section.
When it comes to TDD (Test Driven Development), there is a lot of good feedback out there. Many books are written and you find plenty of articles on the internet with reasons why you should do TDD and how helpful it is and how has changed the life of many developers.
I have tried TDD for myself, and in the beginning, is a little bit uncomfortable, is a new way of thinking. You develop at a slower speed, but with time the speed will increase. You don’t need to worry about your code not working as expected, not being fully covered with unit tests, because you have those things covered upfront(If you write the good tests ;)). I really believe that TDD can provide real value for a software company. Although may not be suited for every company and every environment, for most of the companies is useful.
But what about TDD at the architecture level, have you ever thought about that?
While practicing TDD, this question comes to my mind. I thought about it, and I think we can call that a TDA (Test Driven Architecture).
In order to answer the question, I think we need to explore the following questions first:
- Does it bring any value if we did it?
- Is it possible to do it?
- Is it hard to do it?
- When do we need to do it?
- Is it worth doing it?
- Has someone tried to do this?
I will try to explore those questions and be as objective as I can.
Does it bring any value if we did it?
In my opinion, I believe that it does. The same way TDD brings value at the code level, the TDA can bring value at the architecture level.
As an Architect when you design a system you choose the architecture and technologies bases on your past experiences and what others that had similar problems have done. But you don’t have the certainty that those are the right decisions. You only find out if your architecture is the right one after you reach the production stage. But reaching the production and finding out that those decisions were wrong is not a pleasant thing. Hopefully, you have done some testing first, but I personally know many cases where problems were found directly in production.
If we only had a way to find out from the beginning, if the architecture patterns, technologies, and the actual implementation supports the business needs… Well, I think we could, and is called TDA!
Another great benefit of TDA is the fact that helps to reduce over-engineering. Many times we as architects or software developers do extra things and come up with complex solutions that many times are unnecessary. TDA can prevent this, if you have a business requirement, you write a test, and if the solution satisfies the test, that’s it, no need to do extra work.
This brings us to the next benefit, freedom to refactor. As long as you have tests put in place, and you trust them, you can refactor, and if the tests are passing you are confident to push the solution on production.
It can help also with the technology choice. One of the most important decisions, when you develop a greenfield project, is the technology choice, it needs to support the system quality attributes. If you practice TDA, this process can be a lot easier, you can pick a technology, and if your tests are passing, it means that it fulfills your business needs, you don’t need to be worried about it. It also means that you can change it later, as long as with the new technology the tests are passing.
Is it possible to do it?
I think is possible to do it. Let’s take an example to see how this can be implemented: Let’s say that you are a financial company and you want to build a product that will serve millions of customers. The Architect and the Bussines have a meeting and establish the non-functional requirements: Availability, Security, Scalability…
Now imagine this, before starting to implement anything, for each non-functional requirement you start to do TDA. I will take as an example the non-functional requirement: “The system should be able to handle 100 000 users at the same time, each user doing different actions in the system.”
- RED. First, write a test that simulates 100 000 users in the system at the same time, and this test should fail. The impossibility of reaching the system is also a failure.
- GREEN. Start implementing the production system as soon as possible sufficient to pass this non-functional test even if it means allowing the “worst” solutions. Of course, if a clean and simple solution appears immediately, it must be implemented but otherwise, there is no point spending a lot of time because the architecture will be improved incrementally during the refactoring phases. The aim here is to obtain as soon as possible the green bar of success of the tests.
- REFACTOR. This phase is easy to be neglected but is essential because it eliminates bad implementations that we may have done but also makes it possible to make changes in architecture. This refactoring concerns both the production code and the test code and must not modify the external behavior of the program, which is materialized by a test execution bar that remains green.
Of course, many of the non-functional requirements are hard to automate in tests, but others are not so hard. Some of them can not be automated, some can be automated, but to a certain degree. Ex: Security. You cannot fully cover the system with tests regarding security. But you can write tests for the existing vulnerabilities and as other vulnerabilities appear, you can add them later.
And in the end, the benefits are huge, you can change the architecture, the technologies, the code… and not worry, as long as the tests are passing you should be confident that that was the right decision.
Is it hard to do it?
Well… Yes. Technically is much harder than doing TDD. Writing a unit test is easy. Writing a performance test is hard. Writing a security test is hard. Basically, all non-functional requirements are hard to test. Probably that’s why there is not so much activity in this area. They are hard, but not impossible.
The good news is that you don’t need to test them all, you just pick the most important non-functional requirements for your system, it can be 3, 4, 5, it depends on the system.
Is it worth it? and When should we do it?
Well… as with any other question in our industry … it depends.
There is clearly a lot of work involved in building, running, and maintaining those tests. But if for the business those non-functional requirements are very important I think is worth doing it.
Take for example a Financial system, the security should be a top priority, so for me makes sense to have some automated test that tests the system for security vulnerabilities, and having those tests from the beginning will drive our architecture also. Ex: we don’t need to choose a particular technology just because others say is secure, but because it passes our security tests and fulfills business needs.
Also, it can make sense to use it when you are splitting the monolith into microservices, or any other architecture refactoring. First, you cover the monolith/existing system with non-functional tests, and then, you can start split and be confident that the new system will keep the same non-functional requirements.
For startups, it depends, but for most cases, I don’t think is suitable, at least not until the startup is mature enough. Usually, on startups, things change pretty quickly, also the non-functional requirements can change. Spending time that you don’t really have on performance testing on a system that you don’t know if it will succeed is not a good idea. Let the startup mature, and then you can do TDA too.
Has someone tried to put this in practice?
I don’t really know. I searched on the internet but have not found any study case… Clearly, the idea is not new, I found these articles.
If you have used it or know someone who has used it please let me know in the comment. I’m curious about the results.
Conclusions
I’m not an expert in the TDD and TDA.. and I don’t claim to be so. I just happened to be passionate about software and software architecture. I started to practice TDD and this question pop up to my mind: “Why we don’t try TDD at the architecture level?” In this article, I tried to write down my thoughts.
So back to the question: Why we don’t do TDD at the architecture level?
Well, basically I think mainly because is hard and time-consuming.
Should we do it? It depends, but I really think that there are cases where TDA can prove to be very useful.
What do you think? I’m curious about your opinion, you can leave me a comment.
Also if you found this article useful, just clap ;)