So you got your first software engineering job...
You’ve just graduated college or finished a bootcamp and landed your first job. You’re walking into a situation unlike anything you’ve ever known. How do you succeed?
Having been a junior myself and now having had several on my teams, I have some suggestions on what skills you can cultivate to help you succeed in your early career.
Abandon Your Absolutes
There are no absolutes. There is no “correct” code style. There is no “correct” editor or IDE. There is no “correct” language or framework. Reality is messy and everything is a tradeoff. Sometimes you may need to use a language or framework that you don’t really like because it has the best support internally at your company. Sometimes you need to scope down a project to deliver some pieces faster because customers need it. Sometimes you need to accept inefficiency in your code because your time is more valuable elsewhere.
This is likely a consequence of both the education system, which encourages thinking about things in terms of “right” and “wrong,” as well as pop culture around software development. People enter the industry parroting claims they’ve heard on social media because they don’t have enough experience or context to understand why opposing viewpoints are valid as well. Professors are notorious for this as well. Many have never written a line of production code in their life and they’re going to try to tell you what is and isn’t “correct.”
If you find yourself thinking or speaking in absolutes, stop and think it through. How do you know your claim is “correct?” Do you have evidence? As you dig in, you’ll start to find tradeoffs and nuance that you were previously glossing over. This process is recognizing and challenging your own claims is vital to developing a more nuanced understanding of things.
Avoid Seeking Perfection
You can spend months optimizing your code performance and design, seeking an ever more-perfect version. This is a trap that will lead you to poor productivity.
Learning how to see when something is “good enough” is a critical skill. Yes, your algorithm may not be fully optimized, but given the traffic needs, compute environment, and cost tradeoffs, is it “good enough?” Let’s say you’re able to manage a 2% performance improvement in an algorithm by spending two weeks on it. Are you actually going to save enough compute cost for that to be worth the cost of labor and opportunity cost of you working on something else? Often times, getting 80% or 90% of the way there is good enough, and it’s time to move on.
This is something I struggled with for years and frankly still do from time to time. I want to build something perfectly and can fall into a hole where I’m fussing over details that are ultimately unimportant. Recognizing when you’re doing this and allowing yourself to step away and say “This is good enough” will serve you well.
Learn to Self-Direct
Coming out of college, you’re probably used to the exact opposite of self-direction. Classes give you an assignment with a clear scope and a clear deadline. You do the work, you turn it in. Rinse and repeat for four years until you get the piece of paper. This situation is incredibly rare in the industry, and you should throw away any expectation of clear requirements and clear deadlines, even as a junior. We as seniors often try to give well-scoped tickets to juniors, but there’s a tradeoff for us as well. In the time it would take me to write up a precise spec for a junior, I could probably do the work myself. It also doesn’t push you to learn if you’re just a Written-Word-To-Code-Translator.
In reality, you will get vague requirements and vague deadlines. It’s up to you to figure out exactly how to solve the problem at hand and estimate how long it’s going to take you. Practice decomposing problems into smaller pieces. I use work breakdown structures for this, where I just create a bullet outline of the work as if I were writing a paper. It helps me identify the big pieces. Dive deep into each of those and work out any edge cases or odd behavior.
Once you have that done, send it along to someone to sanity check your work. Iterate until you’ve got it.
Recognize the Difference between Experimenting and Grinding
You’re given a task that’s beyond your current experience. You don’t already know how to solve it, so you go exploring. Reading documentation, trying different approaches, reading StackOverflow, and so on. You’ll find a lead and follow it. Maybe it works, maybe it doesn’t. If not, move to the next lead. This is experimentation and is a critical part of learning. You need to be able to explore an unknown space and try different things on your own as part of being self-directed.
The fatal mistake is doing this for too long. Once you’ve exhausted the leads you have and start trying random things, you’re no longer experimenting. You’re grinding. Grinding away at work will lead to burnout very quickly. This is when you should escalate to a more senior engineer for help.
It’s a bit of a meme in software that juniors ask senior engineers for help and get one of two answers: “Why are you bothering me with this?” or “Why didn’t you ask me sooner?” These answers depend on when you’re escalating. If you’ve spent 10 minutes on a problem, give up, and then message me, you’re likely to get a “Did you actually do anything?” kind of answer. If you wait two weeks, grinding away while your soul slowly disintegrates, then ask, you’re going to get a “Why didn’t you ask sooner?”
Here’s the trick to do this correctly:
Read any related documentation or training
Look for existing solutions if available by searching repositories, internal or external
Walk through the system and understand it. Reverse engineer if needed.
Once you’ve run out of leads, escalate.
I just used this process yesterday when trying to do something kind of unnatural with an internal Amazon library. I read the documents, found nothing. Asked in a couple slack channels and no one had tried this before. Started reverse engineering the library and found a specific Factory class that look encouraging. Worked to see if I could customize the Factory. Found someone else who had done it by searching the code repositories. A couple of hours of experimenting latter, I had a working solution.
If you’ve done all that and still run into a dead end, escalate. Use this template:
Hi <Senior Engineer>,
I’m trying to solve Task X, but I’ve hit a dead end. I’ve tried the following:
A ended up not working because E
B was giving me an error F that I couldn’t root cause
C would be unsupported by the library vendor according to the documentation
Can you point me in the right direction?
By demonstrating that you’ve already done some things and are just at a dead end, you’re more likely to get helpful answers because we don’t have to walk you through the basics. We can also take a look at issues E and F to see if we can find a solution.
Beware the XY Problem
XY Problems are the bane of anyone asking for or giving help. XY Problems occur when the asker is trying to solve Problem X, does some exploring, and runs into Problem Y which prevents them from solving X.
Rather than ask for help about X, they go and ask for help about Y, which may or may not make any sense once given the full context. Instead, always communicate something along these lines:
I’m trying to solve problem X. I found this documentation which indicates if I do Z, I can solve X. Unfortunately, when I tried Z, I ran into problem Y.
This provides crucial context when asking for help, as your initial jump from X to Y may not make sense in the first place or there may be a better route. Just because you found Z first doesn’t mean it’s the right way. Even if I helped you with Y, your final solution Z may have some other issue.
Be Honest and Self-Aware
If you’re falling behind on a task, you need to clearly communicate that and why you are falling behind so you can get help. Yes, it can bruise the ego, but getting fired because you can’t do your work is going to bruise much more, I promise. If you’re on a supportive team, be honest about what’s up and what you need help with so we can provide it. Message people directly if you aren’t comfortable doing it in a daily meeting.
I’ve seen so many engineers go radio silent for weeks trying to do a single task. Meanwhile, resentment builds up in the rest of the team. “What is Joe even doing?” Then, we just go ask Joe what’s up, and it turns out he’s been grinding on something for a week that could’ve been fixed in a few minutes if he’d just asked.
Don’t be like Joe. Ask for help.
Breadth Before Depth
At this point of your career, you should be building breadth. If you’re doing feature work mostly, pickup an infrastructure task. If you’ve been working on synchronous API code, go work on something asynchronous. Try a new language. Go fix a security issue. Find some automation work that needs done. Shadow an on-call rotation. Pickup a front-end ticket. Write a product spec.
This approach serves you well in your early career because you probably don’t know what you actually enjoy. You’ve done some basic coding in school, but do you have any idea what actually interests you? What gets your brain going? If you’re stuck doing the same thing for years, you may end up with a specialization that you can’t stand and ultimately burn out. You may end up lacking skills that make you competitive in the job market and struggle to find work.
I started out doing infrastructure, automation, and monitoring. I had a broad scope and could pretty much get my hands dirty with whatever I wanted. I learned something important about myself in doing so: I really enjoy being a jack-of-all-trades that can bridge the gaps between specializations and I’m pretty good at it. I would never have found that out if all I was doing was configuring monitoring tools.
Continue Learning
It’s a hard fact that most degree programs don’t actually prepare you to be a software engineer. You’re learning the foundations: sorting algorithms, programming paradigms, different languages, data structures, and so on, but no one is teaching you how to breakdown and estimate work, write production-quality code, troubleshoot production issues, or learn how to use common third-party libraries.
You must continue learning on your own. Read books. Read blogs and newsletters. Ask questions. Your education didn’t stop once you got the piece of paper to hang on the wall. All that does is prove you know the absolute bare minimum about Computer Science, not that you’re an engineer.
Most of my career advancement has come from reading books to get the theory, then using opportunities at work to try those theories out. Some I found to work great, like Clean Architecture and Anti-Patterns. Some I found rather useless, like Test Driven Development. High Conflict taught me how to recognize and interrupt systemic cycles of conflict. Management taught me the principles underlying management as a practice so I could lead my team and interface with my managers more easily.
To keep advancing in your craft, you must keep learning, and you generally must do this on your own. There are no more classes with strict curricula to follow. You need to make your own. Be honest with yourself and find the things you aren’t great at. Take opportunities to fix those, be it a project in that space, reading a book, getting a certification, and so on. Step by step, you’ll turn yourself into a leader.
Questionably Functional is a reader-supported publication, providing a single free article monthly with weekly paid articles. If you found this article helpful and would like more, please subscribe below.
Some employers may allow you to expense subscriptions to industry newsletters under their Education or Development budgets. Click here for more information.
If you have a question or topic that’s been on your mind, and you’d like my opinion on it, you can submit a question using the link in the navigation bar. Thank you!