The problem with the static types concept is that it forces us to think about and create our categorization (classes and interfaces) during the initial state of the project. We make assumptions. We create classes and interfaces that follow design patterns, Liskov substitution principle, SOLID principles, but with the limited information business usually provides, all we end up is a categorization full of assumptions. Then new requirements come along and software engineers are usually faced with two choices:
- Refactoring. Long, hard, time-consuming refactoring usually involving changing of interfaces, inheritance, and the core concept of the categorization to adapt to the new situation.
- Compromise. Fit into the current categorization (a.k.a. architecture) due to time pressure or lack of motivation. Taking the shortcut, so to say.
Although many people will confirm that refactoring is always the way to go, the fact of the matter is that compromises are part of our life. They are not only limited to inexperienced developers or companies with bad dev processes. Every good Java developer can give at least a couple examples of violation of the core OOP principles (talking about the traditional, classical OOP here) in the standard Java library. One of them:
java.util.Vector that extends
java.util.List. Therefore a Stack in the Java programming language supports all the functionality of a List, including adding an item in the middle of the collection. Java guru Joshua Bloch admits his team has failed to prefer the composition in this situation (Joshua Bloch, Effective Java). For me, this is a clear example of a compromise in the context of a programming language with strong types. Someone has taken the shortcut.
These kind of compromises usually stack together to create a mess out of a big codebase as new requirements come across.
Another huge disadvantage of using strongly-typed languages is that they usually discourage the usage of simple objects that are created out of thin air. Instead, custom types (classes, interfaces) are created even when all we need is working with a simple object.
Tooling is definitely better in the context of strong types. But I feel people has placed too much importance of tooling in software engineering to the point that we ignore something that should matter much more — the power of programming language alone.