
Dynamic Languages are Unmaintainable - mpweiher
http://williamedwardscoder.tumblr.com/post/54327549368/dynamic-languages-are-unmaintainable-and-unit-testing
======
JunkDNA
One thing I've only come to appreciate during my professional development is
that there are huge differences in the kinds of software people write and
what's good in one scenario isn't necessarily good in another. A lot of people
around here fall into the trap (myself included) of thinking that web
programming is the entire universe of programming. That's not so, and the
difference matters greatly.

If you're going to write a physics simulation, you're really going to benefit
from some language features over others. I would argue that you especially
benefit from static types because you control a lot of your stack and type
checking can make your code very robust. On the other hand, if you're going to
write a webapp, think about what you're doing: essentially slinging text
around and taking random inpust from users, casting all of it to actual types,
and shoving it into a data store (casting it again sometimes). What is the
type system providing you in that scenario? Everything is cast at runtime
_somewhere_.

~~~
gnuvince
> What is the type system providing you in that scenario?

Though the data flowing in and out of a web application is usually in string
form, that doesn't mean that it needs to be treated as such in your
application. You'll convert the string to a richer, more specific datatype and
the static type system will help you correctly manipulate the data.

A nice blog post on the subject is [1] where the author explains how you can
represent different kinds of strings (raw strings, SQL strings, etc.) using
the type system.

[1] [http://blog.moertel.com/posts/2006-10-18-a-type-based-
soluti...](http://blog.moertel.com/posts/2006-10-18-a-type-based-solution-to-
the-strings-problem.html)

~~~
JunkDNA
Thanks for that post, I think the point I was making was slightly different.
I'm arguing that with web programming you lack the end to end control you
might have in a closed system. You depend on all sorts of input from users,
your data is stored in a database or nosql store that is outside your
program's (and compiler's) complete control. It can change underneath you at
any time. The argument that static typing can help you know that your code is
correct is weakened in this environment. Internally you of course construct
your code in a way that uses types and can verify that you are internally
consistent. However, I'm saying that at a fundamental level, in a data-driven
web app, the system as a whole is only marginally improved by this because
your whole universe starts out with a bunch of typecast operations as the
gateway to your codebase. These always are happening at runtime and are one
"ALTER TABLE" (to pick just one example) away from failing spectacularly in
production.

I'm not arguing one way or another that static typing is/is not good in this
scenario. I'm just pointing out that the foundation of the system as a whole
is not as solid as other programming scenarios where you have much more
control.

------
tehwalrus
Having written a lot of scientific code in Python, I agree (partly).

The lack of static analysis is extremely irritating when you've been running a
long old calculation for 20 minutes and it chokes on a misspelled variable
name. You can often catch these with PyDev/eclipse, but not always.

I try to rewrite any code which isn't fast enough in C (or C++, using a few of
its "features" as possible) and wrap it up as a Python module; this gives me
the best of both worlds: the speed and type checking of C/C++, with the
convenience of calling the code from a quick script or an IPython shell.

This approach is much easier for scientific code than for web apps, though,
which do actually _need_ dicts and string parsing.

[[One web app option might be to cython as much of your pure python code as
possible (including cdef'ing many of your local variables) as this will
dramatically help typo checking - I don't know if this is particularly
scalable though.]]

~~~
est
> when you've been running a long old calculation for 20 minutes and it chokes
> on a misspelled variable name.

If you are writing C++, you can't hot edit and continue without re-
compilation. With a scripting language and proper flow control, you can edit
upon exception and resume.

~~~
viraptor
> you can't hot edit and continue without re-compilation

But with recompilation you can. watcom allowed stepping back during debugging
as long as the operation was possible to revert. From there patching up the
code to include a new version of the function shouldn't be that hard. Not sure
if anyone's actually done it, but it's certainly not impossible.

Ksplice works similar way if I'm not mistaken to do reboot-less kernel
upgrades. (patching the functions part that is, not recovering the code that's
already running them)

~~~
est
Ksplice is basically a binary (in ELF format) swap in kernel memory space.

What happens if your binary is faulty?

------
m0th87
I use dynamically typed languages maybe 90% of the time, and completely agree.
But here's the thing: for most projects - including ones in production - it
doesn't matter because the app won't be large enough to not completely fit in
a mental model.

If the app starts getting too large, then by all means rewrite it in a
statically typed language. If you're getting to that point, a rewrite is
probably warranted anyways; regardless of the language you choose, first
versions are usually awkwardly structured and difficult to maintain anyways
since features evolve faster than architecture.

~~~
shangaslammi
I would also argue that you can get the benefits of a static type system with
hardly any cost. Usually when people talk about dynamic vs static typing they
imagine something like Ruby or Python vs Java, but there's no reason why you
can't have a high-level, expressive language with static types.

All you need is a sophisticated type-system and smart type-inference that
works everywhere (not just locally) and you'll be able to be just as
productive as with today's popular dynamic languages but get all the benefits
of static verification.

~~~
gnuvince
OCaml, F#, Haskell all come to mind. Regarding total type inference, I am not
sure though that any of those can do completely away without some manual type
annotations. I still like to declare type annotations (especially in Haskell,
the syntax in ML is a bit too ugly) for top-level functions.

~~~
gizmo686
I can only speak about Haskell here, but how often do you need to provide a
type annotation. I have never been in a situation (outside of the interactive
prompt) where the compiler failed to infer the type. I do provide many type
annotations for the sake of readability, but as far as I am aware the only
effect these actually have on the code is providing additional restrictions on
where a function can be used (that is to say, intentionally making the type
signature less generic than it could be).

------
PaulHoule
The argument between dynamic and static languages has gone one for years and
there's been no conclusive answer that one is better than the other.

People have written 500kloc C++ systems and they've written 500kloc of TCL.

~~~
mpweiher
Submitter here: Yes, and some of the claims are simply factually incorrect:
the original work on refactoring OO programs was done in the context of
Smalltalk, the code navigation we now have in IDEs was taken from Smalltalk
(in fact, Eclipse started out as Visual Age Smalltalk) etc.

So I find the post "debatable"...

~~~
willvarfar
(blog author)

Well I thought refactoring went all the way back to Forth.

I'm sure that with some language xyz it worked out very well for some project
abc and that obviously disproves my whole point and you can move on...

I found writing large projects in Python - a language I was very familiar
with, expert even - to become very difficult to extend aka maintain after a
long time. My post should be seen in the bigger picture of the post I was
following up about "Scaling My Server", and others I have written.

~~~
mhd
Isn't "some language xyz worked out very well..." and "I found writing large
projects in Python..." basically the same (anecdotal) argument?

~~~
willvarfar
Yes, but its a subjective post. If someone wants to say or blog "I use xyz and
I can successfully maintain and extend large projects and it hasn't collapsed
under a cognitive burden and too-specific unit testing" that'd be a cool
counter data point.

~~~
mhd
Advocacy is cool now? Statistically you could come up with almost any
methodology, sane or not, and find one project using it, unless you're going
for outright cruel jokes like Befunge, INTERCAL or ITIL...

------
lhnz
>> And here is that insidious problem; all your unit tests have frozen the
interaction of your components. They have copied the API of your components
and distributed it all over your test code base.

This is what I hate about the unit-test everything approach. There seems to be
a class of developers recently that believe robustness is more important than
anything else.

Often speed bumps in changing API contracts is a higher price to pay [0] than
the kind of bugs which unit testing uncovers.

There are lots of things which are good in small quantities and very bad in
large quantities [1].

Better to be able to iterate quickly, spot a few bugs with sparse unit tests
and completely rewrite your _small_ components if they no longer fit the bill.
There is a tendency (perhaps with those too familiar with large corporates) to
try to de-risk with more and more robustness tests. With all of this
robustness monolithic software flourishes, and you eventually lose the ability
to iterate quickly; the ability to destroy and start anew.

[0] Form does not follow function, form is interdependent with function. Make
the functionality robust and you make the form rigid! How will you find
product-market fit when you are unable to move?!

[1]
[http://en.wikipedia.org/wiki/Hormesis](http://en.wikipedia.org/wiki/Hormesis)

~~~
DanWaterworth
It's better to do the wrong thing quickly.

~~~
angersock
Eh, you'll have an annoying time putting a man on the moon with that attitude.
But hey, your twitters will hockeystick better!

It all depends on your problem domain--the cost of failure for certain things
(pacemakers, telco software, car engine ECU code, and so on) may be much, much
greater than the cost of being super retentive in your development.

~~~
lhnz
Yes, it depends on the domain but outside a few industries (and low-level
code) software isn't a life-or-death matter.

Infrastructure is a different kettle of fish, while it might sometimes be a
life-or-death matter, generally it's just safer to seek pure robustness here:
monopolies with government connections do not need to fear losing
competitiveness since they can (a) manipulate the rules of the market, and (b)
bind market players with contracts.

I don't like your insinuation that I'm just talking about cool hockey-stick
startups; in the domains in which non life-or-death, free-market business and
software colide, losing competitiveness/product-market fit is the central
failure mode. And you absolutely _cannot_ become robust against the market.
You must either (1) become faster than the market, or you must (2) control the
market.

~~~
apalmer
True indeed but the initial comment of 'It's better to do the wrong thing
quickly.' was stated without nuance or caveat so the reply rightly pointed out
that this statement is true in certain domains and completely false in others.

------
manfredzab
First of all, there are no silver bullets and no overall "better" languages.
It depends on the situation, and a good software _engineer_ should be able to
reason about the trade-offs of using statically/dynamically typed languages in
each situation.

In this case, it is about shifting the costs to different stages of the
projects lifecycle. Statically typed languages require more mental effort
while developing, and, by definition, catch more bugs early in the process
(compiler has more information to play with, and the programmer is required to
be more thorough). Pushing it even further, formal proof systems require
exponentially more effort at development time, and, in some cases, rewards it
with the proof of software correctness (i.e. no bug-fixing costs at
maintenance stage). On the other end of spectrum, dynamically typed languages
allow for much faster development (which can be mission critical), but a rapid
release comes with a price of long-term maintenance difficulties.

It's up to the developer to choose which approach is the most appropriate for
the situation (amongst the myriad of other trade-offs, like performance,
scalability, user requirements, existing infrastructure and so on).

------
kyllo
The thing about dynamic languages is that they have a type system too, but
it's called "inheritance." When literally everything in your language is an
object that ultimately inherits from Object, as it is in Ruby or Python or
Javascript, it doesn't matter what "type" each of your objects is, it just
matters that your objects define the fields you want to access and respond to
the methods you want to call on them (And Ruby even lets you fail up the
object tree with method_missing). Fixating on types is focusing on the wrong
thing. You need to understand the object inheritance tree, not the types.

Whether or not that makes your code unmaintainable is up to you, it helps if
you keep your classes small and give each of them a well-defined and
documented purpose. But that's just good coding practice in general.

Also, static code analysis is nice, but it can't determine everything about
your code; Turing proved that in 1936.

Meanwhile, by skipping the the compiler and linker, you can deploy
enhancements and bug fixes to a running program without shutting it down and
restarting it. For a rather long but enjoyable read on why this is important
for long-lasting software, see: [http://steve-
yegge.blogspot.com/2007/01/pinocchio-problem.ht...](http://steve-
yegge.blogspot.com/2007/01/pinocchio-problem.html)

------
jccooper
Are so many bugs really caused by type problems? I run into type based errors
in Python almost never. It is not hard to keep track of dynamic types, and
takes the same effort (or less) than static typed programming. In both cases,
you have to know what type you are working with, right?

If you have this problem you have probably been trained to be sloppy by
compilers.

~~~
prewett
I often get burned by doing something like `key = "player" \+ playerId`.
Unfortunately, playerId is an int, and I forget that even though most other
languages besides C/C++ will convert that for me, Python will not, so I get a
runtime exception. Same thing with `print "no space after this" \+ number`,
because I get used to `print "space after this", number` and then sometimes I
don't want the space and inevitably I forget to do `str(number)`.

Mind you, this is the sort of thing you usually discover quickly, but it's
also the sort of thing that most other languages will catch at compile time.

------
programminggeek
Hey, you might not know this, but if you use a dynamic language with types you
can actually use them to your benefit!

I've wrote a system in ruby that does clean type checking when I want it to
and it cleans up the code and tests a lot.

Just because you're not taking full advantage of your language tools doesn't
mean that "dynamic languages are unmaintainable"

~~~
gnuvince
How can you implement a static typing system in a language that is dynamically
typed?

~~~
programminggeek
There is a difference between static typing and strong typing. Ruby is a
dynamic language that is strongly typed. This will explain it better than I
can:

[http://www.rubyfleebie.com/ruby-is-dynamically-and-
strongly-...](http://www.rubyfleebie.com/ruby-is-dynamically-and-strongly-
typed/)

------
lbarrow
I use a strongly, statically typed functional language. Because my code has no
mutable state, the type system of the program proves the program correct. So I
don't have to write any tests at all and maintenance is easy.

~~~
MichaelSalib
Which type system proves program correctness? A good type system can guarantee
that your program doesn't suffer from certain classes of errors but that's
very very far from correctness. Unless you only write programs that don't have
any goal at all besides being memory safe: Behold MemSafe 2.9: it doesn't do
anything at all, and most importantly, it doesn't do it in a memory safe way!

~~~
DanWaterworth
All safe type systems provide proofs that implementations fulfil
specifications, but most type systems aren't expressive enough to create
interesting specifications. The notable exceptions being type systems in
dependently typed languages.

------
michaelfeathers
I'm thinking about writing about blog called 'Dogs Are Green.' I don't have
any evidence, but it's my opinion.

------
michaelochurch
Bad programming practices (which usually come from the business, in the form
of unreasonable deadlines, untrained or unskilled programmers given high
positions, and politically mandatory use of terrible legacy systems) are
unmaintainable. I've seen static typing fall down just as hard when the
developers weren't good.

Static typing is right for some problems, but the idea that dynamic languages
are inherently unmaintainable in some way that most static languages aren't is
one that I find incorrect.

~~~
seivan
Compiler does help with a lot of things that you'd require unit tests for with
a dynamic language.

Example: Runtime wouldn't be able to give you a protocol between how two
classes would communicate. Say your service object that handles
authentication. You would have to write unit tests so the two classes involved
would know how to respond to each other.

