Ask HN: What habits made you a better programmer?

138 points by bpourriahi ↗ HN
- How you think about a problem - Pushing past resistance

things like that

102 comments

[ 4.4 ms ] story [ 218 ms ] thread
For me, it has been writing small little utility projects using technologies that I'm curious about.

Recently it was jQuery and the Play! Framework -- for the longest time I was trying to think of some "grand" project that I could write to use them all together and become the master of all that is web... then a year went by and I continued to try and figure out what this ultimate project would be.

What a waste.

Then one Saturday I was bored and sat down and wrote a simple collection of AJAX utilities that do the most basic crap that anyone could write - using those two technologies. It helped get me over that hump and I learned a lot about the two techs I was curious about.

I went ahead and stuck the utils online for anyone to use and moved on with my life.

It was a great exercise for me, and a method of write/release I plan on using from this point forward for just about anything.

I became a better programmer by writing a lot of programs.
This will definitely help, to a point. Reflecting on programs you've wrote and seeing the mistakes you made is also important.

I've had colleagues that had wrote hundreds of programs, but they were mostly the same boilerplate, same glue, with different business logic. Writing a lot of different, varied programs, is key.

You can become an even better programmer by re-writing your programs.

Refactoring your code, or possibly coming up with an entirely different approach to solving the problem is what I consider one the most important parts of becoming a better programmer.

Learning. I'll probably be in the middle of learning something new and interesting the day I die. Some on here will disagree with me, but I'm a huge advocate of learning at the expense of doing. Spend time to learn how to not only do something, but do it well. After all, most programming tasks are only useful for a limited time. Knowledge is usually useful for much, much longer. Sometimes I'm surprised at how useful useless facts really are.

Plus if nothing else, you'll be able to do that task much faster the next time.

to add to that.. reading. I spend a lot of time reading, books, documentation, forums, and blogs
i find i can spend a whole day reading stuff and feel like i am much better off. if i spend half the time reading and half the time trying to apply what i've learned, then i will retain it soooooo much better.
Good advice, but remember what Einstein said:

"Any man who reads too much and uses his own brain too little falls into lazy habits of thinking."

To me, the best readings aren't ones that I learn directly from. They're the ones that spark thought processes inside my head. I haven't read past Chapter 2 of pg's "On Lisp", but I still consider it one of the best books on programming languages because it changed how I think about them in a lot of ways.

Three things: learning one editor (Emacs) really well, learning new languages that force me to think differently, and as am6100 said, writing a lot of programs.
I've used emacs for 9 years.

Last week, on a whim, I went cold turkey to vim.

The result: I'm probably staying with a (heavily modded) vim.

I added the standard bash/emacs/OS X input keybindings to insertion mode.

It feels like the best of both worlds.

The moral: it's good to branch out and try new things every now and then. You might get surprised.

I went the other way this year and am now almost totally in Emacs. I recommend trying the vimpulse package for Emacs if you ever want to come back to the dark side.
(comment deleted)
Tackling smaller chunks of work at a time, conceptually.

That might mean committing my changes more frequently, refactoring smaller blocks of code, or avoiding the temptation to fit a lot of functionality in one class/function/file.

I'm a big fan of technologies that have steeper learning curves, but pay dividends over time. Examples are: Emacs, Lisp, XMonad, git, my Kinesis Advantage keyboard, swype, etc. It's nice when technologies are user friendly or obvious to use immediately (like Quicksilver), but If it's something I'll be using all the time I go for long-term efficiently over early user-friendliness.
Are you a colemak or dvorak user?

I agree with you on all of those items. It's a little scary, actually.

Persistence and time. A good part of programming can be self-taught, so you just have to give yourself the time and be patient with yourself.
Noticing pain points. It takes time to develop a taste for good solutions. But when you modify or write code, and you say, "this is harder than it should be," this is a code smell and there is a better way to globally solve the problem.

Thinking first, writing later. When practicing for the ACM programming competitions in college, I discovered that when writing code before understanding the solution, I eventually needed to start over. I have never seen a counterexample.

Learning new things. Every year I try to learn a new major skill. Two years ago I learned Common Lisp, last year I hacked on some AI textbooks, and this year I'm teaching myself how to build websites in Python. Try for some variety - Alan Kay said "Perspective is worth 20 IQ points," and while I make no standardized testing claims, learning from multiple fields can connect the dots in interesting ways.

Reading code. Norvig's PAIP knocked me out of my Object-Oriented rut and changed how I think about coding problems.

Working with smart people. Surrounding yourself with motivated and effective workers has a great multiplying effect on your own productivity.

I definitely won't disagree with anything you said in the general case, but for this:

Thinking first, writing later .... I discovered that when writing code before understanding the solution, I eventually needed to start over.

I think this may depend on how and when you think most effectively. Having the discipline to be productive in pure thought is impossibly difficult to quantify. At times I feel productive working that way, but other times just writing code helps me think through things faster. I type fast so it's not a big burden to type a lot of stuff even if I have to delete it.

Of course, sometimes just drawing things out on paper helps as well. I seem to recall Dijkstra making some criticisms of that, but for me it is often highly effective.

I think it's more "think about what you wish to accomplish with the piece of code that you're about to write". Are you trying to write something that will satisfy users' desires? Then you darn well better think about every aspect of those desires, and make sure that the code you write satisfies them. Or are you trying to learn what users' desires are, or how the technology you have available will help you satisfy them? In that case, you may be better off just diving in and coding, but you should think about the question you want answered before you code.
I think you're both right. Some people do like the grandparent suggests and map their strategies out in advance. I personally do better with Brooks's "build one to throw away" approach. I like this better because I can't work with a plan like others do. I need to have implemented something before I can understand it. Others work better with a different approach. To each their own.
They're not mutually exclusive approaches anyway. Alternating between them quickly can be a good way to nibble away at hard problems. And it's not a big deal if you throw away some work in either mode. Think on what takes you there faster, not necessarily with less (wasted) work.
There's no clean path with regard to this.

Either you plan too much ahead and you end up tweaking your design forever, eventually building high-level cruft code that still won't get anything done because you don't dig deep to the ground to find the real culprits of your solution.

Alternatively, you plan nothing at all and end up rewriting your program several times because you just won't see the big picture as you're just digging up dirt from many different spots.

I think it works best if you plan only as much as you initially can and then start coding a proof-of-concept version as quickly as possible. You can only plan what you know of, and it's not much at first but it's something. Quickly getting to coding helps you understand the problem better, and you will throw away a many approaches that turn out to be suboptimal or solutions to the wrong problem, but there's nothing wrong with that. When the coding brings in more knowledge, then you can plan a bit more, and repeat.

If you're good, you can do that most of the most of the time. A conservative estimate might be half the time. More importantly, you will never hit the optimal planning versus coding point but you wave around on the both sides, sometimes planning too much and sometimes coding too much.

Experienced programmers can keep the amplitude of that zigzagging low, inexperienced programmers rush from endpoint to endpoint doing too much either.

Since I'm still in the early stages of learning to program (past the "durr what are for loops" stage, but still not good at a language), I've found that it's nearly impossible to really plan anything out.

I can plan out what I'll put stuff in and the names of the classes and methods that'll work with data, but I'm clueless until I start trying to implement the idea.

I suspect that once I've done enough things, it'll be easier to plan. I know when I go back to any of my old C# projects, I keep slapping my forehead and am able to see other (often better) solutions.

So I think the better advice is: Try until you know what you're doing, then toss it out and plan around all the terrible mistakes you made.

Quoting myself: You can only plan what you know of, and it's not much at first but it's something. :-)
Try this.

Design your program from high-level and iterate through phases into low-level. So for instance, I want you to make a program that, lets say, is a customer database.

High level 0: I need a customer database

Level 1a: I have these storage requirements.

Level 1b: I need these inputs

Level 1c: I need data to exit the system in these ways.

Level 2: Diagram of work-flow processes (how it interacts with real people).

Level 3: Diagrams of how the database will look, identify what types of objects you will need to work with (of course, this specific action gears you into OOP design and not functional).

Level 4: Problems. Spend some time bad-mouthing everything and how it works, go back to level 2 and when you're tired of crying about the thing go onto the next level.

Level 5: Document how it works. This is your Manual.

Low Level 6: Write the code, and when you need guidance, RTFM.

Level 7: Throw the thing into the trash and start over, they wanted an inventory management system, another tribute to the synergistic perspicacity of business people and software engineering's verbal constipation.

"Thinking first, writing later. When practicing for the ACM programming competitions in college, I discovered that when writing code before understanding the solution, I eventually needed to start over. I have never seen a counterexample."

The thing is. Some times writing code is how you best come to understand the solution. One of the things that have made me a better programmer, was exactly realising this. If there is something I don't understand, I now try to program it, rather than thinking too much over it first. Call it prototyping if you want. It's a fine line of course.

I like what Fred Brooks says here, something along the lines of: Be prepared the throw the first one away since you will anyhow.
1. unplug. no chat, no web, no email, no phone.

2. Find a work flow that keeps you moving forward no matter how slowly. here is mine. Basically design, top down then code bottom up, testing each piece as you go. An example of when I finally get to coding. a. write the comment for the function b. write the signature c. write the tests d. write the internals of the function

It will never almost never work out that perfectly but you should have the goal that, when you are done with that function you should never have to look at it again*

* you will. I do. Being perfect would be nice, but it just isn't going to happen.

Knowing when to stop.

When you reach a seemingly impassable problem, talking it over with other people and then sleeping on it will make everything clearer come morning.

If you work constantly you are a worse programmer than those who know to take breaks. These are the people who are thinking about what they're doing.

Second that - there's a definite point of diminishing returns and it's worth knowing when you're reaching that point because after that you're just wasting time, burning out, and throwing off future efforts.
That reminds me of my worst habit as a programmer: I want to solve every problem myself, instead of asking for help.

I certainly don't recommend quitting every time you have a problem you can't solve in half a hour, but there's a happy medium between that and wasting days (while you're getting paid, or at least wasting your project's time) working on something that the guy in the next room could sweep away in ten minutes.

Really taking the time to learn your current framework really well. For example, my first rails site was written with a shallow understanding of both rails and ruby, and I ended up writing reams more code than I needed to, and the code I wrote was usually rolling my own when rails/ruby offered a cleaner simpler and more robust way of doing it.

In other words, don't go against the grain of whatever framework/library you are working in. Learn the path of least resistance.

edw519 arriving in 5..4..3..2..1
Writing distributed code. Once your data is too large and your logic too distributed to run or debug locally, you are forced to become a much more careful programmer. I have to carefully read the code and think about what is going on, whereas before I often used debugging and stepping through the code as a crutch.
Taking a nap.

Seriously, the most difficult problems I've faced have been solved easily just after taking a quick nap (30 - 40 mins). Most of the time it comes to the point that there's no solutions in the horizon, I'm pulling my hair out and biting on my keyboard and then, a quick nap later most of the problem is solved in 20 mins when I sit in front of my computer again. It's got probably something to do with unconcscious mind.

But the problem is employers may not see this "sleeping to solve problems" act as productive as you do. I read that Google has "nap rooms", and I'll definitely have one of those when I start a company.

I can't agree with this enough, though I've found that the sweet spot for me is a 20-minute power nap. Any longer than that and I feel groggy rather than refreshed when I wake up and start coding again.
Actually by taking a nap you are doing something really cool.

Let me explain:

You are actually inviting your right hemisphere(the creative side of your brain) to come out and play. The right hemisphere cannot be forced into thinking that much so by taking a nap you are actually letting the unconscious work for you.

As a really interesting side note, Thomas Edison used to take a nap whenever faced a difficult problem. He used to nap with ball-bearings in his hand and when he would fall into a deep sleep dropping the ball-bearings would wake him up, and he would wake up and tackle the problem. :)

I've heard this left-brain right-brain stuff before and I'd like to know where it comes from. Do you have any sources you can link to?
It's related to early brain research which discovered some left right specialization. Medically it's been mostly debunked, but for some reason "Pseudo Science" people really latched on to the term.

PS: By “debunked” it was discovered that the brain has more plasticity in how and where specific tasks where preformed that initially assumed. Also the high level understanding of what doing “Math” or “Poetry” has little connection to how the brain actually does this stuff. EX: Some people can count time accurately why reading other people can’t. The most probable explanation as you grow up the brain chooses how to approach high level problems in a fairly arbitrary fashion.

I've read about it in a book called "Pragmatic Thinking and Learning" by Andy Hunt.
I do a top down view and a bottom up view, even if just in my head. How should this look and function when it's done? What base classes will I need? Which parts go in between? Also, breaking up things I have left to do into small enough parts that they can mostly be done in a single sitting (obviously there are exceptions). Then I never have to leave feeling like I accomplished nothing.
1. Embracing paranoia - enumerating everything that can go wrong with my code and testing for it. Over time, learning to think of more things that can break.

2. Writing down every question that occurs to me about the technology I am working with, at any point, specifically the behaviour of libraries and nuances of programming languages: I am not an expert in any programming language. Then chasing those questions until they are resolved.

3. When stuck with a slippery bug, attempting to reconstruct the bug in a toy program. If reconstructed, fixing it is easier. If not, I know it's not where I thought it was. Sometimes I never make it as far as actually writing the toy program; the intent is enough.

Maybe it's my undiagnosed Adult ADD, but:

* turning off Twitter & IM

* closing email and Google Reader

* headphones headphones headphones

* big ol' notepad for notes and doodles

Kill as many distractions as possible, make the ability to create & think ridiculously easy and just keep on truckin until you have something to test.

Never just shrugging some problem off with "boy that's weird." I usually can't let something go until I understand what caused an issue. It eventually became unrealistic to chase down every oddity, but I've done enough of them to learn a lot about different pitfalls.
Funny because that's one of the things that I've learned - when to let something go as 'that's weird' and just solving the problem without spending days digging into root causes. Sometimes it's worth it, but usually it's taking time from getting other priorities accomplished.
When I run into a perplexing thing like that, I put a tag on it so I know to come back later once my mind has had time to work on a different problem.
Prototyping semi-trivial cases. What I mean is that a solution of a problem might require some parameter so I'll just pick a value of the parameter that makes it possible to solve the problem in a brute force fashion and evolve the code base from that point. This is because I'm a visual and interactive thinker so any kind of feedback that helps me explore the problem space as soon as possible makes me at least twice as productive.
1) Being conscientious, actually caring about the work I put out. Spending that extra hour or two or four after I am 'done' with a feature, cleaning up the layout, making sure the field level validation all works, testing alternate paths, adding a few bells and whistles.

2) Learning how to communicate effectively. Keeping interested parties in the loop at all times. Not hiding mistakes or difficulties, or waiting until the last minute to let a PM know that a task is going to be late.

3) Not falling into the 'stupid user' trap. Your users aren't stupid. They know their business better than you do. You need to understand and accomodate their workflow, not the other way around.

1) is very important for me too. Many people will say 'oh don't take your code so personally' but that just doesn't work for me. When I write something I want to be proud of it and I'm going to feel bad if it doesn't work even if it wasn't my fault or responsibility.

Also agree on 3) If a user asks for something 'stupid' you can't just shoo them away. There's a reason they asked and your job is to find out what that reason was - and to come up with a solution for your client that fits the rest of the system. The client may not know what they want but they do know that there's a problem that needs to be addressed somehow.

Code review. Oddly enough, it wasn't so much the issues that were raised by the reviewers that made me a better programmer (though they certainly helped), but the knowledge that every aspect of my code could be inspected, and I'd be expected to fix it, that forced me to double- and triple-check my work before sending it off. Since that time, even when I'm working on personal projects, I find myself coding defensively, and will often write sections of code a couple times until I'm satisfied that it is unambiguous, correct, and clear.

This has helped immensely when I've gone back months later and tried to figure out what I was thinking; now, it actually makes sense!

Participating in programming competitions like IOI, ACM, Topcoder, GCJ, etc
How does that help? To me they seem like interesting puzzles, but with little relevance to problems in practical projects.
Those improves your coding ability a lot. For example, If your application needs to scale well, you should always consider implementing efficient algorithms. Yes, you can use some open source libraries, tools, etc but someone has to write those using efficient algorithms. This is one of the main reasons, that companies like Facebook, Google are recruiting people using these "puzzles" or contests like Topcoder, Google Codejam.
I think these kinds of problems make you comfortable with algorithmic thinking, it may not always be necessary to implement algorithm X or whatever in your day to day code but being comfortable with algorithms (by practicing) means you don't push off or miss the correct times to implement algorithm X.
(comment deleted)
So I do a lot of stuff that's not programming (dealing with managers mostly). But I've begun to practice a simple routine - make tea, close my door, and do nothing but write code from 2 to 4 (or 1 to 3, or 1:30-3:30). Every day.