Let's talk about the technical coding interview
As an interviewer, a mentor, and now a hiring manager I have put a lot of time and thought into the technical coding interview: the practice of asking candidates to solve a programming question on a whiteboard (or in CoderPad.) Rather than writing yet another “cracking the coding interview” guide, of which there are already a million, this post tries to focus on the broader picture. I’ve broken it down into several sections.
- Reasons to listen to me, reasons to ignore me
- Why do companies keep giving these types of interviews?
- Interviews, from the interviewer’s perspective
- April’s theory of interview prep
Reasons to listen to me, reasons to ignore me
In the interest of making this post clearer, I am going to say things matter of factly. But it’s worth asking, are my opinions actually facts?
This advice comes from my experience giving more than 130 40-minute interviews across two companies, which on one hand is a lot of interviews (that’s almost 11 8-hour days) but on the other hand isn’t (my opinions come from only two companies, 11 8-hour days is only 2-business weeks of interviewing, I haven’t been on the receiving end of these interviews in a decade.) Long story short, your mileage may vary when listening to me.
Why do companies keep giving these types of interviews?
Everyone knows it: technical coding interviews are stupid. Just off the top of my head, I can think of a number of reasons not to do them.
- The questions aren’t representative. I’m hiring people to write cloud services, not quicksort.
- Our working environment is low pressure, so it doesn’t make sense to judge people’s ability to answer questions in a stressful interview.
- Some candidates will have seen the questions before. At Google, I got asked the same question by two different interviewers. I told the second person I already did that problem, and he said “well I don’t have another question prepared so why don’t you just do it again.” Obviously I did well on it, but the interviewer learned nothing about my problem solving ability from my performance.
Considering all of that, it’s worth pausing and asking: why do companies, including mine, still give these interviews? Well, contrast it with the take-home coding interview and the design interview.
- It caps candidate time investment. I am uncomfortable with the thought of asking people with busy lives to spend hours solving some take-home (with that said, if someone told me they freeze up in live technicals I would generally be happy to offer them a take-home.)
- A friend was rejected by a company because they felt he “could have done more work” on the take-home. So their process prefers a candidate who takes 8 hours to solve it 100% over someone brilliant who only needed 1 hour to do 90% of it. Seems like a bad choice to me.
- It’s a sign of respect for the candidate: we are investing just as much time as they are. Asking someone you are likely to reject to do a take-home on their own time is easy. It’s also easy to justify it without feeling bad (they want the job and maybe they’ll blow you away with a perfect solution), but realistically you’re wasting their time. By showing up to the interviews ourselves, we demonstrate we’re serious about them.
- My company’s strongest sell is our people. Giving a candidate a take-home saves us time, but we lose an opportunity for the candidate to get to know us better and ask questions.
- It feels like a controlled environment (though it’s really not.) Everyone gets the same amount of time, everyone gets the same question, and it’s easy to set a consistent pass/fail bar.
- Live interviews are harder to game than take-homes, though of course it’s impossible to prevent cheating.
- A good design interview question is generally too hard for junior candidates. Companies know that universities teach students algorithms, not when to use Kafka (answer: never.) If we ask a candidate coming out of school to write an algorithm, then at least it feels like a fair test because we know they’ve recently learned it.
I’ve begrudgingly grown to accept their place in our process. When designing my interview, I do my best to take advantage of the format’s strengths: it gives me an opportunity to discuss (sometimes even collaborate) with a candidate and see how they solve problems in real-time. Maybe someday I’ll learn about a better approach, but this is the format I have today.
Interviews, from the interviewer’s perspective
Put yourself in the shoes of an interviewer. You’ve asked the same question so many times that for you this is a rote process. From the moment you enter the room, you know exactly what you want the candidate to do at each step. There are a few different solutions to your question and you’ve seen them all. You can recognize a solution that works by sight, and know exactly where to look for the typical places that cause problems. Frankly, you’re bored.
So, as a candidate, what can you do to make the interviewer happy? The answer to this question is the same as the answer to “what makes any random person you see on the street happy?” It is impossible to know! Of course there are some things that make the average person happy, you could offer a compliment or give them $20, but the only way to know what will make someone happy is to ask them. Similarly, there are a number of things that may make an interviewer happy (ask about every possible corner case, tell them the big-O complexity of your solution, write an iterative rather than recursive algorithm, etc.) While you could just do all of those things and more, they often come at the cost of time.
As one example of how different interviewers can be, there are interviewers who will fail you if you don’t tell them the O( ) complexity of your solution. On the other hand, I generally don’t care about it at all (though there are cases where I will ask so you ought to know it.) Imagine wasting 5 minutes (~13% of your interview time!) telling me something I don’t care about. On the other hand, imagine wasting 40 minutes solving a problem only to fail because you didn’t tell the interviewer the one thing they care most about.
Your best bet as a candidate is to follow the standard four-step procedure and probe the interviewer on what’s important to them as you go along. That way you focus on what the interviewer wants, and don’t waste time on things the interviewer doesn’t care about. For all of these example questions, make sure you’re prepared to handle anything the interviewer says (ie don’t ask questions you don’t want the answer to.)
- Understand the requirements
- (After you’ve asked the obvious requirements questions) Should I try to think of more corner cases?
- Would you like me to handle error cases? (If yes) I can do X, Y, or Z, what do you prefer?
- I could imagine extending this simple question to handle X/find all solutions Y, should I try solving that too? (This is high-risk, high-reward: if you skip a warm-up you may find the second question to be too hard, and now you’ve written 0 lines of code instead of 10, but if you skip a warm-up you’ll have more time for the main question.)
- Propose a solution
- Does this make sense? Did I handle all of your requirements?
- Should I try to come up with a faster algorithm? (Sometimes candidates waste time optimizing the warm-up problem when I just want them to move on to the next problem.)
- This solution is recursive which means it may overflow the stack, should I write it iteratively instead?
- Would you like me to tell you the O( ) complexity? Ω( )? Θ( )?
- Write the solution
- If I had more time I would do X instead of Y in this solution, should I take the time to change that?
- This looks right to me, should I move on to testing it?
- Test your solution
- Do you want me to write unit-tests or is it sufficient to step through a few examples?
- Do you see anything I’ve missed?
While we can be cynical about those questions (“but April, aren’t you just telling me to follow a script?”), if you ask those questions in good faith and with intention then every single one indicates you are thinking about the problem seriously and thoughtfully. Every single one can lead to a discussion. Consider “how would you like me to handle errors?”, as an interviewer my response is always “what do you recommend?” Now I get to learn how the candidate thinks about something more software engineer-y than computer science-y. My favorite part of an interview is that discussion, but other interviewers may want you to simply write code as fast as possible. There is no way to know unless you ask them.
April’s theory of interview prep
Of course there are an infinite number of ways to prepare for interviews, and presumably none is best. In this section I want to tell you one more, not because it’s perfect but because I think it’s pragmatic.
Please do not read this as a guide to passing my interview. It may help, it may not help. I make no promises regarding my interview content.
The basic idea is to work backwards: at most a coding interview will result in 30-40 lines of code. So what can an interviewer realistically ask? Well, 30-40 lines is approximately one simpler algorithm or data structure plus a twist. This means it’s impractical to ask you to write a red-black tree, there just isn’t the space, and instead they have to ask something simpler. While the twist is completely unpredictable (though LeetCode practice may help you recognize it), and while it’s true there is an extremely long tail of simple data structures and algorithms, you can and should memorize the most popular ones. Being able to regurgitate an algorithm on-demand without any bugs and without thinking will enable you to spend time and energy nailing the twist.
For all of these, you should be able to write a perfect implementation in Notepad (no REPL) that is bug-free on the first try.
- Algorithms to know (including time complexities)
- BFS, DFS, Dijkstra’s algorithm: both iteratively and recursively
- Tree traversals: pre-order, in-order, post-order. Both iterative and recursive.
- Sorting: insertion sort, selection sort, mergesort, quicksort
- Generally you should be able to write dynamic programming solutions where appropriate
- Likely useless things I personally think are cool: A*, topological sort
- Data structures (including time complexities)
- Array-lists
- Binary-trees (including balancing)
- Hash-maps (both chaining and open addressing)
- Heaps (array and tree representations)
- Linked-lists
- Likely useless things I personally think are cool: tries, ring buffers
Good luck!