How to Pass a FAANG or Big Tech Coding Interview

Aleexsan Adal
23 min readApr 20, 2021
Photo by Flo Maderebner from Pexels

The goal of this article is to clearly show you everything you need to do and which skills you need to pass a coding interview. First, I’ll tell you what happens in every coding interview. Then I’ll cover how to explain your solutions properly, and how to explain your solutions with a test case. Then I’ll cover what it feels like to use and get up to speed with Leetcode. Finally, I’ll end with a section on how and when to start mock interviewing.

There’s lots of good information out there, but you have to know what you’re looking for in order to find it. I think that finding the information becomes very difficult if you don’t know anyone in tech, so I hope this article can orient you and provide a roadmap of everything you have to do in order to pass the coding interview.

Note: This entire article is about passing the coding interview in the style that FAANG or big tech companies currently give it. Everything I say about coding interviews in general is only about the FAANG style coding interview. These opinions are my own and not those of my employer.

What you have to do:

  1. Listen to the interviewer’s problem statement.
  2. Ask clarifying questions/edge cases/assumptions to show understanding.
  3. If applicable, state a naive brute force solution***.
  4. If you think there is a better solution, find it and state the new solution***.
  5. Get approval for your algorithm.
  6. Implement your solution.
  7. Get approval for your code.
  8. Test your code with a simple test case*** and fix bugs.
  9. Quickly test edge cases***.

***Stating an algorithmic solution and running test cases on it so that your interviewer understands what’s going on is a subtle art. If you do it badly, you will confuse the interviewer and you won’t finish your interview. If you do it well, the interviewer will understand your algorithm immediately and be able to give you approval or feedback quickly. You need to communicate well or you risk running out of time.

How to Properly State Algorithms and Run Test Cases

Photo by mentatdgt from Pexels

This is by far the most important skill you need in the coding interview, and I think it’s really hard to understand how to do this properly if you haven’t had the opportunity to interview for real. I had to spend weeks training myself to do this properly.

Software engineers solve problems by writing code. Therefore when we see a coding problem, our natural instinct is to immediately hack something together or scaffold out a solution, then iterate until it’s working or optimized. This natural, iterative process will wreck your interview. Many recruiters specifically say not to dive into code immediately, I’m assuming because so many people do it.

Instead, what you need to do is state your entire algorithm and it’s time & space complexity to the interviewer first, get their approval, and then write your first line of code. This ensures that:

  1. Your interviewer completely understands the algorithm that you’re implementing and will be able to guide you.
  2. You will not waste time re-implementing or re-architecting code that you didn’t fully think through beforehand.

If you write a single line of code before the interviewer says “Ok, let’s implement that algorithm,” you’re doing it wrong (in my opinion).

Clear as mud.

Time for an example. Here’s a quick summary of a Leetcode easy question:

Summary of the Question Statement: Basically, either delete all 2’s from the input array or move them to the back by modifying the array in-place. Then return the new length of the array without any 2’s in it.The key to the question is that it's OK if the array still has the 2’s in the back.Input: nums = [0,1,2,2,3,0,4,2], valToDelete = 2Output is: 5 and input array should have all 2’s at the back.Transformed input (Naive, just delete the 2's): nums = [0,1,3,0,4]Transformed input (Optimized/Swapping method): nums = [0,1,4,0,3,2,2,2]

Here’s how my beginner friend described the solution to me:

Beginner’s Solution Explanation:

I would iterate over the nums array and when I see a 2 I would delete the element from the array.

So we start at 0, move to 1, and then we get to the first 2 so we delete it, and I’m seeing that deleting the 2 will iterate over the rest of the elements in the array which I believe means this algorithm is quadratic time because the delete function has to shift all subsequent elements in the array down.

Also I’m thinking about how to iterate because the array indices will be changing as we delete elements, so we need to not increment and analyze the current index after we delete…

Instead what we can do is we can swap all of the 2’s we encounter to the back of the array…

My friend had the right idea, in that he determined that the naive solution was O(n²) and the optimized O(n) solution was to use swapping. However, you might be able to tell that he was thinking about how he will write the code while describing the solution. Thinking about implementation details and mixing the time complexity analysis with the algorithm statement makes things very confusing to the interviewer. If the above explanation confused you, there ya go. Here’s a better explanation (the best one is coming):

Better Solution Explanation:

The first, naive solution that comes to mind is we can iterate over the nums array. When we encounter a 2 we can use Python’s built-in delete function. At the end, we can return the length of the modified array. This algorithm runs in O(n²) because the built-in delete function must iterate over all subsequent elements. I believe we can do better.

A more optimized solution is to iterate from the back of the array until we hit an element that is not a 2. We’ll save that pointer and call it “end.” Then, we iterate normally from the front of the array. When we encounter a 2, we swap the current element with the element at “end” and decrement “end”. When our iteration meets the “end” pointer, we return the current index as the new length of the array. This algorithm runs in O(n) time and O(1) space. Do you have any questions before I start implementing?

How is this explanation better than the beginner’s explanation? Here are just a few things this explanation does better:

  1. It states the algorithm in it’s entirety first, and only then provides the runtime & space.
  2. It confidently states the exact structure of the code without getting stuck on implementation details (however, it mentions relevant details like the fact that we’re using Python’s built-in del function).
  3. It clearly states which solution is the naive one, and which one is the optimized one. It gives the interviewer two complete algorithms, and if something about the algorithm is wrong the interviewer can very easily give you feedback on how to improve or fix it.

It’s much easier to bring this point across in-person as opposed to writing, but I hope you can see how much more understandable the second explanation is. I found it very unintuitive to explain code like this, but after I trained myself to do it all of my interviewers cited communication as my strong point, with one of them even saying I was “crystal clear.”

If your explanations sound more like the beginner’s right now, don’t sweat it. Especially if you’ve done less than 100 Leetcode questions. Almost everyone is bad at this style of communication when they start. Like I said, it took me weeks of focus to do it properly after I had done around 150 Leetcode questions! Don’t give up. If the mountain starts feeling like a wall, take a break and relax for a while. Then, after checking levels.fyi one more time, come back and keep practicing.

Taking it to the Next Level by Running Test Cases

I’ve only covered the first part of this section up till now — “How to Properly State Algorithms.”

Properly running test cases verbally is very closely related. All of my algorithmic explanations are done with a test case and you might have noticed that both explanations use the initial test case. It’s even better to not state the algorithm like I did in the previous section, but instead interactively transform the input into the output in front of the interviewer.

This style of stating the algorithm allows the interviewer to analyze and correct your thinking very quickly. In most of my interviews, the interviewer would stop me in the middle of the process to correct something small or ask a clarifying question about what I’m doing. This back-and-forth is very quick and easy when you’re talking about a concrete test case, and can get confusing if you’re trying to state a general algorithm.

Here’s a more accurate version of what I would actually say, and how I would transform the input, written in a comment in the shared code editor, as I explain the algorithm:

Complete Explanation with Interactive Test Case:

The first, naive solution that comes to mind is we can iterate over the nums array. When we encounter a 2 we can use Python’s built-in delete function. At the end, we can return the length of the modified array. This algorithm runs in O(n²) because the built-in delete function must iterate over all subsequent elements. I believe we can do better.

A more optimized solution is to iterate from the back of the array until we hit an element that is not a 2. We’ll save that pointer and call it “end.” End now points to the 4.

Initial state: [0,1,2,2,3,0,4,2]

Then, we iterate normally from the front of the array. For the 0 and 1, we do nothing. When we encounter the first 2, we swap it with the “end” pointer and decrement “end.”

Modify the array: [0,1,4,2,3,0,2,2]

Now “end” points to 0. Next, we encounter another 2 so we swap again and decrement “end.”

Modify the array: [0,1,4,0,3,2,2,2]

Now, “end” points to 3 and our iteration meets the “end” pointer. We return the current index, 4, as the new length of the array. This algorithm runs in O(n) time and O(1) space. Do you have any questions before I start implementing?

That was definitely more cumbersome to read, but I hope that you can see how this explanation is even more concrete and easy to understand than the “good” explanation in the previous section.

This test-case-based explanation is used to state the initial algorithm. It is very easy to adapt this to validate that your code works as expected after you write the solution. Take the same test case, and instead of stating what the algorithm does, walk through your code line-by-line and do what the code says to do. This is basic manual debugging and all of the big tech companies evaluate you on your ability to catch bugs manually. In my experience, I always catch at least 1 or 2 small bugs by doing this.

You should use this test-case-based explanation to state your initial algorithms and apply test cases and edge cases to your code after you’ve written it. Facebook and Google both tell you explicitly to test your code, and in my experience this is exactly what they mean.

Notice also that I only did the interactive style for the optimized solution, not the naive. This interactive style is appropriate for medium to hard level algorithms because it simplifies the explanation, but takes more time. The non-interactive style that I stated the naive solution with is appropriate for easy level or naive/brute force algorithms. However, interviewers have their preferences and you may be prompted to use either communication style for any question difficulty. Be flexible.

Apply. This. Knowledge.

So how do you train yourself to properly state algorithms by using verbal test cases? In my opinion, first you need to Leetcode a lot (100–150 questions minimum). When you do enough Leetcode questions, you will start to be able to envision the code before you write it.

When the code is in your head already, you can train yourself to transform the input into the output verbally by speaking out loud and mock interviewing yourself. You will be able to do it confidently, without getting stuck on how you’re going to code it. This takes months of tough, ego-crushing, daily practice. If it feels impossible now, don’t give up. Everyone is bad at this when they first start, myself included.

A Guide to Leetcode

Photo by luis gomes from Pexels

Leetcode will help you identify which DS/algos to use and allow you to practice implementing faster. I hope the previous section has shown you how important it is that you focus on your communication — try not to get into the habit of just Leetcoding without speaking out loud as if you were in an interview.

That said, identifying which DS/algos to use based on the question characteristics and being able to implement the code quickly is hugely important, so here’s a section on how to properly use Leetcode.

Mount Everest

It’s a very long and painful process, but rewarding. It will feel like you’re climbing Mount Everest, especially at the beginning. Don’t give up, just take it one problem at a time. Before you can do one problem per day, just set yourself a goal to understand one new part of a problem per day and increase your daily goals as you get better. Do a little bit every time, and learn new things consistently, and you will get better. Nobody is naturally good at this.

In terms of language choice, it seems like people on Leetcode write their solutions most commonly in Java, Python, C++ and less commonly in JS. My personal favorite is Python because it’s often more concise and in my opinion is nicer to look at than the other languages. I would recommend choosing either Python or Java because they are the most common ones used in the solution/discussion sections and you need to see other people’s solutions in your language in order to write better code.

A Glimpse of the Mountain (Understanding)

Start off with lists of questions sorted by topic (such as this list of 75 questions on Blind, or this Leetcode post that you should bookmark and never read again). Pick any topic, and for your first question don’t even attempt to solve it. Just read the solution, deeply understand the data structure/algo used to solve it, and why the time/space complexity is what it is. You might spend hours or full days confused about a specific line of code or the time/space complexity. That’s OK, it’s part of the learning process. When things get frustrating, take a break and come back to the solution later.

When you think you have a good understanding of the solution, write the code yourself. Make sure you understand what you’re writing. If you don’t know why you’re writing the code in a particular way, you probably don’t understand the solution well enough.

This is the most painful and frustrating part of the process. It took me months of only looking at solutions before I could even slightly predict what the code would look like. In fact, I didn’t actually solve any of the 75 questions on the Blind question list. I simply read the solutions and deeply understood each and every question. The slower you go, the better you will understand the DS/algo or type of question that you are studying.

Complete View of the Mountain (Familiarity)

After a long time, you won’t need multiple hours to understand the solution when you know what type of question it is. The solution code will start looking familiar or predictable. At this point, you might be able to partially write out the solution when you know which topic the question is under. You will probably find that for the DS/algo you are studying, there are some variations. Try to identify how different questions in the same DS/algo are solved differently, and why. You’ll have a more flexible understanding of the topic and you’ll be one step closer to solving questions without looking at the solution at all.

At some point, you should be able to write a workable solution if you know exactly what DS/algo to use for the question. Sometimes, questions may be tagged with the same DS/algo but the implementation might look really different. You can tackle these “weird" questions after you can solve the usual case of the DS/algo.

Again, this stage is agonizingly long. I remember being able to solve certain basic problems just fine (like sliding window) but then getting caught totally off guard by what appeared to be a sliding window question, and feeling like I didn’t know anything when I read the amazing explanation about why it wasn’t sliding window. Another example at this stage is I could have solved this straightforward binary search question, but spent a lot of time confused on this more advanced binary search question.

Again, at this stage don’t worry about the more advanced questions. Focus on solving the straightforward ones first.

Gain Confidence (Generalized Understanding)

At some point, you’ll be able to solve questions in multiple topics and you’ll have some flexibility in adapting the DS/algo to the specific question. At this point, dive deeper and ensure you have a really good generalized understanding of the DS/algo. Be able to transform the DS/algo in significant ways to adapt it to fit those problems that seemed “weird” earlier. Think about the DS/algo until you understand how all of these questions fit under the same topic. You just have to figure it out. You’re prepared to climb Mount Everest, you just need to keep going.

Once I had a slightly deeper understanding of BFS/DFS I had fun solving questions like this one.

First Climb (Switching Topics)

Ok, now you have a generalized understanding of some DS/algos. It’s time to work on a few extra things.

From whichever list of questions you’re doing, choose a few questions from different topics and practice solving them one after the other. Switching from topic to topic is hard at first, but practice it so you can do it more easily.

Climb Faster (Speed and Cleanliness)

Now you should be able to switch from topic to topic. It’s time to work on speed and cleanliness. The cleaner your code, the faster you will be.

At this point, you should be solving questions regularly. For every solution that you write, go to the discussion section and look at the crazily optimized solutions written in your language. Usually, people’s solutions in the discussion section use so much crazy syntax that it’s completely unreadable. Verify that your solution has the same time/space complexity, and then try and pick up some simple syntax that they use to make your code cleaner. Try to reduce the amount of duplicated logic in your code.

When your code starts looking a little cleaner, start timing yourself. Don’t put yourself under massive time pressure just yet, just get a baseline of how long it takes you to solve easies, mediums, and hards.

You should work towards consistency, meaning that you solve the same level of question in all topics in roughly the same amount of time. You’ll probably find that certain topics take you much longer than others. Work on your slow topics until you get them in line with your fast ones.

As you’re working on your speed, don’t forget about your cleanliness as well. Keep picking up small syntax improvements from the crazily unreadable code in the discussion section. It will eventually become more readable and your code will become cleaner, shorter, and you’ll be able to write it faster.

Getting the Hang of it (Code In Your Sleep)

Now, you have some momentum. By going through the process in the previous step, you should have already massively reduced the time it takes you to solve questions. Chances are though you’re still not hitting the insanely fast speeds you need for FAANG. Generally, you want to solve easy questions in 10–15 mins, mediums in 20–25 mins, and hards in 30–45 mins.

The reason why you want to hit these times is because interviews are generally 45 minutes, and you need to be able to solve 2 mediums, 1 hard, or 1 easy and 1 medium/hard in your interview. The exact format is a little different for each company but if you can hit these times you will be able to pass interviews at all big tech companies.

At this stage you’re close, but not quite there. You get there by being able to write each DS/algo in your sleep. For all of the common ones, memorize a general version (you should know enough now that you can write your own general version of each DS/algo). Practice scaffolding out each one in a text editor with no question in mind until you can do it in only a few minutes.

Then practice the questions related to the pattern you just wrote out. You should be fast now. Blazingly fast. You should be able to scaffold out all patterns in less than 5 minutes, and doing the question should be anywhere from 10–15 minutes if there is no challenge in applying the pattern and 20–45 minutes if there is a challenge or big challenge in applying the pattern.

However, you also need to start mock interviewing yourself. You’ll probably find that talking out loud, pausing to make sure the interviewer understands, and everything else about the interview format throws you off. This is why it’s important that you can write the patterns in your sleep. If you can’t write the pattern in your sleep, you won’t be able to focus on explaining what you’re doing and you won’t be the best interviewee you can be.

At this point, the code should just flow out of your fingers and you should focus on speaking well and speaking clearly. Focus on the interviewer.

Any Mountain, Anywhere (Fully Generalized Problem Solving)

The final hurdle is mapping non-obvious question statements to a DS/algo. There are plenty that don’t map cleanly to a pattern you know, or they look like one pattern but you actually need to use a different pattern to solve. These types of questions will probably trip you up and you won’t hit the times that you need.

I found that using the test-case-based explanation I described earlier helped me out a lot. For the question that seems like a sliding window but it’s not, if you just try a sliding window solution on the input, you’ll see very quickly that it doesn’t work and you’ll be one step closer to finding the right DS/algo.

Also, it’s OK if you don’t always hit your times for hard or tricky questions. Chances are if your interviewer gives you one of these, they are looking for your thought process. When you recognize that the question is not what it seems and you start searching in the new solution space, the interviewer should prompt you in the right direction if you need a hint.

At this stage, when I looked at tricky questions and got stuck I would just read a few sentences of the solution but not enough to understand what to do next. If you do this correctly, it’s pretty similar to getting a hint from the interviewer. You need to have done enough mock interviews at this point to know how to do this correctly. A hint isn’t telling you exactly what to do next, it’s a nudge in the right direction. A few words or sentences of the solution should be enough to give you that nudge.

There are also questions that don’t map to any common pattern. It’s probably worth learning a few of these just to exercise your creativity but if you encounter them earlier in the process it might be better to just focus on questions that cleanly map to patterns.

A few items that still blow my mind are awice’s Merkle Hashing solution, the recursive solution to Max Path Sum (I actually got this question in one of my interviews, so that shows it’s worth learning the popular hard questions), and this great article on how to deal with all Best Time to Buy/Sell Stock questions.

Loose Ends: Asking Clarifying Questions

So far, I’ve explained how to communicate algorithms and run test cases, and how to use Leetcode to make sure you can implement your solutions. This covers steps 3–9 in the initial list I gave you of everything that happens in a coding interview. The only other thing you need to do is ask good clarifying questions.

Most of the time, the interviewer will give you a properly specified question. Sometimes, the interviewer will give you an under-specified question. Here are corresponding examples and the type of clarifying questions I would ask.

Here is an under-specified problem, and how I would define the problem/ask clarifying questions:

Under-Specified Problem Statement:
“How would you build a cache?”
Clarifying statements/questions:
- I assume that this cache should have get and set methods.
- I assume that the get method should return the cached object given the object's key.
- I assume that the set method should enter an item into the cache given a key and value, and if the cache is full the set method should delete an entry according to some replacement policy.
- LRU is a common replacement policy so we can use that. The other alternative is LFU which may be more performant but more complicated to implement.
- Do you (the interviewer) have any questions about the above assumptions?

Here is a version of the LRU cache question that is properly specified, and how I would ask clarifying questions to show that I understand the problem:

Properly Specified Problem Statement:
“Write a cache class with get and set functions and a capacity that kicks entries out using a LRU replacement policy”
Clarifying Questions:
- What if the capacity is zero? Should we throw an exception since it's an invalid value or just not store anything?
- Should we assume that each set function will come with a key to each item stored, and can we assume that the key will be a valid, hashable data type?
- Can we assume that each key will be the same type (int, string, etc.)?

Final Advice

Photo by John Guccione www.advergroup.com from Pexels

Mock interviewing yourself helps somewhat with your communication, but I found that it only helped me when I knew specifically what my bad habits were.

In order to figure out what your bad habits are, you need to do live mock interviews with people. There are plenty of sites to do this at including pramp.com (free, but questions are too easy), interviewing.io (interviewers will be high quality, but it’s paid), or just make a post requesting an interview buddy on Blind (your mileage may vary).

The best thing you can possibly do is find an interview buddy who is as committed to the process as you are and who understands how important mock interviews are (they are just as essential as Leetcode). Anecdotally, I found that people tend to meet one other person they vibe with on Pramp and then they consistently practice with that one person.

Personally, I used Pramp to meet 1 consistent interview buddy and also spent way too much money on interviewing.io. I did 14 mock interviews through interviewing.io which is definitely overkill, but that’s how I do things when the outcome is very important (and I got to the offer stage at Facebook, Google, and Microsoft).

My opinion is that if you know what you’re doing and have a great buddy who can get you close to the level of a real interview, then you probably need around 0–3 mock interviews from an experienced engineer who you don’t already know. In all other cases you probably need somewhere between 5–8 mock interviews from an experienced engineer. Interviewing.io is currently testing out a deferred payment program, and I would recommend that as a way to access their resources without having to pay immediately.

I haven’t directly answered the question of when you should start mock interviewing. In my opinion, you should be solving Leetcode easy and medium questions across various topics in a reasonable amount of time (less than 45 minutes) before you start mock interviewing. I believe that mock interviews should be used to work on your communication, and you can’t do that if you get stuck because you didn’t know which DS/algo to use or couldn’t implement it properly.

There are other opinions out there, of course. Aline Lerner, the CEO of interviewing.io sent a great email a while back where the tl; dr was that you should start mock interviewing as soon as you can do a Leetcode easy because it’s more helpful than Leetcoding on your own and it allows you to correct bad habits before you solidify them. I agree with both of these points, however I still think you should be able to solve most common easy and medium questions before starting to mock interview.

Finally, here are some additional coding interview resources that I found helpful:

Welcome to the Coding Interview, You Suck.

Grokking the Coding Interview

Elements of Programming Interviews

As an appendix, here are some sample issues and ways to resolve them:

Sample Issues & Solutions

I had no idea how to solve the question.

You probably weren’t aware of the DS/algo or couldn’t identify that this question could be solved with that particular DS/algo. Leetcode and focus on solving multiple problems in a row tagged with the same DS/algo. If you do this for most DS/algos, you will get a feel for the questions eventually.

I had some idea about how to solve the question, but figuring it out took way too long.

You need more familiarity with the different patterns. Do more Leetcode questions until you know the patterns well and immediately upon hearing a question you can map a few patterns to it. Then, time yourself to get faster. First, get easies down to 10–15 mins. Mediums in 20–25 mins. Hards in 30-45 mins.

I know the solution, but my implementation (writing the actual code) takes too long and I run out of time.

Go back to the section titled “Code In Your Sleep.” Make sure you can actually write the patterns down without having a question in front of you. You shouldn’t be figuring out the pattern as you write it (this might take between 5–10 minutes to finish the pattern). You should just have all the code in your head and just be printing it on the page (this should take you less than 5 minutes for all patterns including the more complicated ones like union-find).

I solved the question, but wrote messy & overcomplicated code.

This generally goes away the more Leetcode questions you do. For every question that you solve, go to the discussion tab and compare the top solution in your language to your code. It will probably look crazy because they are using so many advanced language features. You don’t need to code at that level but try to improve your code a little bit by picking up one small syntax technique each time. Over time your code will get cleaner and shorter.

I had to partially implement multiple solutions because I misunderstood the question, wrote code that was sub-optimal the first time, or didn’t think things through completely before starting to code.

This is a communication issue that you can solve by mock interviewing. When you hear the question, create an example input and verify with the interviewer what the output should be for that specific case.

Before you code, walk through your entire algo. If the algo is complicated, consider writing some pseudocode which might help you write the real code faster.

Do NOT write pseudocode before you get confirmation from the interviewer to implement your solution. That’s a waste of time and you should be talking through the solution out loud first.

Whenever I do a mock interview, the interviewer seems confused or annoys me with questions that are obviously answered by looking at the code that I’m writing.

If this happens multiple times, chances are the problem is you and not the interviewer. Before you jump into the code, make sure the interviewer understands the algo you are implementing by walking through a sample input and interactively transforming it into the output.

It could also be that the interviewer is unfamiliar with the language you’re using, you write code that is too optimized to be easily readable, or you just write unreadable code. As you write your code, pause and ask the interviewer if they have any questions more often, and be nice about it. Whether the interviewer likes your communication is just as important as the quality of your code. Also try to figure out if your code is hard for other people to read and if so, fix that.

I can solve questions just fine by myself, but as soon as I go into a mock interview I blank.

It’s probably nerves. Mock interview yourself at least once a day by solving 1–2 Leetcode questions with a timer, talking through your algo before you implement, and talking through your code as you write it. If you practice this enough, you should have a little muscle memory during the mock interview which will reduce the chance of blanking.

I often get the time/space complexity wrong.

Practice reading through past solutions or solutions as you’re writing them and try to figure out why the time/space complexity is what it is. This is especially useful for the discussion section as people will often state the time/space complexity of their solutions without explaining why. You should figure out why the time complexity is what they say it is or leave a comment asking for some clarification.

--

--

Aleexsan Adal

A million lines of code live under our applications, and I love studying them. OMS CS at Georgia Tech, Software Engineer at Facebook.