Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> When I try to build something new, I find myself instantly criticizing my technique, to the point of paralysis. This function is hard to test; this object's dependencies need to be injected rather than initialized internally; that module needs an integration test; and so on and so forth. Even when writing spike or proof of concept code, I find myself revisiting the same lines over and over again, looking for the best, most natural expression of the ideas it contains -- obsessing, like Catt said, over my own construct, rather than on the thing my code does.

I used to be like this for the first few years of programming. And I knew a guy at my last job who was exactly like this. The thing that helped me, when I felt the indecision paralysis come on, is to just do something and accept that it may be wrong. You often don't know the best decision in the first place because you lack experience. Doing it the right way by accident, or making the mistake of picking the wrong way helps get you that experience. Be deliberate about always doing something, and over time the paralysis will get less and less as you gain more experience.



I'm naturally a perfectionist, so I fall into this trap often, though I've gotten better over time. I rely on a few simple tricks (ugh, the buzzfeed headline practically writes itself):

1. If I'm stuck and can't decide which path to take, it's usually because I don't have enough data to tell which path is better. The fastest way to get that data is to pick one path arbitrarily and start walking down it. Pretty soon, it will be become obvious if it's the wrong path.

2. One of my favorite things about code is that it's infinitely malleable. Especially thanks to the magic of Git, I can undo any change. Nothing is permanent which means no decision is carved in stone. Deciding isn't deciding what to do forever, it's deciding what to do for now.

3. Sometimes I get stuck trying to do a depth-first traversal of the fractal tree of all possible implications of the program. Going depth-first down every single edge case and rathole is not an efficient way to get something up and running.

You really want to go breadth-first or best-first instead. To do that, you need to leave markers in your code of which branches remain to be explored. Those are TODO comments. So instead of falling into a rathole and not making progress on the thing that's at the forefront of my mind right now, I just leave a TODO, "Figure out what to do here if the ___ doesn't ___."

TODOs are great because they help my current velocity by not getting sidetracked and my future velocity by giving me something to pick up on when I finish something else. I pretty frequently search my code for TODO and grab one that sounds fun.


I have recently started to just commit to one path as well. It reminds me a lot of writing papers for my classes when I was in school. Staring at a blank page is hard and staring a blank IDE can be even harder. I find that just starting with the smallest thing you know is your first step, be it a simple API call or even just spitting out "Hello World", really helps and after you start you can keep iterating on that base. Over coming the static friction seems to be a lot harder than keeping momentum.


Thank you for the reminder - you've made me realise I've been trying to implement a whole system at once when I really should just do the proof of concept and go from there (the only problem with that is that prototypes have this funny way of making it into production when you least expect it)


I frequently use TODOs and I don't get crazy if I don't go back and do all the TODOs. Some seem important at the time and become less so with age. Others become more important.


I feel like this leads down a dangerous path of no signal-to-noise ratio and destroys your hope of finding relevant TODOs. Better to, as part of revisiting them, rewrite the comment to explain the de facto behaviour for yourself (and others) in future. If you're not going to do them, anyhow.


You make good points. I don't use them so much as to bother me. I code for a team size of one (me) so that gives me liberties that I might not take elsewhere.


No. It has to be clean, correct, and come with full unit test coverage the first time. You have until the next sprint to implement it. Your workspace is a Macintosh at a long desk where you'll be working shoulder to shoulder with other devs working on other projects. If you can't manage even that, you're just not a good fit for our company; we expected more of an engineer of your experience level.

Based on a true story.


Don't forget the sales people spending the day at the telephone on the neighboring long table.


I misread as "You have until the next spring" and I wondered if you were hiring.


you have just described a factory construction line.

Not so much: a person not being a good fit for that company. More: that company not being a good fit for long term viability.


Awesome. I interviewed at one of these once, I knew I wasn't going to get it when I saw the massive imacs and the square glasses... plus the guy couldn't explain what it was they did at all.


I should have known to gtfo when at new-employee orientation I heard a presenter use the word "decisioning" with a completely straight face.


That one hit a little too close to home.


What's amazing to me is that devs/engineers are acutely in demand. It's odd that, given this, so many organizations get away with environments that are overtly hostile.


Depends on your industry and where you work obviously. But I have a strict "that will do" policy. If it looks like it might work, that will do.

It won't' be well engineered, it will be buggy. But the project will be completed and released. If it makes some sales and looks like it has some traction, then I go back and visit the many (many) //todo - clean this up and refactor everything out.

Usually after a while of adding more features, hacking out nasty design decisions the best way on how to do it comes to the fore naturally.


I agree with this philosophy as well. If is a project or program that will be worked on more as time progresses, typically it refactors itself after the programmer has a better grasp of the big picture. Often the bigger picture can only be grasped after there is some momentum with the project. I'd say this works 90% of the time, where the other 10% needs more upfront design and research done before any real work is started.


It's rapid prototyping. It might just happen that the prototype is good enough to sell, but once you have a basis, comparisons can begin. If the prototype doesn't have to act as the real product, just a skeleton, it's not so hard to change the "bones" if something isn't working properly.

If the prototype is quickly made, it could even count as part of that upfront design process.


I like my people to do more than prototype for this kind of work. It's probably a semantics argument, but we prototype as part of design and intend for it to be trashed/rewritten, whereas what I was speaking to above is real development work that will be used, definitely not polished but 'works' enough to keep moving forward.


That often means "getting the bones" right, but occasionally cutting corners to get stuff done. If your focus is having a good product, which frankly it most of the time should be, then done is better than perfect.


I have a simple mantra that helps me remember this.

"Its clay, not stone".


More like tar...


In that line of thinking, I would have to say it's more like dough. At the beginning good dough is very wet and sticky. If you are not decisive with your actions it will stick to your hands, your board, your table, your hair... As you work with it, it creates a workable ball that is dry and elastic. You can shape it with ease, it stretches paper thin without breaking, it rises high and light. The more skilful you are, the faster you can go from gloop that threatens to devour the world to something that is smooth as silk and almost feels alive. Those with little skill and experience never actually get there. They believe that beautiful well formed dough is a myth. They compensate by adding more and more flour so that the dough doesn't stick to their hands, in the end baking literal bricks. They pretend that the resulting bread is good.

Yes. Code is like dough.


You can also use code to _make_ dough.


I had this tendency too, and I've found that it helps to explicitly declare the first part of your process as "start at this new thing and suck at it." Firstly, because in order to suck at something, you're doing it, however badly. Secondly, because once you have some results, however bad, you can improve.

It's exactly like randomly initializing a neural net before starting with your training data. The results from the random weights will be garbage but that doesn't mean they're not useful.


> because you lack experience.

Well said. IMHO, this is the core of the issue and the thing that has helped me both move on and improve. You have to be ok writing crappy code because, in lieue of experience or someone to (on the spot) help you, its the only way forward. As long as you're professional about learning and refactoring, the best you can do is keep moving, keep learning, keep refactoring. Then eventually, help move others through that gauntlet more quickly.


Absolutely. First make it work, then make it better.


I think it's really liberating when you realize that the single most important thing is making it work.

Make it work, and don't succumb to the temptation to worry about what other devs think of your code. The subconscious dialogue that results from that is paralyzing. You can rewrite code n number of times and always believe that n+1 will be better (and it may be).

But, the funny thing is that the eventuality that would objectively make the code better is generally some potential future requirement that is imagined (whether functional or performance). This dictates infintely flexible design and the obsesive hunt for looser coupling. In practice, however, the eventuality seldom comes to pass. This is where the YAGNI principle has some value. Again, make it work.


The thing that helped me, when I felt the indecision paralysis come on, is to just do something and accept that it may be wrong. You often don't know the best decision in the first place because you lack experience. Doing it the right way by accident, or making the mistake of picking the wrong way helps get you that experience. Be deliberate about always doing something, and over time the paralysis will get less and less as you gain more experience.

Well-stated -- and (at the risk of over-complimenting), very close to the absolute zen of self-driven learning.


Yes, this. I'm self taught -- half the time I'm coding, I don't even know what a "good" approach should be. So I just get started on something, since there's no perfectionist ideal I'm visualizing that I need to hit.

The ironic part is that I face this exact problem with the thing I am formally trained in -- writing. The blank page paralyzes me in a way blank vim never does.


That's the thing - we often don't realize what was wrong (or, occasionally, what was actually awesome) about what we're trying to create until after we've put it down not just on paper (or in code), but in front of other people.

It's absolutely intrinsic to the nature of the process. And the best way to virtually guarantee that you won't be ever get to find out whether your idea was good or bad, let alone truly awesome, is... insisting on perfection before it ever gets out the gate.


The thing that helped me out of this, was seeing writing code like forging a sword, where you repeatedly heat up the metal, hammer it into shape and then cool it down again. (note: IANASwordSmith)

This works because heating up the metal makes it easier for it to arrange itself into a more optimal configuration and then you hammer it into shape and cool it down again to preserve the awesomeness you already reached. Then you repeat that process a thousand times until you have a katana that can cut through steel.

So, don't worry about just implementing a feature very roughly and breaking apart old structures you already put into place, you're just "heating up the metal". To see how everything should work and what it should behave like (i.e. "hammering it into shape"). As long as you then do the clean up/refactoring and put everything in the right place and such, i.e. "cooling it down again", you'll end up with awesome code that's robust and expresses what should be done very well.

So, in a way, imagine you're a smith at the forge, forging your code like a Hatori Hanzo sword, which is also a nice methapor, I think (with the difference, that your code's purpose is hopefully not cutting peoples heads of!).:)


Another useful idea is to be not interested (except maybe theoretically) in achieving the best, and concentrating on the good enough.

Sometimes the best is overkill, and a good enough is significantly cheaper (shorter, easier, available already).

Sometimes the best is not good enough, and you have to rethink your strategy.


Perfect is the enemy of good.


+1 on this. I personally had a similar issue where I'd start a project then go off on some huge overengineered diversion.

You Ain't Gonna Need It. Let that be your mantra. And always promise yourself that you'll refactor (and actually do it!)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: