If your code is stable, then it can withstand the turbulence of an ever-changing environment. Stability in code is measured through many attributes. You can measure code quality; determine how rigid and fragile your codebase is. You can measure cohesion and coupling; how focused are your classes, and how interconnected they are.
Metrics improve the stability of your code, but they are one step away from stopping instability, that is, they are not integrated into your codebase itself. If we gather metrics in a different phase from writing the code, then it will be one step closer to not being done. If we have to run our metrics after a code change, then we will be one step behind. So how do we improve stability and be as closer to the code? How do we change our work ethic, promoting stability to a first-class citizen? We modify our development methodology, and begin with stability up-front.
If we want to increase the stability of code, then we need to start with testing it. Tests serve two purposes, one is to provide a way to stop regressions of the system from happening; one is to define the acceptance criteria of a specific method and/or class. Unit testing your code can act as a first barrier of entry to bugs creeping into your code. If you have a firewall of tests that protect you from making your system unstable, then it can become a safe-haven of keeping your production code stable.
Test-driven development is a methodology in which you write tests up-front, before writing any implementation code. This is important, as it means you can be sure that you are thorough with testing, and it can be a great way in which to think out the solution to your user stories. If we are going to weave our testing into our code, then why not define it first so we are sure not to miss anything?
The second level of testing to ensure stability should be integration tests. A unit test is seen as the first level of testing that can provide assurances that nothing on the ground level of your code has regressed. However, there are points in your code where you will need to interact with outside sources, such as databases, apis, and system information.
In the current world of agile, we should provide someway in which to give continuous integration; continuous value to the customers that expect stable code. The last barrier that should be set up in order to prevent unstable code, is within the build pipeline itself. The build pipeline should run all the unit tests and integration within your codebase. If any test fails, then it should error and stop the build, subsequently stopping the code from being released to production.
Not only can you monitor issues in a production environment, but tools like Bugsnag can filter, triage, snooze, and even ignore problems altogether. This can help with the noise-to-signal ratio, as you will be dealing with a vast amount of data. If you have a high noise in your data, then it can be hard to figure out what is something that is a true signal that you should fix.
In this article I have given many ways in order to implement testing as a way to keep the stability of your code consistent. Metrics are a great way to understand where the problem areas of your code are. But a prerequisite is to understand what is in your codebase to begin with, and making sure the changes you make to your codebase are not creating regressions.