Why is there so much bad code? Developers offer myriad excuses:
-
We just have to ship (i.e. we don’t have enough time to do it right).
-
We disagree as to what “bad” is.
-
We know the code is not clean but we don’t think that it’s a real problem (i.e. we don’t care).
-
The existing design makes it tough to do the right thing.
-
We don’t have enough tests to clean the code up safely (“if it ain’t broke don’t fix it”).
Missing from that list is inadequate education. Most programmers learn from looking at other peoples’ code–whether in delivered systems, open source, internet examples, or book examples. A quick probe of the beginner’s forum at JavaRanch demonstrates how horribly-constructed novice programs can get. Many novices simply won’t know better–they copy working code snippets from elsewhere and modify it to their needs.
Let’s shift at least part of the blame where it belongs–to the author of the bad code that the novice copied. Most beginning programming language tutorials and books don’t discuss or teach proper design. Examples frequently demonstrate rampant duplication, intertwining of console output with business logic, clever constructs, and cryptic naming.
Even authors who should know better present examples with severe design flaws. They could at least caveat them (“cleaning up the obvious duplication in this example is an exercise left to the reader”). The better solution would be to find a way to code it properly and not show the bad code at all.
I’ve been working through examples in a couple Erlang books. Here’s a paraphrasing of one unfortunate theme presented in both:
loop() ->
receive
{Client, "smelt"} ->
Client ! "a small silvery fish",
loop();
{Client, "agile"} ->
Client ! "lithe",
loop();
{Client, "lean"} ->
Client ! "trim",
loop();
{Client, _} ->
Client ! "no definition found",
loop()
end.
You don’t have to know Erlang to see the obvious duplication in this short example. Sure, it’s just an example, designed to show a construct, don’t worry about the design, yadda yadda. Yet it’s at the heart of why most of our code sucks. I know little about Erlang so far, but it sure makes a lot more sense to separate handling a server request from domain logic:
loop() ->
receive
{Client, Word} ->
Client ! definition(Word),
loop()
end.
(If we’ve gotten far enough to learn about spawning processes, we’ve certainly learned how to build a clean definition
function.) By presenting it properly, the duplication gets eliminated, and the example may more readily suggest that the server loop is a potentially reusable construct, or at least a common idiom.
By presenting it poorly, I can bet you a large amount of Erlang code out there is similarly difficult.
Why allow beginners to learn poor coding practice, then have to re-train them?
Comments
Jon Eaves May 3, 2012 at 04:14pm
As an author of a couple of books, I’d like to say a little bit about code examples. My first book was really written well, it tried to promote the ideas of good coding, good design and the examples were “well considered”. The book was not received well by the audience, it was considered too complicated, and too confusing. The second book I took a far more “simplistic” view of how to produce code samples and the book was much better received.
From that experience it’s clear to me that a very large percentage of people using these sorts of resources are not looking for code examples that are well designed, or well considered. They’re looking for code examples they can blindly cut and paste into their “solution” with minimal thinking and without needing to understand anything of good design.
This doesn’t excuse “bad design” – but many times the authors of code are trying to show a specific point, with simple (simplistic?) code and many times that may make it unsuitable for specific re-use. Maybe code under those circumstances should be compilable/parseable to prevent inadvertent re-use.
Jeff Langr May 3, 2012 at 04:41pm
Thanks for the comment, Jon!
“They’re looking for code examples they can blindly cut and paste”
Exactly. I don’t think there’s a way to prevent people from pasting in or typing in examples they see. So as long as they are bad, novices (or those who don’t care) will end up creating bad code.
I understand your point, but accepting it also means that we are responsible for a lot of the bad code that’s out there. Still, I think it’s possible–though not necessarily easy–to present better code that doesn’t confuse. One tactic is to present the bad code and then demonstrate how the code should be cleaned up, though that can bloat things (it’s what Agile Java does).
Another is at least declare that the code is not ideal. I think there’s a responsibility–the novice might be a long way off from ever imagining that the code samples presented are not good. If I present a bad example, I try to always annotate the code directly–so at least if they copy/paste, either the comment remains or they have to consciously remove it:
void badMethodName() { // don't name your functions this way!
...
Stephan May 4, 2012 at 12:11am
I find improving existing code design to be hard (see Uncle Bob Martins ‘Clean Code’ material). But then writing code in a clean and well designed way is (to me) even harder. One of the problems seems to be that now and then the design needs to change (due to new requirements), so not only do you need a good code design that makes sense at the time of writing, it also needs to be easy enough to change.
Of course this is no excuse to writing bad code either. Another severe issue with bad code is that it’s hard to test: A method with a loop inside an if/else containing another two nested if blocks and handles exceptions? I’d be very surprised if someone would get that right and thoroughly tested. The seeped problem with this case is that there probably are no tests to support improving such code—making it even harder to improve the overall design without breaking anything.