There are some good tips here.<p>One comment about this:<p>> But what if you can’t add a test for something because the function is untestable? You could refactor it to be testable, but that’s not safe, because you don’t have a test. It’s Catch-22.<p>> Can we do better? Here’s one emergency rescue procedure for untestable functions that I’ve used successfully. Ignore the existing function and write a test for the function you’d like to have. Then make that test pass by writing a new function from scratch. Finally, delete the old function.<p>But how do you ensure that the new function replicates the same behavior of the old one? What's suggested is essentially to do a rewrite, but that's as risky as introducing any change to the untested code.<p>One alternative could be to test at a higher level by adding an integration or E2E test. This way you encode how the system should behave functionally, which at least gives you some safety net to do lower-level changes. Then you could refactor the function to make it testable, or use the rewrite approach to remove it altogether.