24 comments

[ 3.0 ms ] story [ 62.8 ms ] thread
This is a really interesting point of view.
There is a similar dilema in the creative writing field: "I don't know the right way to say this idea" or "I have writer's block." The general advice from the workshops and masters is to write something, even if you know it's terrible, so you have a place to start and revise (what we would call refactoring). The theory is that revising words is much easier and can be helped along by someone else moreso than a blank page.

However, I've only seen it work in practice when you do go back and refactor. Too often the smelly code gets left behind, and that is why I usually ask others to think through code before writing.

> The theory is that revising words is much easier and can be helped along by someone else moreso than a blank page.

Amen. Nothing beats the instructive facility of running code and a usable UI.

> However, I've only seen it work in practice when you do go back and refactor. Too often the smelly code gets left behind, and that is why I usually ask others to think through code before writing.

Well, of course it won't work if you don't refactor. You get results analogous to writing with no revision. There's also the trick of knowing when you've revised enough -- amateurs stop before the pros.

In most of the code bases when 5+ people work on the same code, it's really hard to add some non-trivial functionality by writing it the proper way from the beginning. BTW I think sticking with the attitude "first specs, then code" is old school in most situations. Some very rough spec will be fine, but a lot of the things you haven't thought of, you encounter while actually playing with the app/widget, or stepping through the debugger (unless you've worked on a product for a very long time and know it inside out).

I find "write working code & then refactor" ridiculously easy when working with Git. Add some tiny new things one-by-one and commit early each time it is more or less stable (so you can revert quickly when you screw it later, instead of doing some crazy debugging). When it's done, squash the commits and refactor.

> When the code is working and you’re ready to move onto the next phase, then you can work on making the code pretty, readable, well-documented and tested. While this flies in the face of concepts like test-driven development...

I disagree that writing "shit code that still works" and TDD have to be mutually exclusive. In fact, if I'm going to go the "just get it done" route, I will almost (but not always) try to set up a few tests for it.

Because nothing sucks harder then going back to crap code and trying to decipher what it's doing. If it's crap code, then it's likely non-orthogonal code, which means changing it will likely break something...etc. etc. Tests help me do the refactoring later.

And, also, how do I know that my shit-code-just-works actually works? Sometimes, it's helpful to write a kind-of-shitty-test to at least verify that it kind of works, if not perfectly.

The person I was mentoring (that prompted this blog post) was having trouble getting ANY code down on the screen. Being able to write SOMETHING was the hurdle for this developer to overcome.

In most cases though, I agree with you.

I write bad code all the time, I'm writing terrible code right now. I check in good code.

Some people can visualize an entire program in their head and how it will interact. Not me. I hack at the core of the problem I'm trying to solve, until I've solved it. Then I reorganize the code into something that (hopefully) isn't crap. It seems inefficient, but it works for me.

From the sounds of other developers, I think much of the development community is moving towards this paradigm. Developers have been so inundated with needing to use patterns, designs and other forms that has lead to inefficiencies in designs that were well thought out, but do not apply, instead of inefficiencies from bad code.
"Nothing lives longer than temporary code."

Love this quote. I assumed it was an existing quote/idiom, but it looks to be original.

It's not original to me. Not sure where I heard it, but I know it's not mine.
Thus spake the master programmer: "Though a program be but three lines long, someday it will have to be maintained." -- Geoffrey James, "The Tao of Programming"
Isn't this just usual prototyping? I rarely consider best practices when trying to implement a non-trivial prototype because flow and creativity is broken when attempting to adhere to best practices at the same time (in my experience at least).

My current project contains some non-trivial code so there was a lot of prototyping. If I spent time ensuring this code was also well written, I doubt the problems would've been solved because half of my energy and concentration would be spent on scaffolding and not the actual building.

Having said that, I still find documenting the prototype as important as real code. When it comes to refactoring the code, it helps a lot.

While I agree with the underlying point, the title is an argument in semantics (and I would say misleading and wrong - it is never OK to push bad code).

It may be unelegant, it may lack any number of "best practices", but if your code solves a problem, doesn't break other things, and can be refactored effectively, it doesn't count as bad.

Write prototypes in a language that you don't use in production.

It allows you to get to grips with the problem and prevents you from just copy-and-pasting large chunks of the "i'll rewrite this later" code into production.

I tend to write my prototypes in Perl as I used to use it a lot in the past for hacking things together. It translates well both to Ruby on Rails (front end) and to C (with low level server magic).

That's an interesting tip. I'm going to try that.
This is a great suggestion. I've done this with PHP (the first language I learned) for work I'm doing in Python. It also helps sometimes when you have to translate code from one language to another to use an intermediary language.
I used to say that "temporary is the state before permanent". Unfortunately, the crappier the initial code base is, the crappier the production code will be, unless you have a lot of free time to rewrite everything, which about nobody has.
Having the time to eliminate your technical debt is just like having the money to make your interest payments. You can go into debt for leverage, just don't get caught in a cycle where it gets deeper.
This is why you test from the outside in: You first write an acceptance test where you just specify what goes in and what should come out. The error/failure messages you get when running this test drive your design and tell you which components you should write next.

The RSpec book is a pretty good introduction to this topic.

I don't think this post goes far enough. It's not only ok to write bad code, it's also ok to commit bad code, and to use such programs in anger. Most code is not actually going to be important. It's ok for that code to be crap. What's generally not ok is contaminating already existing high-quality components with quick kludges. Likewise it's not ok for programs that turn out to be important to remain in a crappy state, even if they started out as such.

I write dozens of shell pipelines every day. Some graduate to actually being saved in files and cleaned up a bit. Some of those start needing new features, and end up rewritten as quick and dirty perl scripts. A few of those see constant use in more and more circumstances and end up accumulating an ever larger number of features. Once that program grows large enough, it'll become obvious that it'll become unmaintainable at some point. And you can either evolve that code into a less crappy state when other changes are made, or you can rewrite it from scratch.

And the thing is, you can't really know in advance what kind of a program a given problem really warrants. Is it shell pipeline that'll only ever be run three times, or will it end up as a core component of somebody's workflow. Since you can't predict these things, you have to play the odds. If most programs end up as important tools, it'd be insane to risk the need of multiple rewrites. While if most programs end up just gathering dust somewhere, it'd be insane to polish them to perfection before committing. And even if "nothing lives longer than temporary code" is a nice soundbite, I don't actually believe that for a moment. I'd bet that most temporary code actually dies almost instantly. It's just some kind of observation bias.

I think this varies per person's development style. If the thing that's blocking is getting momentum, then sometimes writing tests and then code to meet those test is easier (for those of us who are more comfortable with documenting).

In the end it depends whether it's easier to just code or setup scaffolding (ie, TDD tests) to then write the code.