Sounds more like a problem with tight coupling than a problem with types per se. A function should not change behavior due to an "unrelated change", regardless of static or dynamic typing.
``` function foo(config: {hasYakShaver: boolean}) { if (config.hasYakShaver) { // ... } else { // ... } }
foo({hasChuckYeager: false}); ```
This will accidentally do what I want, but for bad reasons. And one day, quite possibly, a change that appears unrelated will break this.
And that's me being nice. If I start using reflection to do complicated stuff, or if I'm using C++ and the reason for which my code works is memory corruption... well, when I need to figure out what suddenly broke the code, I'm going to be up for serious a moment of loneliness.
If you have unit tests (or other types of automated testing) you have a much bigger chance of catching logical errors and it will also catch trivial error like misspelling an identifier.
> Testing for the correct behavior should be enough.
Also, in my experience, automated tests tend to catch many bugs, static typing also catches many bugs and defensive programming catches many bugs, too. There is an intersection between all three, but it's by no mean 100%. So I use all three of these.
And of course, let's not discount monitoring, fuzzing, etc.
The only ways I know of to get rid of (some of) these bugs are to: - use as much static typing as possible; - wherever static typing is not sufficient, sprinkle this with a paranoid dose of assertions.
If I read what you write correctly, that's way beyond "testing for the correct behavior", no?