Ask what is wrong with the file actually open in front of you. No tests, and nobody left on the team wrote it → Legacy Code first, always: it finds the seams and writes characterization tests that pin current behavior before anything moves. Tests exist and the structure is wrong — a conditional that has grown a branch every quarter, a god class → Refactoring, which names the smell, picks the matching transformation like Extract Method or Replace Conditional with Polymorphism, and keeps the tests green between each step. The structure is fine and the prose isn’t, with names like data2 and eighty-line functions → Clean Code. Every new feature touches six files, or an interface has more parameters and special cases than the implementation behind it saves you → Software Design, which treats interface-to-implementation ratio as the primary signal. And when the problem is habits rather than any one file — the same knowledge duplicated in three places, modules that break each other on every change, a new system you want proved end to end before filling it in → Pragmatic Programmer.
The pairing that does the most work is Clean Code with Refactoring: one names what good looks like, the other performs the surgery safely. If the code has no tests, put Legacy Code in front of both — everything else assumes a safety net you don’t have yet.