A few interesting points from a recent post from Steve Vinoski...
On the dynamic language front, the worst code I have seen in my career has always, always, always been in compiled imperative languages, most often Java and C++.
This probably depends on the developers and their culture at least as much as the languages themselves. Here's my observations based on having worked directly with handfuls of programmers with hundreds of thousands of lines of Common Lisp, Smalltalk, Pascal (and Modula-like extensions), C, C++, and Java; tens of thousands of lines of Scheme; and thousands of lines of Python and Ruby.
How should we measure "worst"? I'm not sure. I've encountered large amounts of bad code in each of these languages. The bad code I see in each of these typically has these characteristics:
- Hardly tested
- Hardly factored - "run on" code that bleeds implementation details all over
- Poorly named - "unreadable" code that does not communicate its purpose
- Overly coded - way more code than is necessary to do its job
The worst of these, in each of these languages, has significantly lowered the rate of change, often down to nearly unchangeable.
All else being equal, I'd rather work with good or bad code in a dynamic language. The rate of change will almost always be faster. (But don't ask me to work with bad code: I am way past any level of tolerance to work with bad code. Don't even show it to me, I am no longer amused. Urp.)
I would much rather let an average developer loose with a dynamic language... they’ll fail way faster and thus allow much more time for recovery. The fact that dynamic language programs are usually smaller than their compiled counterparts means that they’re easier to read and review, and statistically, they’re likely to have fewer bugs.
Yeah, I would not disagree, as I said, all else being equal. I realize Steve's point is about languages and not the full spectrum of becoming a better programmer. However, my silver bullet would be having them pair with someone who can help improve tests (which in itself is learning to fail way faster), factoring, naming, and doing the simplest things first would help more than switching languages.
Along the way, sure, help them pick up a better language or two. Everything else just gets that much easier.
Furthermore, counting on the static language compiler to save you is simply wishful thinking. To paraphrase Tim Ewald from a conversation he and I had during lunch a week or so ago, compilation really amounts to just another unit test.
Oh, god. I am sure to get more comments from the static type checked folks. I will ignore you!!!
"Compilation amounts to a unit test" - I not sure about the point. Maybe this means that static checks are essentially redundant in well-tested code. Here's a twist on the unit testing thing I wrote some time ago... (Test-first is a command line interpreter for non-interactive languages)...
Test-driven design tools are "command line interpreters" for Java, C#, and other "non-interactive" programming languages.
Programmers using Lisp, Smalltalk, APL, and other "interpreted" languages have been doing test-driven design for decades. The pattern here is to type little snippets of code for an idea, get immediate feedback, then gradually incorporate those snippets into a whole program.
Lisp has its read-eval-print loop. So does Python. Smalltalk traditionally has "workspaces" which are text editors that also evaluate highlighted code.
In either case, the end of a programming episode results in a transcript of code that is then allocated to two destinations... some of the code in the transcript are the objects or functions that go into the program itself. The rest of the code is edited into regression tests.
Looking at a test-driven incremental programming session using junit or nunit you see the very same result... the shell is the command line and xUnit is the "interpreter". You get a small idea, test it, and repeat.
The tests themselves are the "transcript" of the session, and are also the preserved regression tests.