This is a major part of why I like languages like rust. I can do some pretty fearless refactoring that looks something like:
- Oh hey, there’s a string in this struct but I really need an enum of 3 possible values. Lemme just make that enum and change the field.
- Cargo tells me it broke call sites in these 5 places. This is now my todo list.
- At each of the 5 places, figure out what the appropriate value is for the enum and send that instead of the string.
- Oh, one of those places needs more context to know what to send, so I’ll add another parameter to the function params
- That broke 3 other places. That’s now my to-do list.
Repeat until it compiles, and 99.9% of the time you’re done.
With non-statically-typed languages you’re on your own trying to find the todo list above. If you have 100% test coverage you may be okay but even then it may miss edge cases that the type checker gets right away. Oh and even then, it’s likely that your 100% test coverage is spent writing a ton of useless tests that a type checker would give you automatically.
As nice as weakly/dynamically typed languages are to prototype greenfield code in, they lose very quickly once you have to maintain/refactor.