How I became a much more efficient developer

13 Sep 2023

I am a software engineer, just shy of two years into my career. My team and I have spent the last few months closing out a major effort, with great pressure to finish on time.

Inspired by similar posts by Dan Luu and Jamie Brandon, I addressed this challenge by putting lots of effort into improving my throughput. I’m happy with the results: our team velocity is noticeably higher, it took hours to pull together all my individual accomplishments into a brag sheet for this performance management cycle, and my reputation among my coworkers is now stellar.

In case you find yourself needing to do more at work, here are my thoughts on how to do that.

Keep feedback loops as short as possible

Developers on a time crunch rarely get to step back and examine their workflow. This often leads to inefficient processes that:

  1. Could be optimized with a few days of developer attention or less
  2. Routinely waste hours of developer time
  3. Do not directly relate to adding features, and therefore often go overlooked for days or months.

Individual level: testing practices

My IDE reports problems with my code with a latency of a few seconds. Running integration tests locally might take ten minutes. Running our full dev pipeline on Jenkins takes at least an hour if there are no builds in the queue.

My golden rule is to never let a problem that could be caught in a shorter feedback loop go undetected until a longer one is run. This saves time by not wasting forty-five minutes of pipeline build time on a code style issue that could have been caught with a fifteen-second mvn:checkstyle check, and by shortening feedback loops to the point where I don’t feel tempted to switch tasks. Returning to my original productivity level after a context switch takes a few minutes at least, and every context switch through the day accumulates slime on the surface of my brain that makes it harder to focus.

This also suggests adding everything into your IDE that you can. My team uses SonarQube to enforce code quality in our pipeline, but never set up SonarQube integration in our IDEs. We’ve had several pipeline builds fail due to SonarQube code smells. This is a mind-boggling waste of time when SonarQube integration in IntelliJ can highlight these problems immediately.

Team level: end to end testing

My team went several months without a way to test our frontend against our backend on our local machines. The fix wasn’t obvious because we don’t own the frontend or backend, and our compressed schedule meant we only felt comfortable spending time on grinding through tickets. We defaulted to testing in QA instead, but this led to awful resource contention and constantly stepping on each other’s toes: five minutes after my PR build deployed to QA and I could start testing my frontend and backend changes against each other, my teammate’s PR build would deploy to QA and overwrite my changes. This wasted days of developer time.

One evening, I finally dug through the frontend code and found where the logic to send requests to our backend lived. I spent two hours tinkering with it and made some progress, but got confused by an issue with HTTPS and certificates. The next morning I handed my work to my senior developer during standup, and in a few hours he had figured out the cert issue. By the end of that day we all knew how to test our frontend changes against our backend changes locally.

The feedback loop for testing frontend and backend changes against each other was immediately reduced from hours/days to minutes. Since this change our team velocity has about doubled.

The takeaway here: keep an eye out for bottlenecks that affect your whole team. Identifying and removing these bottlenecks can drastically improve your entire team’s ability to execute, and you get to take credit for it.

Org level: pull request norms

My team is currently on loan to help another project get across the finish line, so we don’t own any of the codebases or pipelines to which we contribute. We need approvals from the team that owns the project to merge pull requests, but since they’re constantly dealing with prod issues, they are not quick to review our PRs. They also do not trust our developers as much to write good code, since we have only worked with them for a few months and we spent much of that time learning the ropes. However, they do trust our manager, who they see frequently in cross-team meetings. When we instituted a team norm of getting PR approvals from at least one developer from our team and our manager before sending to our sister team for review, we saw a significant decrease in wait times for PR reviews.

Another pernicious time-waster is undergoing multiple rounds of PR feedback, especially given the long lead time on PR reviews in my organization. Every round of requested changes requires another cycle of testing and PR builds that will take a few hours at minimum. Some of this is irreducible complexity in the tasks, but after my first few PRs I noticed common patterns of feedback. I started keeping track of this in my note-taking app, which eventually grew into a PR checklist. By reviewing my changes against the checklist every time I have something to contribute, I make sure that I won’t burn one or two PR review cycles on obvious things that my sister team will dislike; keeping this information in a note outside my head ensures I don’t forget anything.

Become extremely comfortable with asking for help and asking questions

I basically never needed to ask for help in grade school, and got through most of college without needing it either, so I came into my full-time engineering career uncomfortable with asking questions because I didn’t want to look stupid. This seems to be a common failure mode for newer engineers. It is also one of the most damaging, because so much knowledge lives only in the heads of your fellow engineers.

This is not a new revelation, but I am reminded continually of its truth. I recently had to implement a new frontend component in Angular. I had never written any real frontend code in my life. I could have grokked the basics from watching any Angular tutorial, but the specific knowledge of the codebase required to complete the work had to be transmitted into me directly from the brains of my fellow engineers. I spent tremendous amounts of time programming with my teammates during this effort, and I would not have finished without them: just as an example, it took me and my senior engineer three days to get the frontend tests working for my changes because we discovered bugs in the testing suite we inherited. If I had wasted too much time trying to understand it myself, I would have still needed to take several days of my senior engineer’s time, but I would have waited too long and endangered our team’s timeline while doing it.

A critical skill is to learn exactly when you should stop trying to figure something out and call for help. For me, since some of that psychological discomfort with asking for help remains, I often time-box how long I will allow myself to spin my wheels. This is often down to the five- or ten-minute mark: “if I can’t figure out the next step on this work by ten minutes from now, I’m asking for help.” Note that this is strictly inferior to asking right when you realize you’re stuck - this technique just guards against wasting an hour or more.

In the same vein, I often find myself so confused that it’s hard to intelligently articulate my questions. When that happens, it’s a sign that other engineers need to be pulled in so I can talk it out with them. This is uncomfortable - stuttering through an explanation of what’s happening and grasping at the specific point of confusion is uncomfortable - but the options are to wrangle it by yourself and possibly burn an entire day trying to figure it out, or spend five to ten minutes vomiting words at your coworkers until all of a sudden the problem becomes clear. Your coworkers will be happy to spend this ten minutes if it means you actually accomplish your work and they don’t need to pick up the slack later.

Be particular in refinement

My team has landed on an objective standard for ticket refinement: every ticket should have enough information that any developer on the team could pick it up and know where to start, even if that doesn’t happen until several weeks from now. Since I am the newest on the team - and since one of my strengths is attention to detail - I often ask for multiple rounds of clarifications to be made until the ticket reaches this standard. Consequently, I no longer get tickets that I find confusing - I can almost always get started on them the day I pick them up without needing to hunt people down for clarification. This saves me several hours per sprint.

This is another win with team-level benefits. I was in sprint retro earlier, and one of my team members called out how much easier it was to finish tickets on time without having to figure out what the ticket meant, often going back and forth with the ticket author to figure it out. This validates that other developers had the same issue and also wasted several hours each sprint.

Dump mental state

I take lots of notes at work, mostly using a journal format with one markdown file per day. At the end of the workday, I create the note for the next day and add a to-do list ordered with the most pressing or time-sensitive task at the top. I’ll also write out bullet points for standup outlining what I accomplished the day before, what I’ll aim to accomplish that day, any blockers, and any topics to discuss during our parking lot.

By dumping my current mental state at the end of the workday into this note and structuring it in this manner, I find it a lot easier to relax after work knowing that the pertinent info I’ll need for the next day is written down. I also give much more thoughtful and detailed standup updates, surfacing any issues that require team help and proving to my coworkers (including my manager and PM) that I am actually getting a lot done. Having this clear log of what’s getting done also helps a lot during performance management, as I can refer to examples of good work down to the day it was completed. Finally, arriving at work knowing exactly what to do first lets me capitalize on my most productive hours in the morning.

Always capitalize on your most productive hours

Some people are morning people and some people are night people. I, myself, am an “everytime but mid-afternoon” person - I can be laser-focused in the morning until my first meeting and pretty good until I break for lunch, which takes a while to come back from. Getting anything done from 2:30 to 4:30 requires an elaborate array of ruses and tricks.

Knowing this, I try to get in before the morning rush so I can get to work before the office fills with people - normally around 8:45 AM. I’ll also delay lunch to 12:30 or later and keep it short by bringing lunch from home. Elongating my morning from three hours to four-ish is an incredible boon to my productivity, especially the time I gain before my 10am standup. I do still socialize at lunch, but maybe a bit less than normal.

For the rest of the working day, I’ll do my best to focus. Sometimes I do so quite successfully! Sometimes I’m booked solid with meetings, or just have a hard time focusing, but this matters less when I spend the first four hours of the day productively.

I try not to work much past 5pm, but sometimes it can’t be avoided. When I do, I find that I can actually do quite a bit once the office thins out around 5pm and the distractions go away. I’ll work until 7pm at the latest, normally getting quite a bit done. Then I’ll put the computer down, walk home, eat, and get on with my evening. If the mood strikes me, I’ll re-open my computer briefly sometime between 10:30 and 11. My brain is now well-rested and can fit in a small, discrete task before I got to bed at midnight - like drafting a pull request description, writing my to-do list, or outlining what I’ll say in standup so I don’t have to use productive morning time for it. This time helps me align my priorities for the next day to ensure I tackle the most important thing right when I get started.

Work where you are most productive

I struggle mightily with working from home, and have for as long as I can remember. In high school I could knock out math homework in a thirty-minute lunch, while eating, that might have taken me more than an hour at home. In college my grades correlated highly with how much time I spent in the library and in cafes - I’d work from my apartment only if I was in real trouble. (When COVID made all learning remote, you can imagine how fucked I was). I spent the first six months of my full-time job working from my parents’ house in Florida, and I only got decent results by working brutal hours and having a lot of prior experience. My median per-hour productivity sucked!

For this reason I go to the office all five days a week, despite my company only mandating 2.5 days a week. It’s just much easier for me to get work done, especially on Mondays and Fridays when the office is dead. If I felt that I was much more productive working from home, I would be scheming every way possible to maximize my time spent working from home, including applying for a remote exception at my job.

This idea goes hand in glove with working during hours of peak productivity. Working in my best environment, when I feel sharpest, is a game changer.

Always have work in the queue

Despite all the efforts to reduce our time spent waiting, tasks inevitably get blocked and context switching becomes necessary. I make sure to always have other work queued up and ready to go. When I write down my to-do list at the end of every day, even if I expect to spend the entire next day on a certain effort, I make sure I have the next step for another effort added to the end of my to-do list so I can go straight into it once I’m blocked. If I don’t know what this next task is, I know I need to bring it up in standup the next day to get help understanding the context of the ticket - but then again, since I’m persistent in refinement, I normally know the next step for each task.

Do all the “life” stuff you already know you should be doing

When my nightly sleep drops below seven hours per night, I start having terrible days at work where I get nothing done and lose track of work discussions. If I’m not eating well, it exacerbates the negative performance effects of working in the afternoon and affects my sleep. If I’m not exercising (which for me is lifting weights, running, and walking everywhere), I can’t sit still at work long enough to focus.

Everyone already knows that they should be sleeping to the point of maximal focus, doing regular exercise, and eating a balanced diet. If you feel you struggle with any of these, it’s worth the time and effort to figure out why. I wasted years of my life sleeping terribly because I drank black coffee and diet soda like it was water, which efficiently masked my mounting exhaustion. Nowadays I’m much more cautious about my caffeine intake and my sleep is much better. There was a point in my life where I neither ate well nor exercised. It took a lot of careful self-observation and journaling to convince myself of their benefits, but now it’s obvious to me when I’m not doing them because I feel absolutely terrible. If any of these start slipping, it shows up in my work performance and I know I need to course-correct.

General thoughts on process and takeaways

In the last few months I suddenly had to care about being more productive, and then I got more productive. This echoes Jamie Brandon’s thoughts on moving faster: the most powerful precursor to moving faster is caring about moving faster.

A lot of the most powerful things on this list come less from reading books or blog posts on productivity, and more from observing and taking notes and then trying to deal with my individual problems and bottlenecks. I think this pattern holds in a lot of domains.

The biggest wins on this list are team-centric: instituting team norms around pull requests and refinement, getting the entire team a working local setup, reducing our team’s dependence on a flaky and competitive QA environment, getting help from teammates and using these meetings to spread context throughout the team, etc. I think it goes without saying that overall team productivity matters more than individual productivity. However, I find that the faster I can deal with my own tasks, the more breathing room I have to consider how the team could be working more smoothly.

Things I have not tried but think might work

Further reading