Static typing provides universal guarantees, which sounds great at first, until you realize that it really does mean "universal". Sometimes that extreme rigidity makes changes very difficult, especially when you're dealing with cross-cutting changes across many teams and organizations.
I work in a very large company with lots of services. The best practice, espoused by most of the senior/principal engineers (particularly ones with extensive SOA experience), is to keep your service interfaces and document models dynamically-typed. It makes them much easier to change. Big systems should be assembled from loosely-coupled components which are internally rigid but externally flexible and dynamic.
When operating at any sort of scale, a system that's "easy to change" stops sounding virtuous. Communicating and integrating service changes effectively is usually more expensive than making the changes themselves, for a large organization. Providing service descriptions which can be converted into static types (e.g. swagger) is very cheap, and can prevent a host of expensive human errors.
In truth, poorly specified services or documents can end up impossible to change, since the risk and impact of doing so is difficult to size (or the exercise of doing so costs more than the change is worth).
In many situations, a true statically-typed service interface can be nearly impossible to change, or even add fields to. Adding new schema versions (particularly for write APIs) or dynamic/polymorphic schemas that change based on the input are extraordinarily useful and very easy with dynamically-typed interfaces, but can be very difficult with strict statically-typed interfaces, particularly the kind that e.g. Java nudges developers towards.
There are some hybrid frameworks that mix dynamic and static typing, which solve some of these problems (e.g. protobuf, Avro, Ion). They make reasonable compromises.
Confirmed.
TBH there are very few languages that I'd claim were a bad idea. The point I'm making is not "Statically typed languages are a bad idea" it's "If you think statically typed languages are going to make software correctness magically better then you're going to be disappointed".
> My general experience of the correctness software written in fancy statically typed languages is not overwhelmingly positive compared to that of software written in dynamic languages. If anything it trends slightly negative.
ETA: The point of that anecdotal impression was not "This is definitely true" but "Given that this is my general experience it would be surprising if the actual trade offs experienced in practice were such that static typing was regularly a clear winner", which I feel much happier supporting on an impression than "You should do this".
Not everybody writes new stuff; some software has to interface with stuff written decades ago.
Also, even for software 100% written today it often is impossible to stay in a world where all strings use compatible encodings.
Examples where you need conversions: when connecting systems that support different Unicode versions or when generating http headers, windows file names, or QR codes. I guess many 'older' systems will not handle ZIP or tar files that use UTF-8 for file names in the archive, either.
I have a smaller Scala code base to maintain (around 50k lines) and it is a breeze b/c many types are annotated, e.g. Money @@ Netto or String @@ InvoiceNumber. Beside Option this has been the greatest productivity gain vs. Java.
I don't think the OP would claim TypeScript is a bad idea, as, with its relatively weak type system, it doesn't really suffer from "if you find a bug it’s easy and unintrusive to add a test for it, but may require a substantial amount of work to refactor your code to add types that make the bug impossible".
As an example of a strong type system, in Haskell code, the database access layer will may types such as "ISO-8859-1 string of at most ten characters"
If it turns out that you want to store ISO-8859-2, changing that type can and often will cause a cascade of necessary changes, for example because you're calling a ISO-8859-1 to UTf-8 conversion function, and the compiler will not accept that call to now take a ISO-8859-2 string, or because you are concatenating that string with another ISO-8859-1 one. Then, you will have to decide whether to convert locally or whether to also change the type of that second string, etc.
Now, of course, each of those compilation errors becomes a bug if you just use variable-sized byte arrays. Question is how much work you are willing to do to prevent those bugs.