In my experience - also using both static (C++, Java, Go, Sawzall) and dynamic (PHP, Python, Javascript) languages - I don't get type errors either. However, what actually brings dynamic languages down in larger systems is the time spent tracing through code to figure out what the actual type of the variable is, and hence what you can do with it. When a script is small this is negligible, but when you've got large teams and hundreds of source files, you can spend 10x the time doing this than actually coding.
I view static typing as essential to larger projects, but for documentation reasons, not performance. The performance and error-checking is a nice boon, but you can recover the former with smart VMs and the latter with unit tests. The documentation can only be recovered by writing your types out in comments, where they won't be checked by the compiler and invariably become out-of-date.
You are right, it would help with documentation, good point.
In general I use integration and unit tests for checking breakage. And of course documentation is in comments but that is not forced. The first set of unit or integration tests also function as "example code". Even when reading documentation I usually gravitate to "examples" section quickly anyway.
I just had smaller components that are rather independent if I can. It depends on the project of course. I guess I've been doing micro-services before they were cool.
I view static typing as essential to larger projects, but for documentation reasons, not performance. The performance and error-checking is a nice boon, but you can recover the former with smart VMs and the latter with unit tests. The documentation can only be recovered by writing your types out in comments, where they won't be checked by the compiler and invariably become out-of-date.