I've heard this argument a lot, and I disagree. If your software is changing a lot, that is where types really shine. Refactoring is a breeze when you have types: you just change the code you want to improve, and all use sites are pointed to by the compiler. This is taken from daily experience: I work on a code base that is about 25K lines of Haskell and 35K lines of Javascript. Refactoring the Haskell is a pleasure. Refactoring the Javascript is something we dread, and always introduces bugs, some of which might linger for up to a year.
I've actually had this experience a couple of times on a project like upgrading a library from one release to another when APIs were updated. The compiler would often "show the way" by highlighting every mistake between version x and y.
I would go so far as to say one of the most evil things a person can do when designing an API for statically typed languages is using the equivalent of System.Object unnecessarily.
Exactly. A strong type system enables more rapid iteration than a weak one, just like writing unit tests. I was a fan of dynamic languages until I used Haskell and Scala, and saw how powerful types could be when they allowed you to express intention rather than getting in your way.
Now I relegate dynamic languages to writing single file scripts or less.
I think on top of this the self-documenting properties of a nice statically typed system push back the onset of code ossification quite a lot. I always trust my type documentation while I tend to be a skeptic of documentation that's more than a few months old unless it's being actively refactored regularly.
A strong type system can give you a lot of "free" documentation and I love it! I long for a search engine like Hoogle in other languages (http://www.haskell.org/hoogle/). Most functions are very obvious from their signature and name.
My favorite example are two functions with the signatures:
fun1: a -> a
fun2: Int -> Int
You might think the first is more powerful because it takes any type and returns that type, but ultimately, that function can only be the identify function (barring unusual things like exceptions, undefined values and runtime introspection). With the second function it could do all sorts of things. It might increment, it might decrement, it might divide by two and round down if the argument is a multiple of three.
I'll go further to say that it's very frustrating in languages like C++ and Java to have to deal with statics and objects. They have a lot of hidden state that isn't obvious from the type signature of their methods. Particularly the fact that some methods must be called before others—e.g. initializers—can make the behavior and side-effects non-obvious.
I agree. I define empty interfaces and the types to go with them for prototyping, and it makes building my prototype so much quicker. And with a better final product, too. So IMO static (or optional/gradual) typing can even be better in that way, but only if you think in types rather than hacking away til it works.