2 ms·
You may get something out of Michael Feathers' book working effectively with legacy code [1]. There's a more recent interview with the author that might be inte
by shoo 4y ago
You may get something out of Michael Feathers' book working effectively with legacy code [1]. There's a more recent interview with the author that might be interesting [2]. Feathers' book is well suited to provide guidance in situations where there is a large amount of complicated mission-critical code without any automated tests, which must be changed to fix defects or add necessary functionality, but no one has confidence in changing the code, as the risk of introducing new defects is perceived to be too high. The book is roughly an introduction to test driven development and refactor-to-test in a controlled way, for the situation where it isn't a greenfields software project but there is a pile of existing code that was developed without any tests.
But perhaps that's not quite the situation your employer is in, if people have confidence to change the code rapidly and iterate quickly. Perhaps the cost to your employer's business of shipping a defect is not very high, so it is somewhat reasonable to cut costs on QA and focus on rapid feedback loops, or piling on features, or so on.
Some forms of automated tests are very expensive to maintain -- especially if tests are not well designed and are tightly coupled to implementation details that change frequently, producing a new low-value job of someone needing to painstakingly update the expected values hardcoded into the tests after each code change -- but other kinds of automated tests can have a very good ROI -- e.g. a basic "smoke" or "integration" test that automatically checks if components can integrate and do something successfully along some simple happy path scenario. Any complex algorithmic code (business rules) or security critical code (e.g. logic controlling authorization) should - where practical - be structured as functions (ideally pure functions) without any dependency on the environment or external integrations - the kind of code that is easy and cheap to unit test. Then write decent unit tests for that easy-to-test complicated or critical logic, where the tests assert properties that are actually functional requirements - not implementation details. Ensure the unit tests are regularly run in CI, and that development halts if any of those tests fail, until someone has fixed it.
Another interesting viewpoint about how to structure code and test code is Gary Bernhardt's "boundaries" talk [3] -- this gives some guidance on how to factor codebases into components with complex logic (that can be unit tested) and "straight-line" code that integrates stuff (which can be exercised with automated integration or smoke tests).
Another idea, orthogonal to testing, is designing the system to be more robust against failure, when failures inevitably occur. Sounds like your team is already using a few of these techniques (e.g. enough observability of issues in production to rapidly detect and fix some issues when they occur and cause impact). Michael Nygard's book Release It! [4] discusses some ways to do this.
One thing to try with books is to ask your manager and see if they are willing to have the company pay for useful technical books and make them available for staff to borrow and read. Probably quite easy to make a business case for spending a few bucks on a book about automated testing for staff to read and discuss, ... provided management understands the potential business value of reducing the number of defects that ship to production.
[1] https://www.r7krecon.com/legacy-code https://www.r7krecon.com/legacy-code
[2] https://www.infoq.com/podcasts/working-effectively-legacy-code/ https://www.infoq.com/podcasts/working-effectively-legacy-co...
[3] https://www.destroyallsoftware.com/talks/boundaries https://www.destroyallsoftware.com/talks/boundaries
[4] https://pragprog.com/titles/mnee2/release-it-second-edition/ https://pragprog.com/titles/mnee2/release-it-second-edition/