top | item 5765270

Too scared to write a line of code

219 points| whosbacon | 13 years ago |medium.com | reply

128 comments

order
[+] cheald|13 years ago|reply
This is really kind of the whole idea behind the "red, green, refactor" mantra. The point isn't to write perfect code right out of the gate, it's to write working code. Once the test is green, then you can refactor as necessary with the confidence that you aren't going to accidentally break things (as the test will backstop you).

Ironically, I think this is something that affects seasoned programmers more than new ones; the more you know about the trade, the more voices you have saying "no, that's wrong! Don't do that!", which can freeze progress. The way I combat it is by writing down pseudocode in my source code - literally just english, non-compiling pseudocode - which I then "refactor" into working code (thus the first "green" is "it parses"). By making step 0 the expression of the idea rather "writing the fist line of code", I can get right into the process rather than getting hung up on the "how".

[+] minikomi|13 years ago|reply
Also kind of related - programming by "wishful thinking". You first lay out the flow of the program as calls to high level functions which don't yet exist, and then go back and fill in the functions as you can. The first few SICP video lectures are all about this style, and I found the idea simple but very useful to avoid trying to hold too much of a problem in your head at once.
[+] stephengillie|13 years ago|reply
Sometimes I'm surprised at the lack of parallels between writing long-form articles and writing code. How many people draw a general outline of their program, what they want each part to do, and how the whole thing should be layed out? Who plans before they start coding it?
[+] mawhidby|13 years ago|reply
Recently (as in a crazy experience over the past week), I've been feeling the same way described by you, and by the OP; but I've scaled this up to a much higher abstracted level.

The only way I can really describe it is (in terms of what you just said): "red, green, refactor" your life, not just your job. Constantly question everything you're doing, and find out what you don't like about it. Find the root cause of that feeling, and figure out what you can do to fix it. Find and take the steps to complete that.

Edit: I will add though, that the trick is to do so without stopping progress, as you said.

[+] stevewillows|13 years ago|reply
I really like your common English approach to coding. As a designer I often do the same with a few key points that my client and I have discussed with some additional influences taped up around my desk.

Great comment!

[+] manmal|13 years ago|reply
Using a visual git client greatly helps with this approach, especially if you are coding in multiple files at once. Once you have "green" status, look over the changed lines in the git GUI and look for lack of patterns, code smells, etc. This separates the coder in you from the reviewer in you.
[+] steven2012|13 years ago|reply
I program the way that I imagine I would sculpt. If I had to sculpt a head, I wouldn't start by sculpting the perfect ear, and then sculpting the perfect eye, etc, because who knows what will happen. Maybe in the course of sculpting the eye, I need to move the ear, and then all that work goes out of the door.

Instead, I would sculpt the entire piece of art roughly, almost unrecognizably, and then target large parts of the sculpture. Details like ears and eyes might even never get done, depending on how the requirements change for the sculpture.

I always approach writing software the same way, so the first iteration is just a hardcoded piece of software that just shows me that it will actually work, and then I iterate and start smoothing out all of the sharp edges, of which there are a ton.

[+] andrewljohnson|13 years ago|reply
People interpret "just do it" as practical advice to write good code, but really it's practical advice to get code from people who won't otherwise write anything. Good code comes from both will and knowledge. When you don't have knowledge, will alone will do.

"Just do it" is the best advice for stuck people - you tell them to just start doing the thing, not worry about details, and eventually you'll do it right. This is good advice for a business guy who's pin-wheeling ideas in his head, a programmer with no product sense, or anyone else who wants but does not make.

When you know a little bit about what you want to build, and maybe even a bit about _how_ to build it - when you have some actual experience, skill, and will - then the right advice is typically to slow down and think it through.

[+] jlgreco|13 years ago|reply
I have a theory that the extent to which the "Balmer peak" actually exists is the extent to which it makes the "just do it" advice easier to follow.
[+] danso|13 years ago|reply
What keeps me from coding is not necessarily optimization (well, at least its connotation connected to speed), but maintainability...By that I mean the constant battle between, "Do I write the boilerplate I need to set this up right?" and "Let me just copy/paste/mock all of that". To me, this is a more sapping concern than optimized code, because taking too many shortcuts makes the project harder to maintain in its midstages...even worse, the shortcuts can cause sloppiness to cascade into the design of all the other dependent objects and interfaces.

Doing some nominal TDD has helped quite a bit for me, because it does require writing code, but not code that I have to worry too much about (and if it happens to be a pretty shitty, shortsighted test, I can delete it without it, theoretically, hampering the app) screwing me over. Sometimes this physical act is enough to get me past the code-writing block.

Also, if I actually do the testing right, it prevents regression, improves code quality, etc. etc.

[+] yarou|13 years ago|reply
I think it ultimately comes down to "choose the right tool for the job". TDD is just one tool that you have in your toolbox to accomplish whatever project you are working on. That doesn't mean that TDD is appropriate for every project.
[+] notacoward|13 years ago|reply
It's an oldie but a goodie. Fred Brooks:

"Plan to throw one away. You will anyway."

The mistake most people make is that they mix learning and doing in a single round. It's much better to write a prototype with a specific goal of wringing as much knowledge from it as you can without being distracted by scalability or maintainability minutiae. Then, and only then, will you be ready to write The Real Thing with all of the bells and whistles. In the end you'll probably save more time than if you kept going back and forth waterfall-style between learning and doing on the same codebase. You'll certainly end up with a better result.

P.S. That quote is from 1975. To quote another great mind (Santayana): those who do not know...

[+] rmason|13 years ago|reply
I remember when Facebook's front page code leaked. I am no PHP expert, but it was pretty ugly code. Probably today you couldn't get hired at Facebook if you wrote code like that.

http://techcrunch.com/2007/08/11/facebook-source-code-leaked...

I've heard many arguments where people will tell you, it won't scale. Or you're doing it all wrong by writing quick and dirty procedural code. Quite frankly scaling is about the nicest problem to have if you're a startup. Without traction your ugly code simply isn't going to matter. With traction you will gain funding and the ugly code problem quickly disappears like I assume it did at Facebook.

[+] daeken|13 years ago|reply
> Quite frankly scaling is about the nicest problem to have if you're a startup.

I'm certain I stole it from someone here on HN years ago (whoever it was, I owe you a beer!), but I've always called this a "Maserati Problem". That is, it's something I can think about while I'm driving down the road in my Maserati that I've purchased with money my startup has made already.

Scaling, in most cases, is a problem very much like "where am I going to store all this money that's pouring in?!" If you have it, you've already won.

[+] ww520|13 years ago|reply
Hindsight is always 20/20. It's difficult to judge a piece of code after the fact. The quality of the code, including maintainability, is a result of various trade-offs. We don't know what compromises the developer was making at the time. We don't know what was the highest priority at the time. May be they were short of money and working code however shitty produced at the shortest amount of time was the most important. Suck to be the guy doing the maintenance but if there's no working product, there would be no maintenance.

We programmers have a ugly tendency to judge less than stellar codes written by others. It made us feel superior to pick apart mistakes others have made. The fact is software development is a series of technical and non-technical trade-offs. I certainly had fell in the same trap before. These days I just appreciate they can ship a working product and get it off the ground.

[+] laumars|13 years ago|reply
I'm by no means a pure functional programmer, but I've found some of the mantra from that paradigm helps when building the early blocks of a program as it allows you to break down your application into core problems which can be tackled individually. Plus it allows your application to scale easier and makes it easier to rip out and replace code as you start to add complexity.

But of course, before starting any major project, the first thing any developer should do is map out at least a basic mental design of the program - even if that map isn't written in pseudo-code or in a form of a flow chart.

[+] chousuke|13 years ago|reply
The reason pure functional programming is good for "ripping out and replacing" code is because your code units necessarily have fewer interdependencies.

a pure functional language is really good for writing dumb code that "just works" in the sense that it does what you need it to do even if it isn't very pretty or fast, while at the same time keeping it reasonably maintainable because the code will not be rife with accidental complexity and assumptions about global state.

[+] dustingetz|13 years ago|reply
in other words, FP evolved to solve hard problems. You don't need to understand monads to model a cash register. You probably do need monads if you want to write a bug free distributed asynchronous system.
[+] ww520|13 years ago|reply
Be careful going overboard with pure functional programming. It could become one of the immobilizing factors the blog was talking about. Is the code pure FP? Can I get rid of the states? Am I doing correct FP? Should I write generic function for more abstraction? Am I doing DRY enough?
[+] agentultra|13 years ago|reply
I've missed coding with whimsy and curiosity. There was a time in my life when it wasn't all engineering. Characters from the Lord of the Rings trilogy made their way into tutorials and references to the Flying Circus were everywhere. Communities were a lot more accepting of newcomers and few people were so paranoid of bad, unmaintainable, spaghetti code. There was less of a focus on being a 10x developer and more on learning, having fun, and doing neat things.

I'm probably remembering things with rose-tinted glasses.

These days, even in open source -- the once proud land of eccentric hackers and the can-do spirit, there is such a negative focus on bad developers. I can certainly see how the OP could feel paralyzed when just trying to write a line of code.

In reality we're all at different points in our journey and have had different experiences along the way. There's always room to improve ourselves and it's important to listen to criticism so that we know where to start. But sometimes you just need to cut loose and let it all hang out. I started writing a library I call "Horton," for this very purpose: whimsy, pleasure, and most of all to avoid the engineer mindset!

In order gain insights and develop new ideas you have to find new hunches and investigate them. It's hard to develop hunches in a vacuum where your reality doesn't expand beyond your own self-made bubble. You've got to push once in a while... and people out there who complain so loudly about shitty developers? Get a life and help someone out. We're just making programs.

update: https://github.com/agentultra/Horton forgot the link...

[+] tikhonj|13 years ago|reply
I don't know--my impression is that most people go to far in the other direction. They write code with no thought at all for the future, and then shortly have nightmarish debugging sessions in spaghetti code that's impossible to extend or reuse. All in the name of a rather extreme "worse is better" philosophy.

I've certainly experienced this with some of the people I worked with recently. Anything you win in the short term gets lost several weeks--if not days--later when everything is a horrible mess. Don't even think about coming back to that code months later!

I've found that putting in a little bit of care for the future pays off even in the short term. Perhaps not on the scale of hours or days, but definitely weeks. Which is still pretty immediate.

However, how you do this is also very important. The handy rule I've been using is simple: simplicity. Improve your code by making it do less not more. If you can make code more maitainable or general by simplifying, do it. If it would require adding new concepts or mental overhead, reconsider. Try to reuse existing abstractions as much as possible.

This usually--but not always--means making your code more functional. Do you really need to write this operation in place? Do you really need to tie everything together with a bunch of implicit state changes? Probably not! This is not to say that you should never use state: just be sure to only use it when it fits well and makes sense. And be explicit about it.

The functional style (at least in languages like Haskell) also lends itself very well to reusing simple and general abstractions. The key idea here is that these abstractions are not about doing more: they're about doing less. More general code simply leaves you less room to make mistakes. If you're working with lists of numbers, there are all sorts of arithmetic mistakes you can make; if you manage to write the same code against lists of anything, the only mistakes possible will be in list handling.

Haskell just makes this more practical by providing abstractions with a great "power-to-weight ratio": ones that manage to be simple, general and yet still expressive. Code with functors or monoids or traversables is easier to write than more concrete code and yet still flexible enough for many useful tasks. As a bonus, it's also more reusable. For free.

So the key idea is reduction: all these abstractions work by stripping concrete types of power. A functor can do much less than a list. A monoid can do much less than a number. This lets you write nicer code without becoming an "architecture astronaut" because instead of adding structure, you're really taking it away.

I've found that programming like this really helps in maintaining and extending the code later on. But it doesn't slow me down when I'm writing either--I actually save time because I spend less getting time getting the code correct in the first place.

These days, I've started being able to make large changes or add complicated features and have them work on the first try. Not every time, but surprisingly often. Certainly much more often than before! In large part, I think this is because of writing the code with an eye towards simplicity.

So the important insight: take the simple route, not the easy route.

[+] gfosco|13 years ago|reply
How long have you been programming, and how many jobs have you had writing code in an actual business? Your profile says you're a student, so I'm curious how you can feel justified in writing such a thesis.

Personally, after 16 years in the industry, I do stress about my code as described in the article... Not about making it work, because that will happen regardless, but about how. It affects my ability to code, because "make it work" is far drowned out by "make it work the way your peers will accept it." Many times, that "acceptable" approach is something I find far less intuitive, and far less maintainable... Also, that approach has changed so many times over the years and with each different team, it's hard to keep track. I can build far more in a weekend, on my own terms, than I can build in a week by someone else's. I am not complaining... I actually like to learn those terms, and I enjoy it as long as people are constructive about it rather than condescending. It's trying to pre-determine those terms that is the block, for me.

[+] Zakharov|13 years ago|reply
If you write code too slowly, go faster. If you write code too quickly, go slower.

You can tell if you're too slow when you've spent two weeks thinking about the program and have neither working code nor a detailed design, or if you've created a detailed design only to realize upon implementation you'd gone about it entirely the wrong way.

You can tell if you're too fast if you find yourself spending more time cleaning up messes of poorly-thought-out spaghetti code.

[+] plorkyeran|13 years ago|reply
You're going to see a lot more code from people who err on the side of too little thought rather than too much though for a pretty obvious reason: people who are too scared to start coding... aren't writing any code for you to read later.

Also, while it's easy to identify code which has had too little thought put into it, it's often very difficult to identify code which has had too much thought put into it. The best solutions don't look like the result of lots of hard work, but rather make hard problems look like simple problems that would be trivial to solve.

[+] ay|13 years ago|reply
Great advice IMHO. One trick that I found useful for myself (good chunk of my code is "hobby" code that I myself revisit some months later, so not really "production" problem but nonetheless almost the real-world maintenance example):

- make the function fit in your head. Literally. Make it fit in one screen, if you can. If it does not, think how you can make it fit on the screen - everything, including variable definitions.

- avoid branching as much as possible unless it is the 'exit' branches. Avoiding branching sometimes can be done by tricks like passing function pointers. Do it - it makes code much more expressive and easier to read.

These two heuristics allow me to make the code much simpler to understand for $(me+6months). I saw the same patterns in real-world code at $work as well - using the above two principles makes the code dramatically easier to support later.

[+] garysweaver|13 years ago|reply
> Improve your code by making it do less not more.

Exactly. The problem is that code can tend to be either crap (spaghetti) or too much code (too many methods and layers of extraction). So, I'd go one step further: code should do less, not more, and should be clear and understandable, without making unnecessary sacrifices. What is clear to me isn't clear to everyone else, but striving for clarity and simplicity isn't a bad thing.

[+] johnvschmitt|13 years ago|reply
This is good advice when you're new to a stack.

But, if you know better, do better please.

After writing tons of code, I've come to realize that 80% of the time, it takes about the same time to do it "right" (scalable/secure/modular) as it does to do it "OK".

Ex: Are plaintext passwords any faster to build/code than encrypted+salt passwords? Nope.

And, it's fine to intend to refactor later. But, too many organizations have no tolerance later to let engineers do things that don't have visible results. I've suffered through a stint of handling "legacy code" like that & it was painful & demoralizing.

[+] KVFinn|13 years ago|reply
In general writing a trick to get around this is to do a 'vomit draft' -- intentionally write something bad.

In part, this is simply to get past the analysis paralysis or creative block and get going on something. But also because it can sometimes be easier to make something good when you have a clear bad example in front of you to contrast it with.

[+] dvt|13 years ago|reply
(From a couple of years ago: http://dvt.name/2010/ward-cunningham-on-simplicity/)

Occasionally, I find myself “stuck” when writing code – the coder’s “writer’s block” I guess. This quote by Ward Cunningham is both inspiring and truthful:

-- Once we had written it, we could look at it. And we’d say, “Oh yeah, now we know what’s going on,” because the mere act of writing it organized our thoughts. Maybe it worked. Maybe it didn’t. Maybe we had to code some more. But we had been blocked from making progress, and now we weren’t. We had been thinking about too much at once, trying to achieve too complicated a goal, trying to code it too well. Maybe we had been trying to impress our friends with our knowledge of computer science, whatever. But we decided to try whatever is most simple: to write an if statement, return a constant, use a linear search. We would just write it and see it work. We knew that once it worked, we’d be in a better position to think of what we really wanted. --

Next time you’re stuck, just write the simplest thing that could possibly work!

-- So when I asked, “What’s the simplest thing that could possibly work,” I wasn’t even sure. I wasn’t asking, “What do you know would work?” I was asking, “What’s possible? What is the simplest thing we could say in code, so that we’ll be talking about something that’s on the screen, instead of something that’s ill-formed in our mind.” I was saying, “Once we get something on the screen, we can look at it. If it needs to be more we can make it more. Our problem is we’ve got nothing.” --

Source: http://www.artima.com/intv/simplest.html

[+] hawkharris|13 years ago|reply
I write news articles in addition to code, and a fellow writer gave me a simple piece of advice that I think applies to both disciplines. It has to do with planning -- but not over-thinking -- your work.

Before you start writing, sit down with a friend in a bar and explain to him exactly what you're trying to say and do. If you can't communicate your idea in a concise, conversational way, then you're not ready to start writing.

Applied to code, I suppose this is similar to the concept of Rubber Ducky Debugging: http://en.wikipedia.org/wiki/Rubber_duck_debugging

[+] dustingetz|13 years ago|reply
you need to learn calculus if you want to model relativity. you only need fifth grade math to model a cash register.

if your team builds apps with 500k LOC, you need to be constantly focused on abstracting and factoring out patterns or you will drown in your defect rate before the project even ships. If your team builds medical device software, if you aren't doing this, you'll get someone killed.

If you're building a personal blog, your time is better spent by pasting jquery snippets.

There is a spectrum here and most projects fall somewhere in between.

[+] dsirijus|13 years ago|reply
Of course. And to add an example of the other part of spectrum (opposed to medical device software) - bunch of simple 2d game prototypes. There's people who do such jobs, and many similar, who'd be a fool not just "push the buttons" all the time, with basically just a quick forethought related to structurization and code quality.
[+] jstanley|13 years ago|reply
I can identify with this. Some of the best work I've ever done has been some of the worst code, just because of the paradigm shift involved: "get shit done" instead of "write nice code".
[+] jamieb|13 years ago|reply
I understand completely.

"And all that is fantastically interesting, but completely beside the point. I just fell into the classic programmer trap of exploring and learning about (what I find) fantastically interesting things that will address all sorts of amazingly complex situations, but which the learning of said things resulted in absolutely nothing of tangible value being created."

http://www.jamiebriant.com/blog/2013/1/fireworks.html

[+] systematical|13 years ago|reply
I disagree. Too simplistic. It's my job as a developer to try and think of all the ways my project can fail and bog down before hand (within reason on the performance). I've got a list of items I am actually working on and will post it above my desk when done:

1. Let me think about this some more before giving you an answer.

2. I will use my debugger.

3. I will create a new branch for this task.

4. I have commented my code and commented it well.

5. I have accounted for fail scenarios.

6. I have considered multiple solutions to this task.

7. I have not begun hacking away immediately at a task.

8. I have attempted exploiting my code.

[+] shurcooL|13 years ago|reply
Here's what I came to after 10 years.

1. Write code fast.

2. Refactor when necessary (either because you got to a dead end and can't keep writing code fast anymore, or because you're done and want to move on to another task).

3. Goto 1.

[+] c0mpute|13 years ago|reply
The problem with this approach, while great for you, the developer it is a nightmare for the future team.

I am now working at a startup where the early devs have left. They did precisely what was needed at that point - build quickly, ensure it works for the 3/4 customers we had back then and document it as well. Fast forward a year later, that same code is no longer extensible for many clients. Its rigid and tightly coupled and rewriting some parts means, we have to pretty much rewrite a whole lot more than we bargained for. We will have to burn through a lot of cash to just build a whole new product based on some of the scalability and maintenance learning we now have from a year of learning and growing to client# 100.

The point is, there is always going to be "this is the right thing right now, its what our customers want" and then something else for the future.

I have seen that people who have worked for a while and in many startups do the smart thing (as expected) and while follow "build for now" they also do the sensible thing of investing for the future (from experience).

[+] munger|13 years ago|reply
I can relate to this. There is a real risk in freezing up when you attempt for your first writing of code to be the fully architected solution.

I would still go one step further and at least apply the strategy pattern/polymorphism when you have a large if/else/case block going beyond 2 or 3 conditions in a controller or model or something. (assuming MVC web apps)

The fact is you can write out some ugly code directly in a controller where it might get some code working fast and prevent the freeze up of your output, but once working you should immediately move it to the appropriate place like a model or library, and apply a basic OO strategy pattern (perhaps not with factory/interfaces etc until needed later) where appropriate to cut down on spaghetti/nested conditionals.

This really does not take much more time than stopping at working code that is ugly, and is a good middle ground where it is not painful to return to later.

There is still no good excuse for "taking a dump in the corner" of your code base just to get a marginal gain in output.