The following article was originally posted on my blog Superheroes in Racecars.
There are plenty of good resources out there that teach the technical skills that are necessary for becoming a good programmer, but I’ve seen only a few that give you the more personal lessons that you often only learn through experience or trial and error. In this article, I’m going to share with you some of my own habits and skills that I’ve gained over the years to help me do better work.
This article is based on a post I made over a year ago on the UofA CS Facebook Group in response to someone’s question about how you can become a faster programmer. Instead of posting about tools and keyboard shortcuts, most of my tips are about how to get yourself to think faster, along with a few tips for avoiding time-consuming mistakes.
Why do so many (novice) programmers brag about how much sleep they lost while working on their programs? Why do people assume that programmers only eat junk food and sugar-saturated energy drinks? Who seriously thinks that neglecting your own health is an appropriate way to do what you love?
Anyone who is serious about doing good work in software engineering (or in any profession) should also be serious about taking care of themselves. As Sir Ken Robinson suggests, people often spend too much time “living in their heads”, and so they tend to undervalue and underutilize their own bodies throughout their lives. In our case, programmers often tend to underestimate just how big of an impact their bodies have on their performance as an engineer. Programming is a task that requires a lot of focus and energy, and so it’s obvious how much your performance can suffer if you’re frequently sleep-deprived or malnourished.
Having a consistent sleep schedule with eight hours of sleep each night is crucial for maintaining your alertness and ability to think quickly. It can also give you an incredible amount of stamina for when you do find yourself needing to sacrifice sleep for one reason or another. Also, since losing sleep often hurts your ability to focus, staying up late to finish something is often a lot slower and more painful than simply waking up early to do it.
In addition to sleeping well, eating a healthy diet also helps keep you in a good mood and gives you the energy you need to tackle big problems. If you truly want to work hard, then you’re going to need full meals to fuel you, not empty snacks. Breakfast is often cited as the most important meal because eating fires up your metabolism, thus giving you plenty of energy to tackle the day with. I tend to treat my meals as sacred, meaning that I never skip them unless I have a good reason for it. When you’re working, it’s surprisingly easy to forget that you’re hungry, so it’s important to tie your meals to your routine so that you don’t accidentally starve yourself.
I personally drink a ton of water while working, and while I’m not sure what kind of an effect water has on my performance, most programmers I know make sure to at least keep a water bottle with them at all times when working. I prefer water to any other drink because I can drink as much of it as I want without worrying about any negative health effects (of course, I never even get close to getting over-hydrated), and I also find that water’s the only thing that actually quenches my thirst.
The benefit of eating and sleeping well is that it decreases your reliance on certain beverages or snacks to give you energy, and you can instead learn to rely on your own strength and stamina. If you do find yourself needing a boost of energy, then energy drinks are definitely not the way to go, since the negative health effects mostly outweigh the benefits. Lots of people drink coffee, but there are also other ways to get more energy, such as actually getting some rest.
Whenever I tell people that taking breaks is good for them and that it will actually make them more productive, I never meet as much resistance to this idea as when I’m talking to programmers. People are used to thinking about their productivity in terms of time, and they will often plan their time with the false assumption that they always work at the same level of productivity. In addition to time, you also need to think about energy, which determines how efficiently you work within a given amount of time. If you can learn to properly manage your energy, you’ll be able to get much more work done within a smaller amount of time.
The first step towards being more aware of your personal energy levels is to take breaks. Much in the same way that a runner gets weaker after running nonstop for a considerable amount of time, your energy levels and amount of focus also decrease the longer you work without a break. I typically force myself to take a 15-minute break after every 90-minutes of work. The breaks have as much of an effect on your body as it does on your own psychology. For instance, I usually find myself throwing much more energy into my work because I know that a break is coming soon to let me recharge.
Because focus is such an important part of the programming process, I often rely on timers to tell me when it’s time to take a break and when it’s time to get back to work. It’s just too easy to lose track of time while working, and so this frees me from the stress of constantly checking the clock. However, it does take a certain level of discipline to get yourself to actually take that break, because you’re often “in the zone” and you feel like it’d be unproductive to stop working at that moment. However, I’ve found that postponing or skipping a break is usually not a good idea because you’ll find yourself getting sloppy or unproductive as your energy levels continue to decrease. You’re usually much better off just taking the time to recharge and then tackling the task with a bunch of renewed energy after the break.
Here are a few rules of thumb for taking effective breaks. First, get off the computer and walk around a bit, while focusing your eyes on far away objects. Maybe go outside and get some sun, or find some stairs to climb. Do anything that will help you wake your body up after sitting down all day. Next, you must stop yourself from thinking about work. Your break is your time to recharge, and the best way to do that is to just unwind and let your mind rest. If your mind is still racing around, then that’s like a jogger wasting their break by running in place the whole time. I’ve met a few people who are just not good at unwinding, and they are some of the most perpetually stressed people I know.
Some people are simply afraid to unwind, because they fear that they’ll have trouble getting back into the groove of high-focus work, but that usually doesn’t happen unless you’re just dead tired. The next section has plenty of tips for helping you get back into the groove after a nice break.
If the last section was about unwinding, then this one is about getting yourself back in the zone. It helps if you imagine yourself as an athlete who’s about to begin a big match. Taking a strong, deep breathe usually helps me wake myself back up, almost as if I was gathering my strength to pick up something heavy. I also tend to fidget a lot while working, mostly by tapping my feet, and while most of the time it’s a subconscious habit, there are times when I consciously choose to fidget in an attempt to wake myself up and get my blood flowing because it’s time to work hard.
I also tend to listen to a lot of upbeat music while working. Almost my entire music library is really upbeat, but I also maintain a playlist called “upbeat” that I specifically turn to for when I need a boost of focus. I find that it helps to work with music that you’ve already heard a thousand times because then you can safely tune it out as you get more focused. Whenever I listen to new songs or to online radio stations, I usually find myself getting way too distracted by all of the new music.
It’s also important to avoid multi-tasking. Whether you’re watching a TED talk or eating a sandwich, if you’re doing anything else while working, it’s just going to decrease your focus. Many people probably think that that loss of focus is probably worth it, but I find working with low-focus to be pretty irritating for me. Whenever I work while distracted, I just can’t perform at the same level that I’m used to performing at, and it just frustrates me because I can clearly see how sloppy I’m being or how much time it’s taking me to do something that I typically do in a few minutes.
All of these techniques for getting in the zone are really only effective if you also take periodic breaks. Otherwise, you’ll just burn yourself out pretty quickly and start to underperform. It’s also worth noting that being in the zone is not the same as rushing through your work. Rushing will only promote sloppiness and poor judgments, whereas getting in the zone is simply about making sure your head is 100% in the game.
This particular tip is something that I believe pretty strongly in. When you force yourself to work only a certain number of hours per day, you’ll actually find yourself completing more work per day, not less. When you don’t have a time limit on your work day, you’re more inclined to slack off under the idea that you could always stay late and finish if you don’t have time. But once you realize that you’re only at work during a few precious hours per day, then you’re going find yourself working harder in an attempt to stop those hours from going to waste.
I’ve heard many stories about people who frequently worked overtime lamenting the fact that they now had to work regular hours after starting a family. To their surprise, they usually find themselves to be more productive, more alert, and just plain happier with their lives. If for whatever reason you still find yourself running out of time after working only eight hours a day, then the real problem was poor planning skills with impractical time estimates.
This might be the closest thing to a “rookie mistake” on this list, but I’m still mentioning it because I saw way too many students making this mistake during mock interviews. One of the easiest ways to waste your own time is to jump straight into a problem without thinking things through. Your first idea on how to solve a problem is most likely not going to be the “best” way of doing it. A good developer knows how to balance the trade-offs of several different solutions in order to pick the best one that fits the current situation. Experienced developers also have a good sense for when “there has to be a better way”, which prevents them from foolishly going down arduous paths.
There’s a lot of creativity that can go into how you think about the problems that you solve. It’s about more than just “what’s easiest?” or “what’s the most elegant way of doing this?” You also have to think about how risky a certain solution will be. For instance, you could use a new framework to solve a problem for you, but who knows what kind of unforeseen problems you’ll have while learning that new framework? Or if you go with an overly clever approach to the problem, then how painful and confusing will it be to maintain the code for this solution?
Another solution that is frequently overlooked is to simply do nothing at all. Sometimes the problem you were thinking about simply isn’t worth the effort to solve. Sometimes you might even able to hide the problem under the rug, either by directing the user’s attention elsewhere, or by making the bug seem more like a feature. Finding creative solutions such as these often requires that you always keep a strong grasp on what the actual problem is. For instance, if your code is slow, the real problem is usually “users don’t have the patience for this”, and it’s important to remember that optimizing the code is only one of many possible approaches to that problem.
Yeah, yeah, we all know how important it is to document our code, right? Part of the reason why programmers tend to have a love-hate relationship with writing documentation is because we’re typically not fans of being forced to do things that we think are unnecessary. But this sentiment mostly comes from a confused understanding of what documentation is for. I tend to see documentation as mainly being a tool for communication, meaning that when I write comments, my task is to make sure that whoever is reading it (whether it be a team member, a future employee, or myself) is able to understand the code that it describes. When people resist writing documentation, they usually say it’s unnecessary because either: (1) the code is obvious, (2) everyone on the team already understands it, or (3) there’s no way that they’re going to forget what the code does.
Because it’s so easy to make bad assumptions (such as the ones stated above), I personally prefer to take a risk-oriented approach towards writing documentation. For instance, instead of asking myself “Does everyone already understand this?”, I instead ask “What are the chances that someone will ever be confused by this?” If I think there’s even a small chance, then that’s usually good enough reason to write at least something down.
It also helps to show your code to someone and to actually see how they’re confused by it, so that you have a clearer idea of what points of confusion your documentation is supposed to help fix. Much in the same way that bugs are fixed after discovering them, many good comments are written after finding those points of confusion.
If the purpose of documentation is communication, then the purpose of writing notes for yourself is to help you stay organized. Sometimes you just want to remind yourself of something, or maybe you’re using your notes as a way of sorting out your own ideas. Unlike documentation, these kinds of notes are personal, and they’re tailor-made to fit your own unique way of understanding things.
Because it’s so personal, it’s important to experiment with different tools and techniques for taking notes so that you can find the method that works best for you. I tend to use different tools for different things. For instance, if I’m maintaining a TODO list, I usually rely on simple text files. If I want to write down a reminder, I use apps like Google Keep to automatically remind me at the appropriate time. And if I want to take notes on something particularly complex, I’ll usually turn to pen and paper so that I’m free to make drawings.
A good programmer should also know the limits of their own memory, which will only get weaker once he or she starts getting old. The worst thing about forgetting an idea is that you don’t even notice when it’s gone, and so there’s a small amount of stress that goes into making sure that idea isn’t forgotten. As Jesse Schell wrote in The Art of Game Design:
“When you think of an important idea, and you don’t write it down, it kind of bangs around up there, taking up space and mental energy, because your mind recognizes it as important and doesn’t want to forget the important idea. Something magic happens when you record it — it is like your mind doesn’t feel the need to think about the idea as much. I find it makes my mind feel clean and open, as opposed to cluttered and cramped.”
I find that notebooks are best for recording ideas, because it’s sometimes easier to explain ideas with drawings. Of course, as with any personal note system, it’s good to experiment with several different tools for recording ideas so that you can find what works best for you.
I think most programmers will agree with me when I say that the “right” way to program something isn’t always the wisest choice. Sometimes the solution that does things the most efficiently or uses the least amount of resources just isn’t worth the hassle when compared to the simpler approach that just gets the job done. Sometimes the system that covers all possible use cases and is polished to perfection is just overkill for what you need at the moment.
Knowing when to take a shortcut and when not to is a fairly tricky skill to master. It requires that you have a strong grasp on what your main objective is whenever you’re working. For instance, if you just want to see if the feature is a good idea or not, then you’d want to implement things as fast as possible so that you can answer that question quickly before making the commitment to implement the feature perfectly. This happens a lot in game design, when sometimes you just need to iterate a lot on a design problem, but you don’t want to waste time working too much on a feature that might just get removed in the next iteration.
Working in messy, disorganized code is like trying to live in a messy, disorganized house. You constantly stumble over things, everything smells, and nothing is placed in its logical location. It’s important to remember that these messes are often the natural result of the programming process, especially if you’ve been taking a lot of shortcuts lately. But much like how you’d clean a kitchen after cooking, you must also clean your code after you’ve finished getting your features to work.
Having “clean” code is often a result of several different qualities, such as having good documentation, being well formatted, deleting commented-out sections of code (you’ll never use them anyway), and making sure that everything is organized in a way that makes sense. When working on object-oriented programs, it’s common for class responsibilities to start deteriorating as you add more features, and so a proper refactoring job might require you to redesign the structure and responsibilities of multiple classes. Comments also have a way of decaying over time, so it’s important to make sure that they get updated every once in a while.
Aside from all of the productivity benefits that come with not tripping over messy code, frequent refactoring can often have really useful side-effects. Through the act of re-reading and reorganizing code, not only do you tend to get more acquainted with the code base, but you also restructure parts of it to be easier to understand, which is in turn easier to remember. This is immensely useful for debugging, because if you can remember where everything is and what it all does, then you’ll be much more likely to solve bugs quickly.
It’s also good to know when it’s okay to not refactor something. I personally prefer to refactor code only when its messiness is starting to be a problem, or when it’s obvious that it will be a problem in the near future. So say I just finished writing a class that is pretty confusing, but if there are no plans to touch that class again anytime soon, then it might just be a waste of time to refactor it, especially if it’s a class that is pretty isolated from the rest of the program. Only when the messiness starts to get in the way of progress (and on my nerves) will I start to refactor it.
If there’s a single skill that you can master that will have the biggest impact on your programming abilities, it would probably be debugging. Everyone hates it when they run into that one bug (or series of bugs) that seems to stump them for hours. There’s a lot that can affect how quickly you solve bugs, such as how much experience you have, your ability to stay calm when frustrated, and how well you understand the technologies that you’re working with.
Experience probably makes the biggest difference, however, because it’s just easier to find bugs that you’ve already seen before. I was lucky enough to have gained a ton of debugging experience while being a teaching assistant for several different programs that teach people how to program. When you’re in a computer lab surrounded by students who need help finding bugs, you start to develop a fairly methodical process for hunting down bugs quickly.
At it’s core, debugging is basically about testing your assumptions. Sometimes you assumed that you wrote everything correctly, when you in fact told the computer to do something else. Or maybe you assumed that your algorithm produced the correct result, when it really didn’t. Or perhaps you misunderstood how a piece of technology worked, in which case the bug can be a huge pain to track down (for example: modulus operations are pretty easy to misunderstand). Some of the hardest bugs can come out of the complex interaction of multiple large systems, which means that understanding the bug requires knowledge of how each of those systems work (this is why it’s good to know about operating systems and networking, even if you don’t plan to work in those fields).
It’s funny how debugging is such a big part of the programming process, and yet programmers rarely plan for it when estimating how much time they’ll need to implement something. Even when implementing easy features that we are 100% confident about getting right, we still tend to run into bugs, just because of how easy it is to make mistakes.
When I’m implement something, I tend to maintain a mental list of everything that might go wrong with the system and what that bug might look like if it did go wrong. For instance, I often get lazy when it comes to making sure that values passed into functions have acceptable values, but I at least take the time to imagine what would happen if the wrong parameters managed to go through (so if I was making a platformer game, for example, then maybe the player would start running the wrong way). Then when a bug emerges that looks like one of my previous predictions, I’ll instantly have a theory on what’s causing the bug, without needing to pick apart any code.
Almost every programmer has a habit of asking himself/herself the following question every now and then: “What would happen if this was the case?” The difference here is that I simply take it a step further and think about what the effects might be outside of the immediate, nearby lines of code. I imagine what might happen if that bug were to interact with the other systems of the program and how it might appear on screen when the program is running. Of course, this requires that you have a strong understanding of the systems that you’re implementing and the technologies that you’re working with.
So what do you think makes you a better programmer? Do you have any weird habits that help you do better work? Was there anything I suggested that you disagreed with? If so, then please post about it in the comments below!
Thanks for reading.