| Quality test coverage. I'm a big fan of having integration tests which cover all the main scenarios which are relevant to your users. For example, one of my open source projects has about 120 integration test cases in total; these are enough to cover about 10K lines of code (not counting third-party dependencies); if I break any documented feature of the project, it's essentially guaranteed that many test cases will fail - They will catch the smallest issue or inconsistency; even things which are not being explicitly tested. Integrations tests are great at detecting symptoms. They don't always point exactly at what the problem is, but they are great at letting your know that there is a problem and then you can debug it. Integration tests are the best way to identify and fix issues in very complex systems. For example, when it comes to medicine and the human body; a thermometer reading is an example of a well-designed integration test case; if your temperature is high, then the doctor knows straight away that you have a problem which needs to be addressed. The doctor will probably not know exactly what the problem is immediately, but nonetheless, that simple thermometer reading (assertion) can allow them to detect the symptom of an essentially infinite range of possible underlying issues. One test case is not enough to cover all possible health problems, but the enormous amount of internal coverage that this single test case provides is unfathomable. We ought to try to come up with such test cases in our code. If you have good integration tests, it doesn't really matter what language you use because (in terms of identifying issues) everything else pales in comparison. Also it's important to note that integration testing doesn't necessarily mean end-to-end testing. You can still mock certain external components or engines in order to get more stability in your tests and you can still get that same kind of leverage. |
I agree that this does not replace integration tests or manual QA (dependencies can break and not every behavior can be checked). But as a lever for increasing productivity, a type system (even with the added complexity) is unbeatable.
Basically my point is that you aren’t actually catching errors at compile time, you’re catching them at build time. Your workflow might provide the same guarantees for the code that gets deployed to production, but writing that code is probably less enjoyable and you’re probably less productive than you could be.