I’ve come to see the uncertainty of untyped and interpreted languages as something of a curse. In a strongly typed, compiled language, you know ahead of time that the code is at least trying to do what you want it to; you know you didn’t typo any variable, function, method, or class names. You know you didn’t misuse or misunderstand a function, passing one type when another is required. Sanitizing inputs and returns is a matter of checking bounds, not types. Type-safe comparison is a non-issue.
To me, that’s a nightmare. I miss knowing. I miss that little bit of certainty and stability. With an untyped interpreted language, you may never be 100% certain that you’ve not made a silly but fatal mistake somewhere that your tests just didn’t happen to catch.
These are languages of convenience: easy to learn, quick to implement small tasks, ubiquitous. But they just aren’t professional-grade equipment.
Developing software is both an art and a science. I make an effort every day not to just be a coder, but to be a code poet. That’s hard to do on the unsure footing of a dynamic language. I won’t argue that these languages let you do some neat tricks; on the other hand, I also won’t discuss the performance issues. My concern is purely quality.
Is it possible to write quality code in a dynamic language? Absolutely. Unfortunately, it’s harder, and far more rare – not just because it’s challenging. It’s mainly temptation. Why would the language offer global variables if you weren’t supposed to use them? Why have dynamic typing at all if you aren’t going to have variables and function return values that could have various types depending on the context? Even with the best intentions, you can commit these Crimea against code accidentally, without even knowing it until you finally track down that pesky bug 6 months down the road.
Using (and abusing) these sorts of language features makes for messy, sloppy, confusing, unreadable code that can be an extraordinary challenge to debug. Add to that the fact IDEs are severely handicapped with these languages, unable to offer much – if any – information on variables and functions, and unable to detect even the simplest of errors. That’s because variable types and associated errors only exist at runtime; and while an IDE can rapidly attempt to compile a source file and use that to detect errors, it can’t possibly execute every possible code path in order to determine what type(s) a variable might contain, or function might return.