3 ms·
I don’t try to separate every last code change that doesn’t affect behaviour, but I often find it easier to put major refactorings in their own commits. This ha
by Chris_Newton 5y ago
I don’t try to separate every last code change that doesn’t affect behaviour, but I often find it easier to put major refactorings in their own commits. This has at least two advantages: it means you know there shouldn’t be any test changes showing up in the refactor commits, and it means there is less to understand and review in other commits that do intentionally change the system’s behaviour.
It’s not really a lot of extra work to do that once you get into the habit. Tools like `git add -p` to stage partial changes in files can be useful for separating self-contained refactorings from behaviour changes if you realise they’re happening at the same time and starting to get tangled.
- deleted 5y ago[deleted]
- chriswarbo 5y ago> Tools like `git add -p` to stage partial changes in files can be useful Magit is great for this: it shows an interactive version of `git status` (the "status buffer"), with diffs that can be expanded/contracted for each file and for each hunk; we can also selectively stage only a few lines from a hunk.
- Agentlien 5y agoTim Pope's git plugin for Vim, called fugitive does the same.
- ninkendo 5y agoWhat world do you live in where you can refactor without changing tests? I mean, it sounds like an ideal one but I’ve never lived in such a world. Tests always seem to couple to the way code is currently organized, and I can barely imagine any refactoring change that wouldn’t require some changes in my tests.
- Jtsummers 5y agoIt depends on your tests. Your integration tests should never be impacted by a refactor, or it's not a refactor (using the definition that refactors don't change interfaces). The tests need to be rerun, but they shouldn't be changed. Your unit tests and sub-integration tests (integration of multiple units but not the whole) may be impacted by some refactors, though, but that's pushing the definition of a refactor. Your public interface to your lower level modules/classes are the only things you need to directly test. If that's your smallest unit for testing, then a refactor should also (again, in theory) not impact them because the refactor should not change that public interface, at most changing the internal (private) functions and structure which the existing tests can verify just as they verified the unit before. If you restructure your units (split a unit into two, for instance) then you'll have to change your unit tests and possibly some integration tests if you instantiated the previous unit directly as part of the test. But that's pushing the definition of a refactor.
- Chris_Newton 5y agoIf you use very low level tests then sure, maybe you need to adjust your tests to match the adjustments in the rest of your refactored code. But since by definition a refactoring does not change the code’s observable behaviour, no tests above that level should be affected. I find that very low level tests tend to be fragile and have limited value in most situations as a result, so I don’t personally tend to use them much, but YMMV.
- eyelidlessness 5y agoI routinely refactor without changing tests, and it’s a primary concern when I define interfaces and determine what to test. For what it’s worth: - This works best if you actually embrace BDD to test for behavior rather than results. The behavior is less likely to change, and along with points below so too are the interfaces under test. - I find the least test change in statically typed languages where defining types is the first “test”. Behavioral interfaces become much more stable as you define them if you actually know what they are upfront. - Both of the terms “integration test” and “unit test” discourage this. Validating what your program actually does usually sits somewhere in the middle, and allows you to encapsulate a lot more of what would otherwise likely change in tests. - Stricter interfaces fare better than looser. That doesn’t mean give up on the Robustness Principle, but it does mean don’t let it become infectious (I now want to blog about the “color” of functions which accept “options” or other miscellany). The sooner you parse/validate/have concrete and reliable types, the less your code under test and the tests themselves will change. And that, while it’s my last point, is the most important: narrow types early. Get stability inside the seams of your program.
- LoriP 5y agoAgree here. Helps those who follow to separate clean=up/optimisation work from change of function and specific bug fix work. Seems it would totally help code quality over time.