Fearless Refactoring, Not Reckless Refactoring
I’m a big fan of fearless refactoring, the agile concept that a developer should be able to incrementally change code without worrying about breaking it. Without the ability to fearlessly refactor, it is difficult to implement an application iteratively with confidence.
Unfortunately, I often see what can only be described as reckless refactoring—believing that you can iteratively build quality software without an adequate safety net to detect and correct defects quickly when changes are made.
When reckless refactoring is done, it is common for management to mistakenly blame agile for quality problems, when in fact it’s a fundamental lack of understanding about how agile works that’s to blame.
Here are some key concepts to implement to avoid reckless refactoring.
Automated regression tests
While we may argue about where, when, and how to best automate tests, strong regression test suites are essential for fearless refactoring. Without them, extensive manual testing must be performed continuously as a safety net, and that’s not a good use of your testers’ time.
Testers should spend their time exploring the product, writing tests from a user perspective, validating acceptance criteria, and working with the business and development to refine user stories, not rerunning extensive suites of manual tests every time a change is made.
Integration into CI
Regression tests that are only run periodically don’t allow you to fail fast and find defects early. Unit-level tests for all components should be created as code is implemented and rerun every time a new piece of code is checked in.
Depending on how fast these unit tests run, you may be able to work lightweight security and performance testing into CI as well. This should include both static and dynamic analysis so that you are not only identifying defects, but watching for unhealthy trends in your code base.
Continuous regression testing
Beyond validating your changes during CI, you need a way to continuously run more extensive automated regression tests each time your application (or service) is packaged. Ideally, all regression tests at all levels run at least once per day.
Many organizations are now leveraging the cloud and parallelization to automatically set up and run large regression test suites hourly to reduce even further the time between introducing and identifying a defect.
Running automated tests does no good if you don’t make the results of those tests actionable. Often, tests are run but the results are ignored because the team is “too busy” to correct issues being identified.
Putting in place the appropriate quality and security gates to decide whether the issues discovered are important enough to not allow code to move toward production is the best way to make sure key issues are corrected and do not just pile up.
Code built using TDD
The best way to fail fast and have confidence that your changes haven’t broken anything else is to create a test for a change before even implementing it! This way, code that doesn’t work as expected is identified immediately and saves you the time of checking in something that will just be bounced back to correct.
Thinking of tests as part of your specifications is a great way to make sure you build the right thing as you go.