Photo by Ferdinand Stöhr on Unsplash

This month it will have been ten years since I started my first full-time software development position. I thought it might be good to take a break and reflect on some of the main lessons I’ve learned in that time that continue to benefit me today.

Lessons

Assume nothing, Prove everything

Most of the biggest mistakes I’ve made have come from making and acting on bad assumptions. Sometimes it’s as small as “there is no way this line of code will fail” or “I know exactly how this function works.” Other times its bigger, like expecting another team is going to fix a specific problem or thinking an entire system works a certain way. In all cases it’s the same pattern. I’ve assumed something to be true without ever proving it to be true.

How to tackle ambiguous problems: What you know, what you think you know, and what you don’t know

As I’ve progressed in my career I’ve had to take on larger and more ambiguous problems. Often there are no experts to turn to for help, or if there is no one around me knows who that person is. One of the most useful practices I’ve developed when I hit one of these large, ambiguous problems is to immediately create three lists:

What I know (and can objectively prove I know) What I think I know (anything I believe to be true but cannot objectively prove) What I know I don’t know (questions I don’t have answers for)

Organizing the information I have in this way usually has an immediately clarifying effect. When I start the list of things I know is short and the goal is to move things out of the last two lists and into this list. I can use the list of questions I don’t have answers for as my first to do list. If there are others around me available to help, they can look at these three lists and immediately get up to speed.

Document, document, document

I used to be a big believer in self-documenting code. I’ve dogmatically stuck to this at the expense of writing comments for a long time. I still believe in writing clean, easy to follow code but I’ve learned that there is more that needs to be communicated besides just how the code works. I’ve had sections of code that I find myself re-explaining to others over and over. I’ve had whole systems I continually have to give walkthroughs of. Now, I lean towards over-explaining and putting more detail than may be necessary. There is the risk of these comments becoming out dated but in practice I’ve so rarely encountered this happening it hasn’t become a major issue.

Besides code comments, I seem to spend more and more of my time writing wiki pages and other documentation that is consumed by a wider audience than just other developers. 1 pager system designs, project plans, how-to’s, lists upon lists upon lists. All of this documentation serves a very important purpose: sharing knowledge and saving me time later.

Red-Green-Refactor

Learning to practice test driven development might be my most common recommendation to junior developers. It enforces discipline, helps break down problems into more manageable pieces, and generally results in higher quality code.

This way of approaching problems is useful for more than coding. Any sort of problem solving benefits by first knowing up front how you are going to tell whether you’ve solved the problem you set out to solve.

Make yourself replaceable

This career advice I received from many people. It continues to prove itself true time and time again. Sometimes it means automating simple work so that you can focus your time on more impactful problems. Sometimes it means training those around you so that you can explore new career opportunities without negatively impacting your team. Sometimes it means sharing and writing what you know so that you don’t have to spend a significant amount of your time explaining the same thing.

The best way to convince someone is to make them think it was their idea

“It is amazing what you can accomplish if you do not care who gets the credit.” — Harry S. Truman. People tend to get the most excited about their own ideas. At least, the ideas that they think are theirs. If you try to convince someone to think a certain way, especially if it means changing how they feel about a subject, you often be met with defensiveness. If you can help someone reason about to the conclusion you want them to reach they are more likely to support that conclusion.

The best way I’ve learned to do this is to ask questions. Often there may be problems that the person you are trying to convince may not have considered or benefits to your way of thinking that they cannot see. Asking the right questions can help someone convince themselves of the conclusion you wanted them to reach.

Estimation is a necessary skill

No one ever told me when I was taking computer science classes how much time I would spend estimating as a software developer. Whether its estimating cards for the next sprint, estimating an entire project, estimating the maximum throughput of a service, estimating the latency impacts of a change to that service, sometimes just estimating the number of jelly beans in a jar for an office contest.

Research has shown that humans tend to be bad estimators. Despite this, estimation is a skill that can be taught, practiced, and improved upon. A book I recommend everyone read is How To Measure Anything by Douglas Hubbard. In it he explains far better than I can how anyone learn to become a calibrated estimator and how to apply this skill to any problem with any amount of uncertainty.

Tool Choice Matters

As an engineer, you should care a lot about the tools you use. Good tools that make you more productive enable you to spend more time in a state of flow, and more time in flow is directly correlated with job satisfaction. Don’t tolerate slow or unreliable tools, but don’t just complain about bad tools either. Invest in improving the tools you build, and learn the tools you use inside and out.

I’m going to make a heretical claim: It doesn’t matter what text editor you use as long as you learn to become an expert at that editor.

Who you work with is more important than what you work on

If you work full time, you spend at least eight hours a day working, five days a week. That is a significant amount of your waking life. That means the people you work with have an incredible amount of influence over you and your overall happiness. The best teams I’ve worked with are the teams who I genuinely enjoy spending time with regardless of what we are working on. This means that I care a lot about who my team hires. It also means when I look for new teams I go out of my way to learn about the team I’m considering joining.

When making a career choice, choose the option that allows you to learn the most

You can almost always find a job that pays more, has a better title, lets you work on the tech you want to work on, or gives you the prestige you desire. What I’ve learned through my own experience and through seeing the paths of those around me is that the best career choices you can make are those that optimize for learning. Choosing what would allow me to learn the most is ironically why I dropped out of college with less than a year left. While it raises eyebrows on my resume I don’t believe I’d be where I am today if I hadn’t.

Good design enables necessary change

This is one of those overly nuanced lessons that is easy to misinterpret but may be obvious to those who have been building software for a long time. Most systems don’t change for the reasons we think they will. Some systems that we expect to change a lot sit untouched for years, while others that were hacked together over a weekend end up becoming business critical that need to shift with the direction of the business. This is why I prefer simple, easy to follow code over code that implements every textbook design pattern, and why I love questioning product managers over why they are making certain decisions and what types of things they want to do in the future.

Be a problem solver, not programmer

If you want to limit your career as a software developer, the best advice I have is to write code and do nothing else. If you want to grow, you have to recognize that your ability to write code is a tool in your toolbox as a problem solver. Being in the details gives you unique experience to offer solutions to business problems that may not require any code to be written. Learn to recognize the most important problems in front of you, and wear whatever hat you need to solve those whether it be the hat of a developer, project manager, product manager, engineering manager, or whatever it may be.

There is no one true paradigm

In school I thought that object oriented programming was the ultimate way to tackle everything, and functional programming was for high minded academics. If only I could go back in time and shake myself. Every paradigm has something to offer. Every paradigm is like a different tool you can use depending on the problem you face. Over time I’d say most of the code I write could best be described as “functional-ish.” Stateful design can be difficult to maintain and lack thread-safety. Mutability can be simpler to understand and in some cases more efficient. Use the right tool for the job, while respecting the idioms of your language.

There is no substitute for face to face communication

When you consider the cost of space plus everyone’s time, meetings are extremely expensive. Effective meetings are worth every penny. I don’t know if its body language, the natural ebb and flow of a spoken communication without the latency of a streamed video feed, or maybe most of us are poor writers, but an effective face to face meeting is the highest bandwidth form of communication you can have. Email, slack, video-chat, these are all great tools but sometimes they don’t cut it and the best thing you can do is get in the same physical space as another person and talk to them.

When in doubt, learn everything

I have half-jokingly given this advice to some developers on my team but I honestly believe it. Difficult problems are difficult because of how much you don’t know. The easiest problems to solve are the problems you know the most about. The more you learn about the context of a problem the easier that problem is to solve. Never stop learning. Always look for opportunities to read more, talk to experts, run experiments, whatever you need to learn.

What I hope to learn and improve on over the next 10 years

For whatever strange reason, I like building things. I like it so much, I don’t plan on stopping and I hope to keep solving bigger and harder problems. I know I still have a lot to learn. While this is a short list of things I hope to learn and improve on, I know that there is no way I can predict what I will learn, who I will learn it from, or how my thinking will evolve over the next decade.

Time Management

There are only so many hours in the day and I never feel I use them efficiently. I’ve tried every system I can find on the internet, and yet I never feel satisfied. There probably is no perfect system, but maybe one day I’ll find one that I like.

Estimation

Over the last 10 years I learned how important of a skill estimation is, and I learned that it is a skill, but it’s not a skill I consider myself very good at.

How to be a better teacher

I know that in order to make myself replaceable, I need to better teach what I know to those around me. Perhaps I’m just overly critical of myself, but I don’t consider my track record of teaching where I think it should be.

How to better take care of myself

One of my more recent shifts in thinking is adopting the Jeff Bezos view on work life balance: “If I am happy at home, I come into the office with tremendous energy. And if I am happy at work, I come home with tremendous energy.” I know I don’t work out enough, I don’t always eat the right foods, and I don’t always practice the best stress management techniques. Let’s just say, there is a lot of room for improvement here.

A million new technical things

You’ll notice that even though this is about my experience as an engineer, I haven’t mentioned much about extremely technical subjects. I haven’t mentioned learning to tune the garbage collector on a JVM, error handling in distributed systems, learning new programming languages or network security. I take learning all of this for granted. Of course as engineers we should be constantly learning new technical skills, improving our understanding of how computers work, and deepening our understanding of fundamentals like data structures and algorithms. Every day should be an opportunity to learn and share more of these that we learn.