When you are building infrastructure (buisness logic independent), static languages are better, because most things are clearly defined, and change rarely, so overhead is small, and the code is general (few special cases).
When you build the real thing, that includes business logic, dynamic languages are better, because it's much easier to change business logic and add special cases on the fly. And there are always special cases.
That's why big projects written in static languages often have implementations of some dynamic language embedded, and when they don't, they use unholy mess of configuration files and dependency injection to achieve the same goal.
I much prefer intentionaly designed embedded dynamic language to writing implementations of factories and changing xml files.
To see how much dynamic your static code is count all the casts from Object, and all template classes instanced for Object (especially common are List<Object>, Map<String, Object>).
Writing infrastructure code in dynamic languages - I agree - it's harder to get right than with staticaly typed languages, and the benefits aren't obvious.
(The language I'm using is Ruby).
The problem is not that Ruby isn't statically typed. It's that what I think of as type errors in Ruby pass without incident. The most irritating example of this is that if you don't pass enough arguments to a proc object the other parameters get filled in with nil. In my mind this should throw a type error, but in Ruby it doesn't.
Perhaps this is why Rubyists are so keen on testing, because their language is full of implicit behaviours.
Maybe automate checking this? I don't know Ruby much, but there's .arity method on proc that seems to be apropriate for this.
x = ->(a, b) { puts a, b }
x.call #ArgumentError: wrong number of arguments (0 for 2)
x = lambda { |a, b| puts a, b }
x.("just one argument") #ArgumentError: wrong number of arguments (1 for 2)
blk = lambda { |arg1, arg2|
# do something
}
obj.some_method(&blk)