In 2019, Paula Paul and Rosemary Wang wrote a blog post about the fitness function-driven development concept. I will explore this concept here and show you how we can write simple fitness functions using Python and what advantages and disadvantages this concept may introduce to your projects. The article is not a full-scale guide, but rather an introduction to the concept so you can start digging yourself.
Table of Contents
Fitness Function
A fitness function is a concept that allows you to keep an eye on software architecture metrics that are important to you. The core of the concept is to ensure that the system’s architectural metrics are close to (or do not violate) the architectural goals that we currently have in our project when the developers are developing the application.
Fitness function-driven development
Most developers know TDD (Test-driven development), which focuses on writing automated tests before adding code related to the features. Fitness function-driven development re-uses part of that approach. However, in this case, we focus on automated tests not for specific features but rather on automated tests for the entire system architecture. Thanks to that, we can check how close we are to our architectural goal or if developers do not violate anything on each application build using CI tools.
Why would we use it?
The world is constantly changing, and so is the software. Software architecture is usually a complex structure that takes time to judge. Having a tool that would check if the system meets its architectural criteria without manual intervention is very useful, especially for software where some metrics are not visible at first glance.
An added benefit of automated architectural metrics is that with the growth of the system the earlier decisions won’t be swept under the rug and at some point where the metrics stop reflecting the business reality, a conscious decision of the development team must be made whether they should go back or change architectural drivers of the application.
Core metrics
The authors of the concept listed several metrics/concepts that could be monitored with fitness functions, and I will focus on these two:
- Code quality — whether our code is on some level of quality. For example, we may have a function that checks the test coverage across the system. The other idea that comes to mind is to check whether code is accessed in the correct layer in the layered architecture.
- Performance — we might monitor whether our core modules are good enough to support the production workload.
Advantages
The biggest advantage is that we can monitor the quality of architecture with each improvement/change made to the code. For example, if we decide that performance is the most important concern, we could monitor the performance of core modules of the domain and see if it’s good enough for us.
Disadvantages
The first thing that comes to mind is that we need to write the fitness functions first. Also, we need to maintain them, and it wouldn’t be wise to keep them out-of-date when our architectural aim has changed.
Examples
Fitness Function to watch over layers
To watch over layers in Python, we could create a simple function that analyzes the imports of a given file and verify if a specific keyword is not used in the file that we should check. The fitness function could be created using a simple lstrip
function that would check whether something is an import. However, to maximize the results, I will use Python’s ast
module to help me process the syntax from the file that I specify in the path
argument. It may look like this:
Usage
Given the following structure of files:
/app
/web
gateway.py
/component
__init__.py
persistence.py
service.py
We want to test that persistence layer functions are not used in the web layer. We could create the following pytest
test:
If the analyze_imports
function raises the ValueError
, then the persistence
module is accessed in the gateway.py
file in the web
layer.
Fitness Function to watch over the performance
Performance should almost always be taken into account when developing an application. More often than not, good performance is required by the client. That’s why we might want to check whether the performance of our code stays on a good enough level throughout the changes applied during the development. The simplest example of a fitness function that would check performance is measuring the time of the execution of a given function. For example, it could look like the following snippet of code:
As you can see above, we simply take the callback function, execute it and check how long the execution took. Now, it’s time to generate a proper test that uses the measure_execution_time
function. For starters, we could create a simple example-based test with one example of the following implementation:
We can create a property-based test if we want to check more than one specific example. To do that, we have to install the hypothesis
library using pip
package installer:
pip install hypothesis
If we have the hypothesis
installed, we can create a simple property-based test that will generate more examples for us:
Conclusion
Fitness functions are a great way to support the architect. They can help you more quickly diagnose potential architectural problems in the code from the very beginning of a project you’re working on. Furthermore, they can be a good way to find out which system components might require a refactor to decrease the technical debt. Sadly, we need time to write and adjust them during the project. Thus, use them wisely and don’t overengineer your projects with tons of them for modules that are unimportant to both your client and the project.
Fitness functions can be an important step to long-lasting, maintainable software…
Schedule a consultationKamil Kucharski is a dedicated Backend Developer at Makimo, constantly exploring the dynamic terrain of DevOps, AWS, and Software Architecture. A fearless hero in the tech world, he confidently navigates his way through complex challenges, often sharing his insightful discoveries through articles on Makimo’s blog. Guided by his passion for Python and Clojure, he continually seeks the right tool for every unique challenge. Outside of his professional journey, Kamil channels his energy into mastering the art of Kendo, embodying the fearlessness and heroism he expresses at work.