This post from Tony Payne, our VP of Technology.
Over the years, I've put a great deal of time and effort into learning how to maximize my productivity as a software engineer. Some of the best ideas came from The Pragmatic Programmer but many I've had to learn the hard way. Now, it's time for me to share some of the most effective means of achieving maximum programming productivity.
Type Fast
Much like a car, which can only put down as much power as its tires have grip, a programmer can code no faster than he can type. While this may seem obvious, it constantly amazes me how many programmers stumble their way across a keyboard. The great thing about typing is that it is a skill that anyone can acquire. Once perfected, typing takes no mental bandwidth and allows you to commit text to bytes at your natural speed of thought. By taking the typing speed-bump out of your mind's path, you can avoid the fits and starts that come with waiting for your fingers to catch up with your brain. The result is a natural rhythm that tends to build on itself until you find yourself coding at a breakneck pace.
Typing speed of course starts with good fundamentals. Typing as a skill has been thoroughly studied and a standard approach and practice regimen are well-established. This is not without good reason. The standard home row finger placement allows you to reach all the keys with minimal movement. Once you are thoroughly familiar with the placement of all the keys, the biggest time loss in typing is in the motion of your fingers. By maintaining a home row finger placement, you minimize this time loss.
As a programmer, you will need to make one slight adjustment to the placement of your pinky finger. Because coding requires so many sigils that are not used in common English, keeping a pinky close to a shift key is highly recommended. I like the right-hand shift key myself, but either will work. As a result, my entire right hand tends to shift one key right from a standard home row placement, and my left hand picks up all the mid-keyboard key press duty (at least on a standard keyboard).
After proper typing style, the second most important aspect in typing speed is the keyboard you use. The most popular keyboards seem to be the "ergonomic" convex split keyboards. Perhaps this is because they come with most computers, but I can't see why anyone would willingly use such an instrument of torture. Not only are they painful, forcing your hands to tilt up at an unnatural angle, but they also slow you down considerably. The convex shape forces your fingers to circumnavigate the keyboard to reach any key not on the home row.
Personally, I use a concave keyboard, such as the Kinesis Contoured keyboard. With a concave keyboard, every key is literally a flick away from the home position and that proximity shows clearly in one's typing speed. While such a keyboard may look intimidating, they are easy to learn. If you already have strong typing fundamentals, you will find the transition pain free. Allocate one to two deadline-free weeks for familiarizing yourself with your new keyboard. Take yourself back to fundamentals for a while as you break bad habits your old keyboard may have taught you. Find a "typing tutor" online and practice until you are back to the speed you had on your old keyboard.
Listen to Music
Music has a natural power to pace human activity. This lesson really set in at a dinner party not too long ago, where fast paced music was playing in the background. As I finished slicing my famous Santa Maria style tri-tip, I placed it on the dinner table as the music hit its crescendo. Before I had a chance to inform the guests that this would be a sit-down dinner, they were on the meal like a pack of hyenas, orchestrated to the music. It was quite a sight to behold.
The same holds true when you're coding. Play slow, calm music in your headphones, and you will naturally slow your rhythm to accompany the music. When a critical deadline looms, turn on high tempo music such as jazz, rock or, my favorite, bluegrass.. For the most critical deadlines, I will play Sing Sing Sing by Benny Goodman on infinite repeat. There exists nothing better (for me) to maximize my pace. This song has an amazing structure for the purpose: it starts with a steady, manageable pace. It's quick, but not frenetic. The lead instrument is the drum, with just enough horn to spice it up, letting you focus on your rhythm. About a third of the way in, the pace starts to pick up and you can't help tag along. Halfway through, you get what feels like a break although the pace has only backed off to where it began and immediately begins to build again. As the horns start to pile on, desperate, frantic, you're off to the races. Near the end, the horns start to mellow while the drums double time it. You take a deep breath, but your heartbeat continues to follow the drumbeat.
Before you're quite done with that breath, the crescendo is upon you and all the instruments are in full swing. You sprint through the last twenty seconds and land squarely back to the beginning with a tempo that just begs to double time all the way through the opening section.
Each time through, the process repeats, redoubling your pace until there's just physically no more speed to be had.
Music holds another benefit: reduced interruptions. When you are wearing headphones (much less effective with earbuds), people are less likely to break in for a "quick question." They'll do that extra bit of legwork to find the answer themselves, or they'll send you an email so you can answer it when you've hit a break in the action.
Additionally, it helps to quell any natural tendency to involve yourself in conversations taking place around you, whether they be social in nature or a design discussion to which you've got the perfect solution. Don't waste your time solving other people's problems. They'll be much better off if you let them learn how to find the answers themselves. Remember that old adage: "Give a man a fish; you have fed him for today. Teach a man to fish; and you have fed him for a lifetime." There is much to be gained, both for you and for your protégé if you let him find his own solutions.
Learn Your Editor
I must admit that I never paid attention to the importance of harnessing the power of your text editor until I read The Pragmatic Programmer. If you have not read that book, I recommend you remedy that situation quickly! A typical software engineer spends about three quarters of her time staring at a screen full of text. Have you ever stopped to think how much of that time is spent with tasks such as search and replace, cut (or copy) and paste, adjusting alignment, et al? A high quality text editor can greatly reduce the time of such repetitive tasks, but only if you take the time to learn how to use it effectively.
At one point, I found that I very often need to replace the word after the cursor with some other text. At the time, my vim vocabulary was smaller than a two-year-old's English vocabulary, so such a task required numerous keystrokes: dw (delete to the start of the next word), i(to insert), type the new text, plus the whitespace that got inadvertently deleted, followed by escape to get back to command mode.
This approach had an added downside of being practically useless for application of the repeat command (.). Eww! It turned out that the command I needed already existed: ce (delete to end of current word and enter replace mode). By spending just a couple minutes reading through my editor's documentation, I was able to save hours of laborious typing over the span of the next few years. Repeat that process just a few times and you'll find yourself a wizard with your text editor in no time.
Develop Tools
So far, you may have noticed that none of our tips are specific to software engineering. They could just as easily apply to technical writing, quality assurance, product management or any other endeavor that requires producing text and some amount of repetition. Very little of a software engineer's time is spent doing the one thing they are hired for: creative thinking. Therefore, we will not focus on speeding up the analysis or design phases of a project (besides, it's tough to teach someone how to think in a blog post). Instead, I'll keep banging on the same drum: the most effective time savings are found in speeding up common, repetitive tasks -- and there's no better speed-up than automation.
A great software engineer always knows that her software works. Why?
Because she wrote it to work -- and then she tested it. I've known great software engineers that code for every possible exception, design for every possible corner case and then test for each and every combination. One in particular spent 6 months on every project because he was such a thorough analyst and tester. How do you become a great software engineer without falling into the analysis paralysis/test-to- death trap? Automation.
It's surprisingly easy to shove a ton of test cases into your system and then get overwhelmed by the pile of results you have to wade through. However, if you start with a small, modest, automated test suite, you can avoid having to comb through endless results. My favorite test suite does nothing but run the application and return.
What a useless test suite, you say? Rarely. Without a doubt, your application has some configuration and setup steps that must be executed. You've got to clean, compile, package, configure, setup the environment and run. Do you want to type all those commands every time you test and risk wasting an hour debugging because you forgot a step?
Automate it. Now, you can begin adding test cases, one by one. Be strategic. Run through a couple tests manually and pay attention to what takes the most time and what's so annoying that it makes you not want to run the tests. Automate anything that's slow or that you hate doing. Pretty soon, you'll have a turn-key framework for running your application, capturing the important results and analyzing them. Don't forget to share it with the QA team!
Apply this same process to any task you find repetitive: logging in (set up your .ssh/config!), setting up your environment (get snazzy with your .bashrc or equivalent), building library packages, branching, merging and tagging in your revision control system, and so forth. Any task that you perform more than three times a week and which takes more than 10 seconds is a candidate for automation. The seconds will add up quickly. For example, a 15 second task repeated 3 times a week takes almost 20 minutes over the span of 6 months. Most business would be thrilled if their investments paid for themselves within 6 months! Invest in yourself and you'll see rewards quickly.
Avoid Writer's Block
While most developers have their own idiosyncrasies and stumbling blocks, writer's block is universal. Anyone who has ever sat in front of a blank pad of paper, an empty text file or a newly stretched canvas has experienced writer's block. It's the creative mind's version of stage fright. Called into action without being warmed up, your creative side freezes up and refuses to come out of its shell.
The simplest solution is to not scare it into its shell in the first place. Never attempt creativity on a blank page. Imagine if you were going to paint a landscape: you wouldn't start by painting the details of the autumn leaves, but would instead begin with a simple line where your horizon will eventually be.
The same applies to software: start every project with the basics.
Don't fret about how you're going to implement the caching to avoid that nasty O(n^3) algorithm. Don't worry about what fields you need in your data structures. Start by writing Hello World. Make it compile, make it run, write your test framework, and then start filling in some of the details: parameters, classes and placeholder methods for your algorithms. Only once you have laid the foundation should you pay attention to the details. As you go through the rote tasks of laying out your source code structure, checking it into revision control and scripting the configuration steps, your creative mind starts its warm- up. When it's time crank up the creative juices, you're ready to go.
Develop Frameworks
It is an incredibly rare piece of software whose crucial feature is an algorithm or other fancy piece of work that software engineers crave to write. In reality, our job is about understanding the end user, the business and the requirements. Software development would be so much simpler if product managers could just write code (or if developers could understand people). By building robust frameworks, you can essentially give your product managers the ability to implement their own requirements. Build a flexible, configuration driven system and they truly can.
Take those fancy algorithms that you end up rewriting a hundred times and capture them in a library. They may be fun to write, but trust me, eventually it'll get old and you'll appreciate being able to focus on the requirements rather than the machinations of complex interdependencies that you already figured out once (or 99 times) before. Frameworks free you from the distraction of leaky plumbing code and allow you to focus on your requirements. Any non-trivial system will eventually need the same plumbing: multithreaded processing, capturing & reporting job status, workflow, decision control, ... Don't fall into the trap of thinking that your system is unique in its system-level requirements. You face a design task that many others have faced before you and which you will undoubtedly face again -- soon. The only facet of your system which is truly unique is its requirements and users, so spend your effort there.
Debug Your Debugging Skills
For any developer not proficient at debugging, tracking down and fixing bugs quickly becomes the most time consuming task he faces. If you've ignored the "Develop Frameworks" hint above, this is doubly true, as you'll find system code mixed with your application logic and will be forced to debug the two in tandem. Debugging is about your ability to read code. It doesn't matter if you use a graphical debugger with breakpoints and watch variables or printf. Debugging is about your being able to quickly understand code, to visualize the path of execution and to understand how variables interact.
Personally, I've never found debuggers helpful, as printf (to inspect variable state and to see the flow of execution) and a stack trace are the only tools I need.
Much of the approach to debugging is about focus. Too many people focus on getting rid of the bug. They'll throw ideas at the wall until something sticks. Off by one error? Add one and see if that fixes it.
Log entries missing? Throw in some extra flush() calls, maybe that'll work. Granted, sometimes you can get lucky by mimicking a monkey, but you rarely get anything good out of it. You won't learn why you made the error, so you'll likely do it again. Often, there's a deeper- rooted cause that goes unnoticed until it's much too late. Instead, change your focus. Don't worry about how to fix the problem. Find out WHY it happened. Once you understand the root cause, the fix is often trivial.
Don't Solve Problems You Don't Have
The leading cause of bugs in software (in my opinion) is coding for requirements that you don't have yet. It is one thing to create generic, reusable frameworks and something else entirely to create code branches or database columns that you intend to use "someday."
The contrapositive is that you should immediately remove functionality that is no longer needed. If you find the need for it again, you can always retrieve the old code from revision control. These dead branches are impossible to test, and code begins to pile up to handle cases that may never exist. Un-utilized database columns have a tendency of being hijacked for other purposes than to which they were originally allocated. As a result, their naming makes no sense, there may be code lying around which accesses them according to their original purpose and no schema review ever takes place, because no schema change has been been made.
"We should forget about small efficiencies, say about 97% of the time:
premature optimization is the root of all evil." -- Donald Knuth
How many times have you designed a system that you just know will perform dazzlingly, only to discover that database bottlenecks, I/O bandwidth, or memory utilization slows it to a crawl? Right. You are not alone. Time spent optimizing a system that has not been profiled in a realistic environment is wasted. Until you know where the time is being spent, you cannot effectively optimize. Sometimes a slow system meets the needs just fine as long as it can scale linearly. Sometimes raw throughput is all that matters. Knowing exactly what your performance requirements are can help lead you to the right solution the first time.
Avoid the Not Invented Here Syndrome
It's an age-old story: developer builds system, debugs it, stabilizes it and then a new developer joins, immediately declaring the system unmaintainable, rewriting it and then fixing all the same bugs.
Suddenly, a year has gone by and the system has not progressed at all.
The Not Invented Here Syndrome is universal to developers. The earlier in your career you realize this, the sooner you can stop wasting your precious time reproducing someone else's mistakes. Granted, there are cases where systems truly are unmaintainable and deserve to be thrown out. More often than not, however, you're reacting to the feeling of discomfort that comes with new surroundings. The risks of rewriting from scratch are not to be underestimated. Not only are you likely to reproduce the same mistakes as the original developer, you are extremely likely to lose functionality that you never realized existed.
Next time you are faced with a new system and concerns about the quality of its design or the correctness of its implementation, try refactoring. By refactoring the existing system, you can learn its implementation intricacies while improving its design and maintaining its functionality. All the while, you continue to have a working system that can be extended with new functionality as needed. Resist the political urge to distance yourself from a project that may be perceived as a failure. Often with a slight nudge in the right direction, a flagging system can be brought to life.
Know Yourself
Common concerns and pitfalls which apply universally to software engineers are plentiful. However, each of us is unique and faces his or her own challenges. Some of us bore easily; some of us can't be bothered with details; others tend to over-think. Invest the time to analyze your personality, learn what you do well, where your mental blocks are and how you can improve.
Remember to focus your effort on the biggest problems first. Find ways to simplify and shorten repetitive tasks, avoid wasted work and get things done quickly. Don't be tempted to take shortcuts. Do it right, but do it quickly. When it comes to design, take the time to think the problem through and do it right. Focus your time savings effort instead on the never-ending mindless, rote tasks that plague the life of a software engineer and the rewards will be fantastic.