Welcome To The Coding Interview - You Suck
Welcome To The Coding Interview - You Suck
You Suck
This is my rant/vulgar opinionated guide about coding interviews that will instruct you on how to
prepare for them, the mental models you should use to improve at them and become a better
engineer, and, most importantly, why people just suck at them.
I will also explain how to not suck at them, the methodology I used to become competent
(henceforth known as the incredible concept of “git gud”) at interviews, and how to approach
interview questions in a way that makes sense to you.
This guide will not cover resumes or how to start the interview process, but I might write a
separate guide for that if there is interest.
I will assume you know the basics of how coding interviews are done (phone screen, on-site,
etc.) and what the overall process entails. I’m mostly going to talk about the nature of coding
interviews, what is being tested, how to study for them, and how to consistently pass.
I do not make money off of any of the resources I recommend (other than my own
services).
To everyone else reading this who isn’t trying to hire me, yes I know I’m an abrasive
butthole. Please stop emailing me. Pls don’t fire me kthx.
Due to numerous requests and a high amount of interest, I coach as well.
Book here:
https://www.echantech.com/book-online
You can watch prior coaching sessions for free on my YouTube channel:
https://www.youtube.com/playlist?list=PLTh5zOK8tL21kxJ67rDRo-EARDDy8t86V
And if you want to support my work and tip me, here’s my Ko-Fi
https://ko-fi.com/echantech
Quick Links
Sample Practice Diary
Studying Patterns
Evaluating Your Own Mistakes
Thought Delta Check List
7/21/2022
Added Mistake #9: Being full of shit. Because some of y’all are so full of it.
4/16/2022
Table of Contents 0
Studying 0
What Makes A Good Study Source? 0
Sos as Studying Topics 0
System Design 0
Why The Fuck Are We Asking These Questions? 0
What Is the Ideal Candidate 0
Closing Thoughts 0
How To Study (Grokking the System Design + DDIA) 0
L6+/Staff Engineer 0
Behavioral Interview 0
Intro 0
Example 0
How do you respond when you disagree with a coworker? 0
The Interviewer’s Questions 0
“Do You Have Any Questions For Me?” 0
FAQ 0
Intro: You Suck At Coding Interviews
It’s been two years since I released my first version of this guide and, during that time, I realized
that there are a lot of things that went over people’s heads—especially the raw mechanical parts
about improvement and the esoteric explanation of the process. This version will hopefully be
more streamlined and explain step-by-step what you need to do on an easily understandable
and more basic level.
The underlying belief system is the same: you need to git gud. You need to perform well
in rain or shine and in sickness or health. The easiest way to get an offer you want is to
deserve one.
If you’re reading this, chances are you’re in the middle of preparing for a coding interview, you
suck at them, and you’re looking for some quick tips to help you do well in two weeks or less.
If that’s you, you might as well stop here because this is not what this guide is at all.
This guide is an in-depth exploration on how to beat any coding interview, land any job offer at
any time, and become a better engineer in the process.
The process is not easy. However, faithfully implementing what is in this guide will be the
shortest way to achieve consistent and high-value offers. If you do this right, you need only
spend a hundred hours or so. And I don’t mean a hundred hours while you have Netflix on in the
background. I mean a hundred hours of crying, rage, and imposter syndrome.
The benefit of doing this right is that once you master the proper thinking, you will never have to
study again. You will also become a much better engineer.
Today, it is easier than ever before to pass the coding interview. The reason is that the
interview process is very well understood, mostly uniform, and a predictable exam. The range of
topics are broad, but most of the questions you encounter are similar across companies. A lot of
tricky and odd questions that were once commonplace are banned. No one company is known
for using a certain type of question or favors one topic exclusively. Microsoft brain teasers from
the 2000’s are a silly relic of interviews’ past. Heck, even 4 years ago, the questions and topics
were different.
For my very first job, I was asked to actually write the code for the producer/consumer
multithreading problem as a Java developer. The closest I’ve done to multithreading prior to this
was taking a computer architecture class. I had never before written formal multithreaded code
with a semaphore or a lock. These days, I think it’s very unlikely to ask this to a L3 candidate. At
best, you’ll probably be asked to do a conceptual rundown.
Yet, while studying for my own interviews, I found I was inconsistently performing despite the
wealth of resources at my disposal. I probably did over 100 problems and wasted a shit ton of
hours before realizing I was not getting any better at all. I had to rethink my approach to these
questions. This guide is just a culmination of all the efforts I undertook to consistently land
interview offers.
I’m writing this guide to help you save a good couple hundred hours of your life. And your sanity.
Less than four years later, I was the primary stakeholder in senior-level work for refactoring our
project’s architecture and the sole engineer working on that refactor.
At Apple, I was also active in the search for adding both junior and senior candidates to our
team. Yes, I actually interviewed people for positions above me. I failed several of them outright.
Even though decisions required a team majority vote, the result tended to be that if I did not give
a candidate a pass, he was not hired.
When I applied for other positions four years after starting Apple, I aced all my phone screens
and hit a 80% rate for on-site interviews. Currently, I’m one of the youngest engineers on my
team at Uber for Infrastructure, managing the developer experience of hundreds of mobile
engineers.
Not only that, I reproduced this success at a higher level with my clients. Want specifics? Here
are all my long-term clients’ results from 2021 (direct emails and contact information upon
request. I’d like to make sure that they are comfortable with being contacted).
● One client who got an upleveled L6 offer with eight years of experience. He boasts the
3rd highest salary on levels.fyi for his country. You can see my chat with him here.
● One client who got two L5 offers with four years of experience from Tesla and Box. You
can see my chat with him here.
● One of my clients even got COVID the day before his interviews and got a 100% hit rate
on Amazon, Google, and Uber. He managed to score a $250k offer with one year of
experience from Google, the company that’s most known to lowball candidates. You can
see my chat with him here.
I don’t know how people can say I don’t know what I’m talking about or cry that the interviews
are unfair if I can achieve those kinds of extraordinary results consistently.
One of my clients was handicapped by a deadly disease that shut down the world
economy and he STILL aced every interview. You complaining about interviews is the
equivalent of crying that a marathon is unfair while you’re getting lapped by multiple guys who
broke both their legs at the same starting line.
Either you would need to accept that me and my clients are lucky every time and you are
perfectly fine. Or that I am extraordinarily skilled and you suck. Maybe both.
Bottom line: I get results consistently. You’re allowed to disagree with me once you can get
those results for yourself and others as well.
Still don’t believe me? Feel free to tune in when I am streaming. You can even follow my clients’
progress in real time.
And I dare you to find someone who can coach clients to get a 70% success rate on all their
interviews. Not a 70% chance of getting at least one offer, but a 70% chance of acing ANY
INTERVIEW across ALL CLIENTS and ALL COMPANIES. Especially if its mostly FAANGM tech
companies.
Firstly, it increases your negotiating power when it comes to the job. The more offers you get,
the more you can convince the company to give you more money. The amount can be
anywhere from 20k to 50k, depending on the number. You can use levels.fyi to do the
comparison but the range between the ends and the mid for an L3 position is about 35-40k. It
takes you about two cycles for you to be calibrated back to your true level. Usually, these
meetings are every 6 months so this can mean that your pay will come back to your actual skill
level in a year at the earliest. Those 100 hours of study effectively works out to about
$350-400/hour. What other job could you do that pays you that much?
Secondly, it is not that difficult to pass these interviews. Google tends to be conservative so
you’re looking at a 10-20% pass rate at worst once you go on-site. That’s like getting a B in
school or getting into UC Berkeley/UCLA. Chances are if you can pass the interview at one
company, you can pass at almost any company.
Finally, working up the ladder is arguably much harder. There are people who have many, many
more years of experience on you who will be first in line for promotions even if you do a stellar
job. Everyone will be scrambling to take high impact projects or taking credit for things. You’ll be
playing a much different game than simply being compensated for your skill. If you’re a social
and personable type of guy, then maybe going up the ladder is for you. I am not.
But if you’re someone who likes to be rewarded for their raw skill and dedication, the interview is
just that: an exam of skill. Unfortunately, it's not about how good you are as a professional
programmer. Rather, it is an exam of how competent you can get at a game that has its own
rules/meta and tactics that resemble real world coding. You very rarely get to code up an
algorithm from the ground up and you aren't going to design all of Twitter in an hour. But like any
game, you can get extremely good at it and some of the core principles do transfer over.
Randomness and luck no longer play a role. It is entirely on you and how good you are. No
bullshit. Not excuses. Now isn’t that an extremely freeing thought?
For all intents and purposes, we will use the Google Leveling System to refer to engineer levels
with the approximate definition:
Try the two following questions. These are on the easier side of questions.
When I say you need to be fast, I mean it. You need to be VERY fast. Not “Oh, I could’ve gotten
to the right solution with a little more time.” No. You need to be “I got to the optimal solution with
five minutes to spare” fast. And maybe even faster than that.
With CS being ultra-competitive these days, you’re going to need more than simply spitballing
random information to try to get an optimal solution. Time is of the essence during these
interviews and every second is incredibly important.
The only way to get fast is to practice. Practice slowly and deliberately. The speed will come.
Question 1
/**
* Serialize and deserialize a general (n-ary) Tree
* A
* / | \
* B C D
* /\ /|\\
* E F I G H J
* |
* K
*/
A has children B,C,D
B has children E, F
F has child K
D has children I,G,H,J
C has no children
Solution:
Intern/L3: Deserialization and serialization (deserialization has to be mostly there, maybe a bug
but the idea behind it should be correct).
L4/L5+: Fully working for both. L5 would be more likely to write out something similar to the
above because it is cleaner/simpler (also smaller string size) than the alternative of writing out
the number of children and inserting a delimiter between the values of interest.
For instance, what happens if you have 10 children at each level and you decided to include the
number of children in your serialization? Or what if your tree representation has millions of
children? Your string length would essentially double because you need to carry around the
number of the children whereas a bracket representation would be more compact and
consistent and scale with the number of children.
Question 2
Consider your smartphone. A view is something on the screen that is rectangularly bound that
has a x and y coordinate with a length and width. Views can be children to other views. For
instance, in a list, the list view will have item views as its children.
Any view on the screen can overlap any other view and any touch event is intercepted in Z
order. The view that is overlayed on top will be given a chance to intercept the touch event and
consume it. If it does not consume the event, then it is passed off to the view underneath the
view, and so on.
a) At a high level, how would you implement a logging system for a view hierarchy system?
b) Write the class definition and algorithm for finding the most frequent sequence (not
subsequence) of view navigation. Assume that you are given the sequences as lists of
integers that represent view ids.
c) Define the view class with the aforementioned information. Write an algorithm that finds
all the possible views in order that can intercept a touch event at a particular x and y
coordinate.
Solution:
a) The logging system would be a singleton throughout the entire application since it does
not make sense to have multiple loggers and we would want to funnel all the information
into a single source. It functions as a secondary system to the primary role of the system
so it can run on a service in a separate thread while the main application runs on the UI
thread. It's not a huge matter if the logging fails because the end user should never
notice. On a scale of 1-5 with 1 being a top priority, from a UI development standpoint, I
would rank it as 2-3. It is not a core feature of the application and if it breaks, it should
not visually interfere with the user navigating around the application. From a business
standpoint, this can be different. For instance, if we are logging plays for Spotify for
paying royalties, this will be a 1 and may require tighter integration. However, if such is
the case, it would most likely be tied into the model objects and therefore should not be
tied to the views themselves since you will be sending off the IDs of the objects (ie.
songs) rather than the IDs of the views themselves.
While metrics are important for user engagement, it's more important that the user
interacts with the application crash-free, smoothly, and with all the features intact.
Logger.getInstance().logClick(View v)
The path that would be constructed for the most frequent sequence of views can be
done from a trie. Why a trie over a tree?
Consider the path 1,2,3,4 and 1,2,3,5. Both paths share 1,2,3 and we can efficiently
store these values as a trie as if it were a common substring. Technically, a trie is a sort
of tree but we’re just splitting hairs if we want to argue the definition. But hopefully, you
get the idea.
b)
c) Breadth first search. It is more reasonable that the hierarchy would be assembled with
the larger view containing the smaller view. Therefore, the larger view should have a
chance to intercept the touch event before its children does.
Consider a banner at the top of a page with multiple elements inside of it like your
Facebook banner with a profile picture. You would want the outer container to have a
chance to intercept your click (like in the case of editing your profile) before handing it off
to its children.
The condition to add the view to the queue is whether or not the touch point is inside of
the view rectangle. This is a simple exercise left to the reader.
Intern: Would not ask the first part. I’d probably just give him the logging implementation and
ask him to write a trie search for the most commonly occurring path.
L3: Some consideration of the first part of the question. May not mention the singleton pattern
and may keep creating multiple logger instances with a builder-like solution (maybe creating a
logger that only logs certain events based on hard-coded configurations). Expect the second
question to be fully implemented and working.
L4/L5: Should absolutely nail this one. I don’t think there is a more optimal solution but email me
if you can think of one. Would be concerned if did not mention the singleton pattern. These are
just simple BFS and DFS.
L5 should also mention how to make the logger more extensible without an interviewer asking
him to discuss this possibility.
Question 3
Design Yelp/FourSquare Check in System (L4+)
Solution:
See the attached screenshot. Your answer should come very close. If not, the alternative should
be well-reasoned.
L4: Should be able to glue a basic end-to-end system without much consideration for the
tradeoffs. Approach may not be systematic, but the answer should be close to the proposed
one.
L5: Should be able to systematically approach, define, and derive the problem and solution.
Every step is logical, self-contained, and easily understood. Should be closed to the below
solution. If not, then you should be able to reason how the answer was derived, why it is
right/wrong, and how your solution is better and why.
L5+: Should be able to talk about tradeoffs and scalability and pick the appropriate data store
and technical components. Should cover all of what L5 does. Should also immediately be able
to talk about developer productivity and how they will modify, maintain, and use the system and
how the organization accommodates for that.
Why The Interview Sucks
The industry has taken great strides to eliminate the “stupid” questions in the past 4-8 years and
much of my commentary in the previous guide is somewhat moot.
But the interviews still suck, just a whole lot less and for different reasons. Fortunately, it's
almost to the point where the suckage of the interview is almost negligible compared to the skill
of the engineer himself because a competent engineer has a better chance now more than ever
to ace the interview.
There are two disconnections in the interviewing process today. The first is that there is a
disconnect between what the average engineer/student does and a top tier engineer. And this
wisdom is obscured behind a vast amount of “information” that is perpetuated by people who
don’t know better.
What most people believe is that they need to know how to copy-paste code from
StackOverflow or recite facts from a textbook. After all, that’s what the average engineer and
student does.
But the reality is that to be a top tier engineer worthy to be hired, you must be able to do so
much more than that. You must be able to move beyond the common algorithms and knowledge
and learn some non-standard yet important techniques. And this poses a problem because this
non-standard area is quite varied.
Let’s take a very simple example: Maximum sum of subarray of size K. The naive solution is to
iterate through every index starting from index to index + K and sum up everything. The optimal
solution is to just use a sliding window of size K.
Wait, sliding window? HUH? What college textbook is going to talk about a sliding window
algorithm?
Even if you didn’t know it, the insight you needed to derive it is to reuse a portion of the previous
sum. How were you supposed to know to look there? How do you derive that on the spot?
Don’t get me wrong, sliding windows are the basis for rolling hashes and are incredibly useful.
It’s how text matching is done in such a fast time when looking up strings in a book. My issue is
that you would only know about these if you either read a lot of research papers or had
access to them in the resources recommended by the recruiters (which they are not).
Fortunately, the industry has moved away from these obscure patterns and more towards more
solid interview problems that are more flexible. For instance, this red tape blue tape problem
has a pretty clear answer. But what people miss is that the approach itself must be clean, clear,
and logical (ie. how many different ways can the tapes overlap and what will you do about
them?). This consideration and approach is what a top tier engineer does on a regular basis.
That’s what people really judge when they’re looking for good engineers, whether they know it
or not. Consistent, clean, and logical approaches. Yet, this cannot be properly pointed out
because seeing someone who can do this is more art than science. After all, how “logical” is
logical? Does the person make jumps in logic? Does “clean” mean that a non-technical person
can follow along? Does consistent mean that this approach will work for any other system?
There is too much ambiguity.
Companies have a major disconnect between what they say they want, what they mean, and
what types of engineers they really need.
“Consistent, clean, and logical” is way too subjective a standard for companies to use as their
official standard. After all, what is logical and clean to one person may not be to another.
Esoteric language and fast-paced discussions can make a skilled engineer appear weaker than
they really are if the examiner does not have the proper vocabulary or considerations to begin
with. If a compiler engineer talks about desugaring and dexing to a product engineer, they are
effectively speaking two completely different languages. While this exact scenario would never
actually happen in a real interview, you can begin to see how and why small and different
perspectives change drastically based on a person’s background.
“There is no truth. There is my truth. There is your truth. But the truth is subjective.”
If that is the official policy and stance of a Facebook executive, how can you develop a
company-wide objective evaluation around a person’s skill, especially when the person’s skill is
subjective? On one hand, you don’t want to get sued for discrimination so you standardize
everything. On the other hand, proper evaluation of another person’s skill goes beyond a simple
checklist.
This is where corporate standards become somewhat muddled but are not entirely incorrect.
Companies will say that a person must be able to make tradeoffs, that a person should be able
to think through the problem, that the person should be expressive and have good
communication, etc.
But these are all approximations of what they really want. At the core, they are all symptoms of
having a clean, clear, and logical thought process. But the company interviewer will most likely
just be checking off a company-approved checklist and keeping his own subjective evaluation to
a minimum. Companies want to hire the best, but cannot describe it.
The irony? Most companies don’t actually need to hire the best!
Because, funnily enough, most people are right! Most companies just need code
monkeys to copy-paste code! The architecture at top tier tech companies is well thought out
and created. All they need are people to write terrible, disposable code and ship disposable
products, yet they hire as if they’re looking for the next Einstein.
In other words, what a company says they want, what they think they want, and what they
actually need are different from one another.
This means that the interview is more easily understood but the goalpost has been confused by
the gatekeepers. In the past, simply answering the question right with a vague approach or
something that sounds remotely correct would have gotten you a free pass. Now, this is much
less the case.
Fortunately, this is where this guide comes in to show you how to ace the interview consistently.
But let’s say you don’t actually believe this point. Let’s say that the interview is purely random.
You know what the good news is? The less you believe that the interview tests your abilities as
a software engineer and the less it is tied to the experience you have, the easier it is to fake
being qualified. If you’re going to bitch about how bad the interview is, you need to accept this
fact.
Because even if you need to memorize a few useless algorithms, the interview
fundamentally tests how you decide which one to apply and why. This is absolutely true
and I will prove this later on. The whole purpose of the interview is to test how competent you
are as an engineer.
Maybe I haven’t convinced you. Maybe you’re still gung-ho about “LeetCode interviews are
broken and don’t test anything.” Ok, so then it should be easy to game. And if it is a lottery, you
can literally get an offer by not studying! See how well that works!
The less the interview tests your actual skills as an engineer, the more it must be an
examination of some other irrelevant skillset. Therefore, getting good at that irrelevant skillset
will make the interview yield a false positive. So go study that, whatever you believe it might be.
Then once you realize you’re just plain fucking wrong, come back to me.
In short:
“You’re such a crappy engineer, how did you get such a high offer from x?”
“I tested well.”
Because of the aforementioned issues with the interview, the current state of them, and the
flooding of people into the “information selling” space, a majority of information is relatively
useless. Or are at least 5-7 years behind the times.
For example, take a look at this post from an interview prep website.
Someone please tell these guys that Buzzfeed-style listicles died 5 years ago.
I don’t agree that the interview has changed so much that it warrants the existence of all this
new preparation material. Not to mention, how can you have an industry with so much more
information and so much more content, yet people are still failing at higher and higher rates? It's
just a flood of bullshit information that seeks to confuse people, not to improve their ability to
execute. Truly amazing. The software information selling industry, mostly ran by software
engineers who are professionally trained to actively solve problems, has managed to create a
problem where there is none.
This “problem” exists because a big majority of the coding interview prep books and
material out there just sell recycled information, regardless of the quality. The founders
and creators of these products themselves have no idea how to execute on this
knowledge.
Most of these resources are created by sophomores who are thinking they’re boss lords
(brownie points if you recognized that as a Pusha T line). Do a lookup of how many of these
interview prep platforms and companies are founded by people with 2-3 years of experience in
tech before starting a business.
So what kind of content do you think someone with two years of experience is going to put out
there?
At best, they are problem sets designed for a novice who is getting their feet wet with interview
practice. But they do not do a good job at teaching people how to master the coding interview.
They just talk about basic CS topics, walk you through some problems, and let you run off on
your own. They’re basically a glorified test bank and simplified college textbook.
Allow me to prove my point with Cracking the Coding Interview (CTCI). Because I think it’s fairly
mediocre as a study resource and it's pretty representative of the low quality resources that are
out there.
Cracking the Coding Interview was published in 2008. The author, Gayle McDowell, worked at
Google from 2005-2008. Prior to that, she only had internship experience with return offers from
Microsoft. This means you are listening to someone with just two years of coding experience tell
you how to master the coding interview.
Holy fuck. Why the hell would you ever listen to someone who has not even three years of
full-time coding experience on how to ace every coding interview from junior to senior-level and
beyond? Sure, her track record has shown she can crack the interview at an intern level. But I
don’t think that should qualify her to put out a book that claims to know how to ace the interview
on an industry-wide level.
To me, the book is just a “first mover’s advantage” in the information selling space. Nothing
more. It's just a watered down textbook and with the bar of entry so low for information selling,
it's only power is its brand. Its actually fucking garbanzo beans when it comes to effectiveness
on its own and this guide (the one you’re currently reading) is worth more than that fucking
book. Heck, even fucking toilet paper is better than the book.
Are you kidding me? The “industry standard” was written by a junior engineer with not even three years of professional experience?
In all fairness, I don’t believe Gayle is the worst in the industry. Actually, she is an excellent
business woman for having achieved this and at least that aspect is to be admired.
But the worst are the “gurus” and “influencers” who get a single FAANG internship, then
proceed to get nowhere in their careers. They don’t ever land another FAANG job out of college
or have an extremely low success rate at interviews. They never get promoted or improve. They
only know one way of getting into a job: sending out a lot of applications, getting rejected on
multiple phone screens, failing the majority of their on-sites, and then getting only one job offer.
The point here is that what I find most disingenuous about this entire practice is that a lot of
resources hand-wave how to actually be successful in the process. They never say how to
study or how to practice for the coding interview. They just say to study topics, practice
problems, watch the videos they give you, and that if it fails, it was just a bad day and you need
to study more.
I’m of the opinion that anything that has value can produce consistent results. That’s
what I’m here to do. I’m here to make sure you can consistently get offers. And I don't
believe luck should EVER be a significant factor.
Sure you can say 100,000 people have bought your product and claim that 1,000 people have
received offers. But come on, do you really think you’re doing the world a favor with that 1%
success rate?
I’m not saying that every “guru” is a fraud. Nor am I saying that only senior engineers are
capable of writing a guide on how to pass these interviews. After all, if they are getting a FAANG
job, they’re doing something right. But I guarantee you a terrible guide from a competent
individual is 100x better than a well-written guide from someone who only got one internship or
one job offer. More often than not, chances are the senior engineer will write a better guide than
someone who only passed a FAANG interview once.
Just make sure that the guy you are listening to can consistently land offers and doesn’t wave
around their one-hit wonder of a success as a mark of someone who knows what they’re doing.
I could go in-depth into my issues with everyone marketing themselves but I digress.
Because it's not about knowledge. It's what you do with the knowledge that matters.
Bitching and moaning will not help. It’s like blaming FAANG companies for being incompetent
because they won’t hire you. The reality is that you’re not showing you’re competent enough for
them to hire you.
And maybe the reason for that is maybe because you ARE incompetent at showing them and
you don’t know you are incompetent. If you aren’t consistently landing job offers or doing well on
the technical interviews, you suck. Plain and simple.
“The incompetent cannot know they are incompetent. The skills you need to
produce a right answer are exactly the skills you need to recognize what a right
answer is.” – David Dunning
This quote by the guy who crystalised the Dunning-Kruger effect explains why I think it's
basically pointless to review your own efforts or have your friend of similar skill give you
interview feedback. Neither you nor him have the insight or skill to actually self-criticize properly.
It is also for that reason an incompetent individual who cannot consistently get results is not
qualified to give tips or write a technical interview prep course; they do not know what actually
works because they’ve only done it once.
At best, you’re just going to guess and test approaches until you find a hit. There are better
ways of improving and it starts by learning from people (or resources) better than you. Which is
why you’re reading this guide.
With that out of the way, here are the common mistakes I see with candidates when they are
practicing. I guarantee you that if you are not getting offers consistently, you’re making at least 1
of the following mistakes. These mistakes can range from your practice habits to your approach
to the actual problem.
If you read any section in this guide, it's this one. Read this section. And reread it. And
again. And again. Every. Mistake.
Because the easiest and most simple way to win at anything is to not make any mistakes.
Mistake 1: Not Putting Enough Time
We’re literally talking about changing your life and making an extra six-figures a year. For the
rest of your life. You should be absolutely willing to put in extra 20 hours a week at minimum for
3 months to do so.
“I don’t have time” is a very weak excuse. I’m not asking you to dedicate 10 hours a day like
entrepreneurs or professional athletes. They have to do that on top of playing their actual
professional lives. I’m simply asking on average for 2-3 additional hours a day.
I don’t care who you are. Kid at home. Significant other. Whatever. You can find a spare 20
hours a week for three months if you want it badly enough. Literally it's the same amount of time
it takes for you to go to the gym, work out, and come back.
If your doctor told you that you needed daily exercise or else you were going to die, you’d find
the time. I would argue making an extra 100k every year (before compounding, inflation, etc.)
for the rest of your life is a pretty close second. And I'm asking for almost the same amount of
time.
And don’t half ass this. You need to put in your full concentration during those hours. Focusing
on how to improve your problem solving and approach. Buying a course and doing nothing to
apply it is an excellent way to waste your money.
However, if you are spotty and all over the place, that changes. If you breeze through the on-site
for one company and fail the phone screen for another, you’re doing something terribly wrong.
The only way to overcome this is with habitual practice such that your approach to one interview
will work for all interviews. If you want consistent results, you need to have a consistent
approach.
To do this, you do not have to reinvent strategies that have already been invented or grow it
from the ground up.
Instead, you should use the consistent approaches, practices, and habits that lead to success. If
you want to always get the right answer, you should copy a systematic approach that gets the
right answer. That way, you can replicate this approach no matter what the problem is.
In fact, later on in the guide, I’m literally going to give the step-by-step to you.
Let me be clear: memorization of answers is actually less effective than ever before.
This is the case because of the dynamism of the interviewers and the trend towards more
abstract questions with less clear answers or a less clear path.
So anyone who thinks that it's all about memorizing LeetCode questions is completely wrong or
outdated by about 4-6 years. Feel free to fight me on this by memorizing the 400+ questions on
LeetCode and see how many offers you get.
If it was that easy to simply memorize answers, not only would you be scoring offers left and
right, you could theoretically score an offer far beyond your level simply by studying the right
material.
Let me know when a junior engineer gets a staff level job simply by memorizing questions and
answers. I’d love to watch the codebase meltdown because his “memorized” facts fail in
practice.
“In theory, theory and practice are the same. In practice, they are not.” -Yogi Berra
Deliberate and consistent practice is how people become good at something. You don’t luck into
being good. Without focusing on why you did not solve the problem and without an external
influence that is more competent than you guiding you (whether that is the answer key or
someone else), you’re just going to end up memorizing problems and answers with no ability to
actually evaluate problems.
Furthermore, if you just keep grinding problems randomly and aimlessly, you will lack depth and
understanding. Shifting your focus from learning from trees to graphs to searches back to trees
is a really good way to get confused and to miss out on the nuances of a particular data
structure or algorithm. The reason is that you’ll spend more time trying to remember these topics
instead of diving into the topics and figuring out multiple ways of parsing it. Focusing only on a
single topic for a good amount of time will make you an expert in a much shorter amount of time
than spreading your focus and jumping from discipline to discipline.
It is also for this reason that I honestly think the whole “I got my FAANG internship after
memorizing 100 questions of LeetCode” is bullshit. Put that guy through another set of
interviews from the same company and he’d fail. The same guy advertising this is that same guy
who gets rejected by Facebook but accepted by Google. That makes no sense: if you’re good
enough for 1 company but not good enough for another of equal or similar status, then
something is wrong.
There is an OCEAN of difference between doing 20 interviews and getting an offer on only one
versus doing 20 interviews and getting 20 offers. It's like some guy who can hit a bullseye 20
times in a row versus some guy who can hit a bullseye 1 out of 20 times: one guy is going to the
Olympics. The other guy is just going to be hanging out with his buddies with a beer at the
archery range. Who do you trust to teach you to get better?
You want to be the guy who is acing every interview. That way you aren’t ramming your head
against the wall wondering why you didn’t get the offer.
How can you possibly expect to improve if you never look at what you did right and wrong?
Absolutely mind boggling. You’re just going to repeat the same mistakes over and over again if
you don’t understand how and why those mistakes occurred to begin with.
Because, again, it's not about memorization. It's about application of knowledge, approach, and
execution. And most importantly, not being stupid about the mistakes.
This is why you MUST keep a practice diary. Here is a sample that I make every single one of
my clients use and it makes a tremendous difference. Note how meticulous and self-critical the
notes are. We will discuss later on how to actually review your own mistakes properly.
But the bottom line is that you must record your mistakes and understand what decisions you
made and why you made them. Understand if those decisions were right or wrong. And be
absolutely ruthless and relentless in making sure you do not make those mistakes again.
If one decision caused you to not solve the problem, then you better make sure you never make
that bad decision ever again!
I’ve actually found this to be the #1 reason why I drop clients: they simply refuse to do a
self-critical evaluation. They just understand what they did wrong and then think they will fix it
the next time it comes around. They are simply lackadaisical and don’t have an understanding
of what it means to be absolutely relentless in fixing mistakes.
Seriously, if you’re not kicking yourself over making the same mistake over and over again, how
can you possibly hope to improve? How does any basketball player hope to win games if he
keeps missing free throws and never corrects this? Really examine every thought and step you
make with a fine toothed comb. Everyone makes mistakes, even the best. If you find you’re not
making mistakes at all, go get that Google offer and throw away this guide.
I can tell you what to do. I can point out your mistakes. I can boil it down to a recipe. But I can’t
make you disciplined enough to fix your mistakes and bad habits. That’s on you.
If you’re repeating your mistakes, you aren’t fixing your mistakes. Plain and simple.
If you don’t care about your mistakes and say you’re going to do better next time, you don’t
deserve to succeed at all. Because your attitude sucks.
This one is not so obvious. Most of the time, the seeds of failure are sown long before you
actually fail. This occurs because usually you’ll pick a bad design or overlook some details in
planning. Because your code was written with a bad premise and misunderstanding, it will fail
test cases and therefore cause you to fail the interview. And you’re going to spend the entire
interview just debugging your code when you really should have been looking back at your initial
plan and proposal to begin with.
Bad planning (or not planning at all) is just one way you can get destroyed in coding interviews.
There are thousands of ways to trip yourself up. So why not plan out how to get around them
ahead of time?
The way to get around this is to really understand why you made every single decision you did.
Why did you pick a data structure or algorithm? What characteristics of the problem lent itself to
that. And so on. Every decision you make must be deliberate and intentional and only then can
you begin to understand how the chain of decision making created the mistake.
And usually it just boils down to one tiny bad decision or one missed step that starts a chain
reaction that causes failure.
What I see is most people trying to do everything at once and not fully understanding their own
solution before writing code. This never made sense to me. How can you implement an idea
you don’t even understand? How is your code supposed to work if you don’t even know how the
algorithm works to begin with?
The most common form of this I hear is when someone complains that they have “too many
ideas in their head.” Most people believe that great and fantastic ideas come from some crazy
and weird convoluted ball of reasoning that nobody in the world can figure out.
Bullshit. It means you don’t know what you are thinking. Which means you have no idea what
you’re doing. In reality, most clever solutions are actually born from a very few simple but
somewhat obscure observations and facts.
The highest form of an answer and understanding is one that is easily understood and simple
because its so difficult to misunderstand or screw up. If a child with no computer science
education can understand your solution, you are on the right track.
Someone might probably screw up making a cake. But nobody is going to screw up instant
ramen. God help you if you do.
Therefore, a great way to prevent coding before planning is to explain the solution as if you
were talking to a five-year-old child. If a child can understand it, anyone can. And if anyone can
understand the solution, it's much harder to mess up the execution. Simplicity beats complexity
every time and it creates a structure that allows you to modify the details as need be.
The second tactic is to draw and test every idea out on pencil and paper. Humans are terrible at
doing mental tasks precisely but are great at mental approximations. This is why algorithms that
are created in a person’s mind fails so often: their algorithm doesn’t have an objective nor is a
firm step-by-step solution. Or, they only work on approximate test cases and examples and fail
on others.
Drawing it out with shapes and figures and testing the idea forces you to not only demonstrate
the algorithm to the interviewer but also makes sure you understand what you’re doing. Not only
that, testing will go a long way in proving its correctness.
You will frequently hear me on streams asking people to do both these. And these are
absolutely a great mental tool to have in your toolkit, even outside of the interview.
Mistake 7: Magic Bullets
A lot of people try to solve the problem in one go. They try to jump immediately to the optimal
solution from the starting premise. Then wonder why their answer fails a bunch of test cases.
Jumping to the optimal solution immediately is very rarely the right answer or the right way to do
things.
Humans are not that smart. We are only able to think about one idea at a time efficiently and we
need logical, bulletproof steps to guide us from the simple ideas to the more complex ones.
Societal progress is very slow for this reason.
The reason why is because there are details that are usually overlooked. Even one or two small
details can blow up your entire plan. I’m sure we’ve all been there when writing code. Simply
missing a few test cases can completely derail your code.
Let’s say we make jumps in logic and skip steps on a problem and jump straight to the solution.
Then we can save ourselves maybe 5-10 minutes of planning and testing if we get the answer
right. But if we get the answer wrong (which is more likely), then we have to start over anyway
or waste time debugging and fixing things, often on the order of 20+ minutes. The risk reward
ratio isn’t there: why would I risk 20+ minutes just to save myself 5-10 minutes?
This is why, in the real world, we would never build a product to handle every use case on the
first iteration. No, we write a minimum viable product to handle the happy cases and then grow
the system to handle the weirder and less common cases. The time penalty for getting it wrong
is too damn high.
Just start with the simple naive solution that you know works, eliminate the inefficiencies, and
grow the problem to handle the next case while still being completely bulletproof for the previous
case. That way, you have a step you can fallback to in case your next step is wrong.
Maybe you do understand everything. Maybe you do understand how hash maps works. Maybe
you can recite time-space complexity off the top of your head. Maybe you actually know how to
apply it in a meaningful way. Maybe you solved your coding phone screens but then you are
wondering why they don’t invite you to the final round.
Good. Do it faster.
Where people make this mistake is that they assume that the coding interview is just like their
college exams: just memorize facts, recite facts, and then pass the exam. Except this isn’t
college anymore. It’s the real world and knowledge alone means nothing. You only get points for
execution on that knowledge. And this is where your approach and ability to parse the problem
comes into play.
When it comes to an actual problem, if you’re constantly spitballing random things to try to get a
solution, you’re probably in the large majority of interviewees who don’t know what they’re doing
and are trying random stuff. They just plain suck because they have no idea how to apply that
jumble of knowledge to anything.
The application of data structures and algorithms should be autonomous and second nature.
Similar to how you can talk without thinking about the words coming out of your mouth, you
should be able to apply your knowledge in the same way. Even when you talk about your past
projects.
If you have done your practice correctly, you should be able to run through all your list of
questions and observations to evaluate the problem very quickly and efficiently. There should be
no wasted effort, no pointless questions being asked, and every step you take should be
efficient and get you one step closer to the answer. Anything else beyond that is inefficient and
you can improve that.
This post on LeetCode does a pretty good job at explaining this concept of levels of proficiency
and is also a very good writeup of a candidate’s path to an L5 offer at Google. To quote the post,
the levels of proficiency are:
You want to be at the autonomous stage of proficiency when it comes to the whiteboard. But
chances are you are currently somewhere between cognitive and associative.
The goal is to get to a point where you can look at a problem and almost immediately
have 2-3 different ways of doing it and where one of the ways is the optimal solution. Or
you at least have an idea of how to get to the optimal solution. The actions and
evaluations you take should be autonomous and does not require a significant amount of
thinking.
Mistake 9: Being Full of Shit
Here’s a great way to test if someone is full of shit: ignore them for 10 minutes. If you come back
and haven’t lost anything of value, that person is full of shit. Great test to keep in real life by the
way.
I love this one because everyone thinks the key to everything is “communication.” No, that’s
wrong. Like anything in life, you need to communicate something of substance. Otherwise, its
not worth listening to.
I guarantee you that if you are even a few years out of school, this applies to you.
Let me give you an example: take a video of your favorite influencer and every 10 seconds you
watch, skip ahead 10 seconds of the video. I’m not going to link any video in particular to avoid
starting drama but I think we all know some influencer who fluffs a 1 minute video with 10
minutes of mindless commentary, especially those who are in the finance space.
Who would you rather listen to? I mean, people who are not full of shit tend to do well in the long
run and on average. The reverence for Charlie as both a brilliant investor, lawyer, and all-around
modern deity of wisdom for decades should tell you something.
My point here is that if you tend to ramble on and try to “communicate” your thoughts and you
do not advance the conversation, then your thoughts are probably shit. And since most people
are full of thoughts, ergo, you are full of shit. If anything, the fact that you are full of shit is a
symptom of the other mistakes and problems on this list. But ultimately, if you have a strong
logical framework for approaching problems and you learn from your mistakes, you should be
able to advance the conversation meaningfully and avoid this.
Despite its overlap, I just thought I’d add this one not just to poke fun at everyone. But also to
put a god damn mirror in front of y’all. Because some people need it.
Why the hell should I listen to someone who I can just ignore for 20 minutes and get the same
amount of information from?
Ok that’s enough of me insulting you. Let’s actually learn how to git gud.
Studying
Let’s talk a bit about some common principles that will apply, whether you are studying data
structures and algorithms or system design. This section will explain my justification for content
in general and the mentality you must employ when studying.
For instance, there will be A LOT of problems in a row that practice your ability to iterate over
two sorted arrays. There should be A LOT of problems in a row dedicated to binary search.
There should be A LOT of problems in a row dedicated to having leading/trailing Linked List
pointers. And so on.
Having all these in a pre-curated centralized location will save you lots of time because you
won’t be out hunting the internet wondering what you should be focusing on. This is why I
don’t recommend LeetCode until you’ve finished this guide! LeetCode is just too
inconsistent in their solutions and very lackluster in their explanations.
Don’t worry, you will make back that money in a single hour once you land your dream job. Just
make sure it’s the right one. And there are plenty of cheap quality courses taught by accredited
professors instead of your favorite tech influencer.
This will apply for both the Data Structures and Algorithms portion and the System Design.
Studying Topics
The way anyone improves at anything is by focusing on one topic over and over and over again
until they cannot make any mistakes with that topic. That’s what you need to do.
Let me repeat that. Focus on one topic at a time. Pick a topic/section and work on it until
you can solve every problem flawlessly!
That can be trees, hash maps, graphs, whatever. For me, my biggest problem was arrays.
There are so many ways to manipulate an array and I always felt that choosing one of these
manipulations was what was tripping me. Trees and graphs operate in pretty much a similar
fashion so I naturally felt ok at both of them.
Wait, arrays? They’re just a block of data. How can that be complicated?
What you fail to realize is that while you can know WHAT a data structure is, being able to
handle ANY and ALL manipulations of it is what you will be doing in the interview itself. And
there are so many ways to manipulate arrays. That’s why “studying” doesn’t really work in the
typical sense; you’re going to be asked to perform actions and executions over that data
structure. THAT is the test, not whether or not you know what the data structure is.
This sounds boring but believe me, this is really important because it will help you focus on the
right things when evaluating your own mistakes.
Let me first convince you of a simple fact. That it is simply better to avoid mistakes rather than
seek excellence. This isn’t just me saying this. It's also billionaire Charlie Munger, Vice
President of Berkshire Hathaway:
“It's remarkable how much long-term advantage people like us have gotten by trying to
be consistently not stupid, instead of trying to be very intelligent.”
If he can become a billionaire by simply avoiding stupid mistakes, surely you can get a job offer
by avoiding them too.
In my previous section, I have listed out the top mistakes I see. And there are many others out
there that people make that aren’t listed. Note that the penalty for committing those mistakes,
whether that is by studying or in executing on a problem, is extremely high and the reward for
taking these shortcuts is extremely low. Usually, the penalty from committing these mistakes is
failing to solve the problem and thus failing the interview.
Thus, we simply want to choose to study and approach problems in the most stable
fashion. We work off of principles that are proven to work and focus on minimizing errors
rather than choosing to express brilliance or flex genius level intellect to impress the interviewer.
Because believe me, most of you are not that smart.
When you have rules, you create discipline. When you have discipline, you get
consistency. When you get consistency, you can create and execute winning
approaches. We want to create a framework for consistently and logically solving any
problem given to us using a set of questions we ask ourselves and rules we follow.
Every time you study and review your mistakes, you will always be asking yourself how to
incorporate that into your approach.
In short, our mantra is:
1) Don’t make mistakes.
2) Minimize the penalty in case you do make one.
3) Don’t make ANY FUCKING MISTAKES.
Next, let’s address the big elephant in the room. The Dunning Kruger effect.
How can we improve ourselves, on our own, if the Dunning Kruger effect exists? That is, how
can we improve on our own results if we are not competent at seeing or identifying what we did
wrong. If we are so fucking bad that we cannot identify our own mistakes, whether that is writing
bad code or bad planning, how can we improve by looking at our own stuff?
This is where a good source of knowledge that can adequately walk through the solution comes
in. We assume that the source of knowledge is absolutely correct and our goal is to see the
results and get as close to it as possible. And it's even better if the source of knowledge walks
you through the logic so that you can copy it.
What we will try to do is reverse engineer the thinking behind the solution. So it's vitally
important that it's consistent at getting the optimal solution logically, consistently, and in a well
explained and understood manner.
And by copying its approach, we clone ourselves to become as close to optimal as possible. We
adopt different ideas, strategies, and questions into our thinking in order to better improve our
approach.
This also means we don’t blame execution or the final step. It's absolutely meaningless to say “I
should have been more careful.”
Why? Because it's vague and doesn’t really target a specific area for action.
My belief is that mistakes come from being put into the wrong situation or making a lot of bad
choices prior to the mistake. If you say something like “I should be more careful when writing” or
say “I could have executed better”, that’s not helpful.
How careful is careful? Should I retest everything over and over and over again? Why would I
do the exact same thing over and over again?
Again, there are many many factors that lead up to a mistake. And we want to eliminate those
factors with good habits that apply across the board.
Randomly doing things just to avoid one mistake or corner situation can lead to a lot of issues.
Good preparation makes avoiding mistakes easier and so we want to optimize the preparation.
The planning of the code, the design, the understanding, the testing, the code coverage. All the
areas should be scrutinized and thought about, whether that is being too slow or missing out on
something.
For this section, I will mandate that you pick up the following two materials: Grokking the
Coding Interview (Grokking) and Elements of Programming Interview (EPI). These two are
the best interview prep material I’ve seen so far and are the basis for this guide. And no, this
guide is not sponsored by any one of these books and courses.
You must complete at least 60% of Grokking before you go to EPI. DO NOT STOP AT
JUST GROKKING. GROKKING IS JUST STEP 1 OF THE PROCESS.
If you want to see me do an example of the below steps, please watch here. You will repeat
these steps for every topic you study. Fortunately, Grokking divides the sections up into their
own topic. And remember to stay with a single topic until you master it completely!
There are many algorithms out there that you won’t see in your textbook and are really only
useful for your interview (sliding window, 2 pointers, etc.) because they are not academically
interesting or worth researching. But your job is to master them anyway, such that it is second
nature. Unless your interview is scheduled for 2+ months in advance, don’t worry about
retaining it though. Once you’ve mastered it, your subconscious will retain it long enough for you
to actually do well on the interview.
The implication is that the algorithms themselves should become second nature. Writing a
binary search or DFS should be a breeze and you should be able to do it on command without
thinking.
For me, this means that I will rewrite the bare bones of the pattern over and over and over again
until I can do it automatically without thinking. And maybe a few more times after that.
No, that’s pretty much backwards. Think of an algorithm as a way of getting a desired
outcome based on a series of preconditions. Once you meet these preconditions, you
can apply the algorithm to get the desired outcome.
Djikstra’s algorithm is a way of finding the shortest path between two nodes of a graph. This
means that Djikstra’s algorithm can be organized as follows:
This means that if you see an undirected graph and that graph has positive weighted edges and
you want a shortest path, then you can apply Dijkstra’s algorithm. However, you must ensure
that all these conditions are met first.
Preconditions: Able to use lots of memory, cannot tolerate a O(n2) worst case scenario
Desired Outcome: Sorted list
Why do we think of things this way? Because it allows us to assemble solutions from the ground
up using very simple properties. That way, if someone asks you to justify your decision (ie. why
merge sort over quicksort?) you will be able to understand and explain why.
Ok seems simple enough. Now let’s try something that’s a little less textbook-y and more
interview-like. Consider cycle sort. It is an unstable in-place sorting algorithm that runs at O(n).
However, there are certain conditions that must be met before using this.
Preconditions: An array, can translate values into positional values, and values are sequential
Desired Outcome: Sorted array in O(n) time
In order to actually know what and why these are the preconditions, you need to actually
understand what cycle sort is and you need to break it down and fully understand each step.
Personally, when I studied this, I modeled this as a graph with connected nodes and positional
values such that they became a cycle. You must also be fully aware that O(n) time is the next
fastest time complexity after O(nlogn) which means you must know that the next slowest option
is something sorting-related (since most of them are O(nlogn)).
As you practice each algorithm over and over again, you should actively understand the
preconditions, how to write each algorithm, and the result you will get. The preconditions are the
most important, NOT the algorithm itself. The algorithm is important and you should be able to
do it enough times to write it off the top of your head. But it is so much easier to check these
conditions than to throw a data structure or algorithm at it.
You can either check if a graph is positively weighted and if you need a shortest path by asking
the interviewer or parsing down the problem which takes 15 seconds. Or you can try and
randomly throw Djikstra’s algorithm at a graph and waste five minutes.
Furthermore, some of these rules can be optimizations on top of naive solutions. Consider
dynamic programming and the Fibonacci sequence. The naive solution is to do the problem
recursively. An optimization of it is to realize you can reuse certain values and you cache
previous results into an external data structure. Finally, you optimize over that and realize you
can use a dynamic programming array where the dimensions of the array are the values that
change as you recurse into the function.
But it's about being able to see optimization on top of optimization that gets you there. Each one
of these steps is a precondition to the next step.
Data Structures can be thought of the same way. They are ways of organizing information
efficiently based on certain preconditions and very rarely should they ever be dedicated to a
single data type. Let’s take a trie. The characteristic of a trie is that sequential subsections of
each data overlaps.
Preconditions: Multiple values that share sequential subvalues, O(n) lookup time
Desired Outcome: Efficient storage of all values
If you apply this, you can do question 2 in this guide. Instead of thinking about exclusively
thinking about a trie as a way to store common partition of words, you can think of them as a
shared pathway.
That being said, this isn’t necessarily baked into the common definition of a trie but there is
nothing that says each of the nodes in that tree can’t be numbers or other values. Strings are
just one possible one, in which case you can think of a trie as sharing substrings.
Now, let’s tweak those conditions. Suppose that I tell you that I want a constant lookup time
instead of O(n). This leads us to a hash set which is faster but we trade off memory space for it
(consider why that is).
By doing things this way, we organically grow our solution from the smaller properties at hand.
That’s all these interview questions are: a set of beginning properties that you have to assemble
into a clean combination of data structures/algorithms.
As you master more and more techniques, you might end up using the wrong technique
because some of the preconditions/rules clash with older techniques you learned.
Let’s actually qualify this phenomena in a 4 stages of competency chart. You will start off at 1,
trying to figure out what the algorithm or data structure is about and why/how you should use it.
You will come up with a few rules that will be your hints to decide when to use the algorithm or
data structure. Then you try it out against your current problem, then the next problem and so
on. This is going from 1 to 4.
But your biggest problem won’t going from 1 to 4. You’ll be doing that several times over with
each new technique. The biggest problem is going from 4 to 1. As you learn the nuances and
patterns for each technique, you might end up learning the “wrong” thing or learn an incomplete
set of preconditions. While it might work for now, it may clash with other future preconditions. If
you rely on the wrong insight, that will make you apply patterns in the wrong way or at the wrong
time.
In short, the conditions you pick for each data structure/algorithm can overlap with others.
Going back to the cycle sort, you might incorrectly use it when it is better to use a heap if you're
looking at a sorting problem. You might forget that you need to have the elements be
translatable to positional values. Or maybe you didn’t take that into account.
For a trie, you might just misclassify it as a tree that represents strings and only strings since
that is the textbook way of teaching.
That’s fine. What is more important is that you can course correct quickly, especially during the
actual interview. The only way you actually course correct quickly is by encountering this
confusion in practice.
To mitigate and fix these, if you think you’ve come up with a new precondition(s), backtest it
against problems from the last five or so patterns you have done. Usually the problems are
grouped by the data structure they are applied to so it exposes you to more problems where you
might actually make that mistake.
Make sure it doesn’t confuse you or make you mistake the usage of one algorithm for another.
Most importantly, make sure that preconditions you use for the algorithm can consistently lead
you to the right answer.
As you backtest, make sure you actually understand what is happening and WHY. Understand
what the implications are for misusing the algorithm because sometimes that is what you want.
As an example, consider a problem like, “Find the missing number in the following scrambled
array: [0,5,4,1,2]”. While we can sort the array and iterate over it in O(nlogn + n), let’s try cycle
sort and get O(n + n) time. Since the preconditions were that the numbers were sequential and
directly translatable to positional values, it makes sense that cycle sort could be used. It might
not work but let’s give it a shot.
What you will find is that if you apply cycle sort to this algorithm, your array will look like:
[0,1,2,5,4] where 5 occupies the space of the missing position.
The point I am trying to make here is that you need to and should make mistakes in misusing
the techniques you learn. You need to try them against different problems because those
mistakes are part of the process as well. So embrace it.
Achieving Mastery
You only arrive at this step once you can do over 80% of the problems of a single technique
without any help. You know what nuggets of information to look for or how to strip the problem
down into what you need. You can “sense” the heuristics needed for the problem. You know how
to write the skeleton of the code off the top of your head without thinking (level 4 mastery) in the
most efficient way possible.
For you Java programmers, that means using Java 8 lambda expressions for comparators in the
constructors and abusing getOrDefault() and putIfAbsent() (consider why in terms of code
efficiency).
If you have done all the problems in the problem set and you still can’t consistently solve them
with the handset of rules you have, go back and do it again. And again. If you can’t derive a rule
for the technique, then at least memorize it and maybe a few of the problems AS AN
ABSOLUTE LAST RESORT (time constraint).
Are some techniques absolutely useless? Yes. Cycle sort is one of them. I would say you almost
never run into a real world situation or even a whiteboarding interview where cycle sort is the
expected solution because of how nuanced and weird it is. However, you should be aware it
exists at the very least and give it a try.
Why? What you will find is that because you are constantly evaluating problems to find
these preconditions and rules for the algorithm, you also practice your ability to break
down a problem. The more you practice breaking down the problem in the exact same
way, the better you will be at it.
This is what will save your interview 9/10 when the patterns you have committed to memory
don’t work. Not only do you have the patterns and templates in your mind that you can use and
solve problems very quickly, you also have your raw skill.
And all you had to do was do the same thing over and over again.
But now, what we want to do is improve our efficiency. Improve and avoid any mistakes we
might have made. Any incorrect thoughts, etc. How can we properly learn from our mistakes?
I’m going to show you how to optimize your problem solving structure. While the above basic
way of problem solving is still the basis, what we’re going to do today is to learn how to improve
upon it ourselves in a way that makes sense to us.
Note that this section doesn’t exclusively apply to EPI. If you find yourself struggling with
a particular topic, you can apply these steps to Grokking as well.
For the lazy, here is an example practice diary and a sample video of evaluating your own
mistakes. I have a playlist of me solving a few problems and performing this exercise of
evaluating my own mistakes here as an example. And here is a checklist for your practice diary
to make sure you are recording your mistakes and entries properly. But do read below for the
full step-by-step.
Prerequisites
Get a timer and an audio recorder because you are going to need to time and record your
problem solving. You don't necessarily have to be on a google doc. Pencil and paper do just
fine as long as you know at what time stamp you wrote what idea.
For me, I usually have OBS running recording my ipad/Google doc and my microphone set up. I
will have the timer shown similar to what I do for streaming but you can use your computer’s
timer or clock.
I also encourage you to speak your thoughts similar to how you might conduct an actual
interview. But most of your actions and thoughts should be on the paper. If you didn’t write it, it
doesn’t exist.
Plus, speaking your thoughts aloud is very helpful. If you watch my coaching videos, you’ll
notice I preach talking through the problem and running through examples verbally. This helps
the interviewer understand what you’re thinking and it helps you walk through your own thinking
instead of doing things in your head.
Third, and this is really important, you need to actually try to solve the fucking problem.
Seriously, a lot of people just try to look at the solution only. Don’t do this. Actually take an
earnest attempt at trying to solve the problem with your systematic approach. Don’t be fucking
lazy.
Finally, once you have had a good run at the problem, then we can begin the actual evaluation.
You have your attempt at a solution, your record of how much time you took, and then the
sound.
Step-By-Step
With that, we’re ready to begin. You have your attempt at a solution and the recording of
yourself.
What you need to do now is really dissect every decision, every thought you had when solving
the solution. And this comes in the form of a bunch of questions you need to ask yourself. That’s
really all this process is. Watching your own replay and obsessing over efficiency.
You would be so surprised as to the number of times I’ve heard people tell me that they don’t
even know what they were thinking. If that happens, then chances are you had no direction
when solving the problem. If that was an actual interview, it would be an instant fail.
At no point during the interview are you allowed to be in a position where you cannot explain
your ideas, solutions, or direction to a five-year-old child. Simply being able to do this is a great
way to focus in on what you need to do and help the interviewer follow your thinking.
Can a five-year-old child understand what I am doing at any moment in time? And can
you convince a five-year-old child that the idea works?
This has a lot of implications. First, you should make sure that the most important ideas that you
think about, test, and say should be written down. Nothing more, nothing less. If it didn’t make it
to the paper, it didn’t exist. This heuristic will help you from rambling aimlessly as well.
Second, make sure that your tests are explicitly tested on the paper. No idea should ever be
committed to without a proper runthrough and testing. If there is no test plan or testing that was
written down, it does NOT EXIST and you must correct this.
Third, you must be able to draw your solution out step-by-step using a stack trace. You can find
an example of me doing this here. But you must be able to walk through the idea, say explicitly
what the value of the variables are at each step, and draw out at what step in the function you
are in.
Firstly, compare the difference between the optimal solution and your solution. Walk through
how you got to your solution (or your attempt at one) and compare it to the steps the optimal
solution takes.
Because we emphasize a good source of information, we assume that the optimal solution in
the book is the best one in the universe. And we want to try to get as close to the optimal
solution and execution as possible.
Every little decision that you made vs what they made should be compared. Its all about
emulating the steps and trying to figure out why they took the steps they did and how you could
have avoided some of the mistakes you made.
What were the steps that the optimal solution took? Did I try to explore those? If I missed
an idea that the optimal solution had or a step, what would it take for me to recognize
that is the next step?
On top of that, when looking at your own solution, you should be asking yourself:
Were there thoughts and paths you took that didn’t lead to anything? How can you avoid
these? What did you need to see first and how can you have a better intuition to see that
insight?
Or if you didn’t get the solution at all, what were the insights that you needed to even get
started? Is it logical and simple that anyone can figure out the step that you got stuck
on?
And, as a big point here, very rarely is an optimal solution born out of thin air. Usually there is a
logical series of thoughts and steps that can be built on top of each other to get to the optimal
solution.
So you should try and look for places where you tried to do some weird hail marys, where you
tried to throw random stuff at the problem? Did I try to yolo the code? Did I try to do a magic
bullet? Could I avoid this? Is there a logical train of thought that can prevent me from doing so?
Once we have that down, the next thing is to look at your own replay. I recommend watching
this at 1.5x speed and record the timestamp at which you express certain thoughts.
3) Time Efficiency
Now look at your time stamps. Look at where you spend a lot of time. What thoughts stalled
you.
What did I spend too long on? How can I avoid wasting this extra effort and time? Was
there extra thoughts that did nothing? Did I overemphasize this set of thoughts? What
did I not spend enough time on? Could adding more time here help prevent mistakes that
I made later? Should I invest more time into this area to ensure I am at a lower risk of
mistakes later on in this step?
This part is tricky because too many times, people have a habit of cutting corners. So for this
part, you should be asking yourself how to improve your time without sacrificing the quality of
your design or planning. You want to eliminate inefficiencies without increasing your risk factor.
It's about seeing how much time you can reallocate from one area to another to ensure that
there is less risk, less chance of mistakes, in other areas. If you invest more time into design,
you will have less risk of writing buggy code. But there is a point of diminishing return as well.
If you cut too much time in design though, it can come at the expense of your code becoming
sloppy and buggy. So this part does require a bit more finesse and practice.
4) Code Optimization
Finally, after all that, we examine the code itself. Because we follow the philosophy that good
design leads to good code, we always critique the steps leading up to the code itself. Once that
is clear, we can examine our implementation.
Why did I write this bad line of code? Why was this comparison wrong? Could I have
planned this better? What were the decisions that lead up to this bad code or bad line?
Could I have made a better decision earlier up in the chain?
What edge cases do I need to look out for? Could I have tested for this? Was this
obvious earlier on?
If there are too many edge cases, can I simplify my design and approach to be more
systematic so I don’t have to worry about remembering so many edge cases?
You see me do this in my own video on how I study. I state how there were some execution
mistakes in my design or that I wrote things too sloppily that confused me that lead to bad or
mistaken code. And its because I ask myself why I made the mistakes I did. I conclude that
simply organizing my own work and avoiding global variables would have helped to prevent me
from making the silly mistakes that I did.
Notice across all the questions, I don’t ever blame the execution. I always blame the steps
leading up to the execution. Like I said before, we focus on the planning and the setup.
So instead of saying vague things like “I should be more careful,” we should make these
actionable during our planning. For instance, actionable and proper observations and
improvements are:
“If I organize my space a little cleaner, I can avoid writing an incorrect comparison
operator”
“If I do a test by cases, my conditional statements will be cleaner and easier to manage
and independent”
“If I try to draw a logical line of thinking from start to solution, I don’t have to try
magically yoloing my code”
Each of these thoughts are based on avoiding the mistake to begin with. And if you have
enough factors in your favor, the execution and decision becomes so much easier. So do try to
answer the questions I have listed in this manner.
Make every answer actionable and target a part of the planning. Ask why things are the way
they are and what series of actions lead to the mistake and attack the easiest and most obvious
action.
And most importantly, write all your fucking mistakes and lessons all down. Record your
mistakes in a practice diary with the problem, the amount of time you spent on the problem, and
the lessons you learn from them. This will help you to remember your past mistakes and lessons
in the future. And be as detailed as possible!
That said, the final point I want to drive here is that this takes a lot of time. A lot. You can easily
spend 1 hour on a problem with self evaluation, self critique, and solving. A far cry from the 20
minutes you might be used to spending on doing a LeetCode problem, giving up, and looking at
the solution.
But I really encourage you to try and spend the time to do so. These lessons and feedback
really add up. You might learn the wrong ideas or lessons and need to correct them. But
eventually, you’ll find yourself executing almost flawlessly because you’ll have learned, iterated
over the bad ideas, and kept improving the execution.
Of course, if you are still struggling to do this on your own, you can book me for coaching. The
difference between you doing this and me is that I’m much faster at this and more accurate.
Not only because I know the answer, I am much better at identifying proper decision making
weaknesses a lot quicker. I’ve been through this process not just with myself but also other
people. And since the interview does adequately judge your decision making as an engineer, I
am also very well equipped to know what a good engineer does and what a bad one does.
Now, if you need a solid example of all this in action, I’ll link these videos here. In these videos, I
solve a problem and then I compare two solutions that my clients submitted. I ask myself these
questions and ask at each step what could be done better as if I had submitted the solutions
myself. I compare what the optimal solution was to what they are doing and judge them on their
efficiency and what they wasted a lot of time on.
TL;DR: You suck so just try to make sure you suck less with each problem you do and
don’t make the same mistake. After 100 problems, you’ll be pretty fucking decent.
How To Solve The DS&A Problems
So now you know a bunch of algorithms and data structures off the top of your head and can
instantly pattern match the obvious and easy problems (ie. Combine 2 sorted arrays). And now
you can review your mistakes and find out what you did wrong.
But now you’re wondering how to put it all together. After all, not every problem you solve will be
solvable by a pattern. So what should your initial approach be to solving a problem?
This section will give you a basic blueprint to solving any problem. Note that you will still need to
review and study the problems after using the study techniques described in the previous
section. This is simply a starting point that can be optimized using your own heuristics and
observations.
So here are the rules of operation. It's basically codifying all the vague tips and tricks people
have been telling you to do but you never really understood when to use them.
At any point if you violate these principles, note that you are making a mistake and you
must figure out why you ended up in that situation when you go to review your answer.
Approach:
Let’s dive into each one of these steps and see what they entail.
Where most people mess up is by skipping this step and trying to solve the problem in one go.
That is extremely difficult because there are often many corner cases that need to be accounted
for. Corner cases that can simply be avoided by handling the main case first in a simple manner
and then just adding one or two modifications.
If the problem can be solved with a pattern, you can easily apply it here. However, keep in mind
that if you can instantly solve it with the pattern, you must repeat the exercise with the naive
solution. The pattern you learned arises because of characteristics that stand out in an
inefficient solution. So you must be able to go back and recite the step-by-step of that as well.
2. If I cannot figure it out, let me generate a few examples. What are the patterns
generated from the examples? What will the algorithm look like? What is the simplest
naive solution?
If it's impossible to figure out right off the bat or if a pattern does not apply, you should then try to
mechanically solve the problem. This means simply trying a few examples, looking for
commonalities between the solutions, and then abstracting what works into a simple workable
solution.
There are various techniques that I use and you will develop your own with time. In fact, in the
previous version of my guide, I laid this out. I’ve omitted it here because the discussion is too
esoteric and deviates too far from the objective of this section.
The easiest and most simplest way of doing this is just going through examples, drawing each
of the examples out step by step, and looking for patterns.
Oh, and remember to explain what the solutions are and why they work as if you are talking to a
five-year-old while you are doing this. It will be much easier to nail down what actually works
and what is just your conjecture and imagination.
3. What are the short comings of this algorithm? Is it taking up too much time,
space? Or is the resulting algorithm and code messy?
4. For every inefficiency, how can I fix it?
Usually, your first (or even second or third) attempt at a solution is going to consume more time
and space than necessary. This makes it inefficient and you need to be able to correct these.
Consider solving a problem by iterating through a tree twice. Doesn’t that strike you as
inefficient? Maybe there is a way to solve it by iterating through it once.
There are many dimensions to inefficiencies: time/space utilization, repeated steps, code
cleanliness, etc.
For time/space complexity, here are the order of operations and their implications.
For instance, if you find your solution or part of your solution is N2, then you should try sorting,
iterating, doing a binary search, or some combination thereof to improve your solution as those
will be faster run times.
For repeated steps, simply ask yourself if you’re repeatedly iterating over the same data or
using up more space than you really need to. Usually, you can combine steps together for a
faster runtime.
For code cleanliness, this speaks for itself. It's the least important dimension but it's nonetheless
something to look out for,
Where most people screw this up is by not connecting it to their naive solution. That is, they
know they need to improve the space complexity but then don’t modify their original solution to
do so. Going back to the tree example, if you know you can solve the problem with 1 traversal of
the tree, maybe don’t quite abandon the traversal idea just yet.
Example
Given a number n, find the smallest number that has the same set of digits as n and is
greater than n.
1. We will be moving digits around and it doesn’t really seem like our simple math operations will
help. So we can think of these as an array of numbers and throw every data structure we can at
it. Heaps are glorified sorts and sorting doesn't help. There is only one array so we can’t do a 2
pointer iteration through two sorted arrays. We could use a sliding window since there is one
array but it is not immediately clear how it would be used, especially since it doesn’t appear we
need a subarray. It’s not immediately obvious where trees or graphs play a role here.
Permutations seems to be a very naive way of solving it. That is, we generate every possible
number, sort, find our original number, and then get the next largest. This forces us to use 2n
space and n2 time. Both of these seem extremely inefficient and gross but it's a solution at least.
Optimizing on this isn’t very clear. How do we know what elements to cut out? Is there really
unnecessary repeated work that we are doing? Alas, we arrive back at where we started and
need to start from scratch.
So we know that if the array is sorted in descending order, it is already in the maximum integer.
(4321) Let’s see if we can follow that logic: what happens at the point where it begins to ascend
again?
4123 ->4132
4321 -> 4321
32876 ->36278
32841 ->34128
Based on our examples, we see that the first digit that changes is the first element that is one
whose neighbor is greater is the number you want to change. You’d substitute that number for
the number on the right hand side that is the next highest. For instance, in example 4123, you’d
see that 1 is smaller than 2 and mark that for change. Then take 123, replace 1 with 2, and then
sort the rest of the numbers in ascending order.
4123 ->[41][32] (Corner case: looks like sorting 23 does nothing. Should replace with
descending sort?)
51234 ->[512][43] (Built off the above case. Looks like an edge case where if sorting after the
pivot does nothing, then just swap the last 2 digits)
4321 -> 4321 (No-Op)
32876 ->[36][278] (278 is sorted)
32841 ->[34][128] (128 is sorted)
That looks like the case and would work. Can we do better? Sorting requires O(n log n) so we
must find something that requires scanning O(n) or binary search O(log n) time since those are
the only times that can beat it.
Let’s go back to step 1. This time, this is a sub problem: how to sort a list faster than O(n log n)?
1. You should already know all your primary sorting algorithms cold and know that the average
case never beats O(n log n). We can never sort by log n so therefore we must sort by O(n).
Therefore, there must be a certain characteristic that allows us to be faster than O(n log n).
Cycle sort is a O(n) sorting algorithm but we don’t see a guarantee between position and value.
The immediate characteristic isn’t clear.
2. Let’s try pattern matching then. After applying our first step, we get something like
So our numbers of interest are 278 and we know that the numbers on the right hand side are
sorted but in the opposite order.
Let’s then rephrase are problem: instead of sorting faster than O(n log n) how do we reverse a
subarray?
1. If we reverse the subarray, the first element becomes the last and vice versa (first in, last out).
We can solve this with a stack or 2 pointers that advance towards each other and swapping.
Remember those preconditions? If you studied your stacks enough, you should know:
And voila, you just passed your interview. Just write the damn code now: first find the part where
the number sequence starts ascending from left to right and swap with the next highest number
on the right hand side. Then reverse the right hand side.
System Design
Before you continue, I will mandate that you pick up the following 2 materials: Grokking the
System Design Interview (Grokking) and Designing Data Intensive Applications (DDIA).
These two are the best interview prep material I’ve seen so far and are the basis for this guide.
And no, this guide is not sponsored by any one of these books and courses.
Note that you will NOT be asked System Design questions if you are applying as a junior
engineer.
The system design is a very difficult beast to understand, mostly because the average engineer
approaches engineering in a very braindead way. He writes code for the sake of writing code.
Nothing else. No problem to be solved. No objective. Nothing.
Sad really. But ironically, in my opinion, it's the majority of the “senior” engineers in the industry.
When you’re asked to build a product, what would you do? 9/10 engineers would say they slap
together code, glue APIs together, stack overflow everything, and copy a random tech stack
they saw online and ship it. Maybe they’ll add a few things like load balancing and configure and
nginx proxy because someone on Stackoverflow said to. All without truly understanding what
happens underneath and call it a product.
What’s wrong with this approach? Well, who’s going to maintain that product when you have
more users? Nobody wants to maintain dogshit code/architecture and certainly dogshit
code/architecture does not scale.
Scaling dogshit involves producing more dogshit, both figuratively and literally. I pity the poor
fucking dog who has to keep pooping that out though (again, use your imagination as to who the
dog is in the figurative scenario).
But doesn’t this approach sound familiar? It's basically what a lot of failing candidates do for the
algorithm portion of the coding interview: throw random patterns and data structures at a
problem until it works. There is no objective thinking.
And it's no wonder that people fail the system design for the exact same reason. They don’t
design a system: they create a product that “just works.”
So don’t sell me on that “system design is only about information and knowledge”
bullshit. If it's anything like the coding interview itself, it's about the application of that
knowledge in a meaningful way.
And when it comes to a nebulous and unknown system that has no real solution to them, this is
doubly true.
More often than not, I see candidates fail both the system design and algorithm for the exact
same reasons. If they only fail one or the other exclusively, it is almost always a lack of
knowledge. Maybe it's because they have no experience with certain tech or they forgot what
they studied in university long ago. That usually can be solved by simply reading more blogs,
reading the actual core code, playing around with a tech stack, and deep diving.
Why might this be the case? Because both system design and algorithm interviews are the
same at their core: you are trying to prove that your solution works and is optimal given the
constraints of the problem. And you grow that solution from a naive one and then optimize and
eliminate the inefficiencies. The only thing different is that you’re using a hashmap instead of a
load balancer. You’re using 2 pointers instead of Elasticsearch.
What system design requires compared to algorithms is much an appreciation for nuance that is
ultimately born from your decision making. There is no such thing as a free lunch and every
component you pick and choose to use will have benefits and drawbacks. The whole interview
is you wrestling with those decisions and trying to design something that can combat the
downsides you’ve chosen to accept.
As an example, if you want to make an argument for MongoDB or Cassandra, you need to be
able to appreciate the differences between the two when it comes to scalability, performance,
and a multitude of other factors. You also should at least have experience implementing 1 or the
other and have been bitten in the ass in some way since no decision or tech is ever perfect.
If you chose the wrong database, blew up your project because of it, you’re never going to
forget it or the reasons for it. All that will show through.
If algorithms is chess with a finite number of deterministic choices and moves, the system
design is Go where you negotiate territory and pieces between you and the opponent with
moves and stones.
Which is an excellent metaphor for the system design: you are negotiating with the problem,
accepting certain benefits in exchange for certain drawbacks so that you can best solve the
problem at hand.
This brings us to how one should actually approach the System Design interview. For now, we
will only focus on the classic System Design that consists of choosing components. But the
fundamental ideas apply no matter what system design type you are doing (ie. if you were
asked to design a system to deploy language translated strings to a mobile binary).
Let’s start with why we ask system design questions. Why would we ever ask a candidate to
build a theoretical system that they’ve never built before? Certainly most engineers have never
had to design a version of LinkedIn for 1M users.
However, most L5 engineers have seen the shortcomings of bad decisions when modifying
systems and, in general, should know what to look out for.
And at FAANG specifically, this is essentially what we’re working with. An engineer needs to at
least have a cursory understanding of what parts of the techstack can go right or wrong and
why. If he cannot choose the right way, he can at least reason about the 1000 wrong ways
because he’s seen them.
But you don’t have to be a devops or tech lead to have this knowledge.
As a product engineer, it most likely means navigating a code architecture and having a cursory
understanding of how the layers interact with each other. Knowing that you might want to battle
test a system by periodically injecting synthetic failing requests. Knowing how to handle sending
data back upstream in a microservices architecture and choosing an appropriate TTL. Knowing
how the data models interact.
While that all sounds super crazy and a lot, the fact of the matter is that it's relatively simple if
you have any understanding of how data flows end to end. There are a myriad of system design
interview solutions you can find on YouTube but I believe this is a fairly good example:
So the reality of the system design interview is that it's actually a behavioral interview: it's a
behavioral interview disguised as a technical problem. The behavioral part is the questions and
considerations you ask when trying to prove or build up your system.
How do you want to organize a particular subsystem? What are your options? Why did you pick
the component or design that you did? What are you willing to give up? And how did you justify
it?
The depth of your knowledge and the types of questions you consider will actually determine
what level you are. More on that later.
Consider 2 candidates: Candidate A proposes a solution that uses the latest and hottest tech
stack, stating the benefits of every component he is using. He argues that with such a robust
tech stack, it will be able to scale well.
Candidate B creates a solution that, while not optimal, addresses the primary pain points of the
problem and delivers the minimal function. But he doesn’t know what a NoSQL or MySQL
database is.
Some might think it's the first. After all, he really knows what he's talking about with all these
tech stacks. He has experience writing with these stacks. He knows what we need to write a
product!
The problem is that the tech stack or design might not even fit a person’s needs.
For instance, if I asked someone to use MySQL vs NoSQL or MongoDB and Candidate A just
said to use NoSQL because it scaled better, I would ask why. If you couldn’t answer that and
just say “it just does”, then you fail to understand the idea that “there is no such thing as a free
lunch.”
Because there is no magic wand solution. There very rarely is without a significant tradeoff.
What if your use case primarily needs to do a lot of relational joins and pull data from multiple
tables? The performance from MySQL would blow MongoDB out of the water. Doesn’t matter if
it “scales” if you can’t tell me how or why it scales. Or even if scaling along that performance
axis matters.
Yes, there do exist NoSQL databases that can effectively act like a MySQL. But those have a
subpar performance. It's akin to driving the Indy 500 with a Prius engine. Will it get you around
the track? Yes. But it's not going to do it well at all and once you hit enough traffic on your
system, you will start encountering the tail risk.
Candidate B would have considered the nature of relational joins and maybe come up with
some weird B tree-like structure for the underlying structure. Or maybe he would consider an
append only log for the high read/join volume. It doesn't matter. The point here is that Candidate
B has identified the characteristics of the system that are needed and prioritizes them. From
there on, he can research the thousands of databases available to him and pick the one that
aligns with the needs of the system.
Seriously, the number of proprietary NO-SQL databases in the world is so big it would make
your head spin.
Candidate A would just be chasing tech stack clout and not really understand what his system
needs. If you hired Candidate A, he would blow up your tech stack with shitty decisions.
Imagine hearing “I don’t know why it's slow! It's the database everyone is using” when you ask
him why the performance of your product sucks. Is this the kind of guy you want to hire?
Nothing wrong with using an old tech stack if it solves your problem sufficiently and well. Tried
and true fundamentals trump trends.
Remember what I said in the section about what is an engineer? You’re solving a problem. The
first candidate is the equivalent to a trend-rider who can only evangelize the popular and
potentially overrated. The second candidate is methodical and addresses real problems. All he
needs to do is read up on some tech stacks which change as often as the seasons.
In other words, every component, every model, every choice we make is done in service of the
problem or a subproblem. Which seems obvious but you know, some of yall need to hear it.
But the actions of the general engineering public run contrary to this. When everyone spams
“node js, fusion, redis cache, mysql database” as the solution to every problem, you begin to
question whether people are just rolling the same tech stack over and over again mindlessly or
are actually trying to solve the problem.
Granted it DOES solve the problem. But it doesn’t solve the problem in a meaningful way. It's
akin to building a house with cat feces. Yes, technically it's a house. Yes you can technically
build every house on a block out of feces. And yes it has walls. But it stinks.
In short, what separates bad candidates and good candidates is just 1 thing: logic as it pertains
to the problem at hand. But in that, there are a lot of meanings and we will discuss this further.
For now, consider that you can think of the golden solution as simply being able to justify your
decisions as much as you can.
Closing Thoughts
System design isn’t hard. It’s not unlike actually solving a LeetCode problem where every step
you take is meant to address the inefficiencies in a solution. Where people fuck up is in trying to
throw some predetermined tech stack at a problem and thinking it magically solves everything.
Those people have never tasted a catastrophic architectural failure from shitty decisions and
don’t understand the gravity of their decisions. These are the last people you should ever ask to
help run your system.
And yes, there are an infinite number of things to consider. The database, the software
architecture, load balancers, sharding, etc. But if you understand them at a high level and can
describe the arrangement/characteristics of the problem you need, you don’t even need to
describe tech stacks at all or name any proprietary technology.
Tech comes and goes. So I hope you hire the right guy who can analyze and understand the
right one to use. A company that doesn’t do that is a company I would short into the ground.
So let me repeat the two resources I recommend: Grokking the System Design and
Designing Data Intensive Applications. Use these in tandem.
Many of the same ideas in the section on How To Study Algorithms can be applied for system
design. Nothing really changes here. You start with a systematic approach and compare your
answers to the optimal answer suggested and fix your mistakes.
The only difference is that you have a lot more variables thrown at you and its important. You
can’t really just study databases in a vacuum: you need to actually apply it to really understand
it.
The best way to actually improve at the system design is iteratively and continually fix mistakes.
Look at solutions provided by a good source of knowledge like Grokking the System Design and
try to reverse engineer the thought process behind developing the system. Why did they pick
the components they did? What was the intention at each step? What was the pain point they
intended to deal with?
For any area that you don’t quite understand like sharding or synchronization, you should read
the Designing Data Intensive Applications chapter for that. Understand every tradeoff, factor,
and application listed in DDIA that went into the Grokking solution and try to reason why it was
reasonable or not.
I personally find it meaningless to read a dense 500 page book without applying it and with my
clients, I find it's very effective to consume maybe 10-15 pages at a time and really try to
practice those pages.
For instance, if I do not understand why a load balancer would be placed at position A instead of
position B, then I’ll go and read the chapter on it and apply it to the problem and solution I just
saw. It's not so important that I agree or disagree with the answer. It's important that I
understand or at least come up with a reason why the solution is to put a load balancer there
given all the characteristics of a load balance and why it's there.
You need to repeat this process until you’ve fully understood a good majority of DDIA and can
hold a meaningful conversation. If I wake you up in the middle of the night and ask you how
asynchronous replication and synchronization works for various configurations of
leader/leaderless/multi-leader databases, you should be able to tell me how they work.
Otherwise, practice harder.
And be patient! This alone can easily take you 3+ months if you’re starting from scratch!
This is where you actually craft your MVP, your minimum viable product. This is what will cover
your p90 case or so. Because no system is actually created perfectly or instantly off the bat, you
need to start somewhere.
This is not unlike the code or engineering process itself. You start with a very naive solution with
off the shelf components that will service a small set of users. You get your product up and
running and then address the concerns you run into.
The reason why we do this is to scope out the main flow of our system and where we may get
blown up. Where our problem areas will be. Where we should pay special attention. If this is the
planning stage, consider creating a suite of tools that allow you to scan the entirety of the
problem.
My personal favorite tools for this section are the CAP theorem, estimating metrics, and
outlining how user interactions flow throughout the system. These are your starting clues, your
initial scan of the system to see where you will choke and might run into issues. We want to find
areas of high traffic or where the data flow is not solved by an obvious solution.
2. What is our current pain point or unknown after making this decision? Address the
inefficiencies/problems that occur as we add more constraints or build out the system.
Because every system and design has their limitations and tradeoffs, we are forced to address
them. And this is an exercise we will repeat over and over again not just across problems but
within the same problem. As we scale up in users, resources, or some other activity like external
vendors, our system will eventually start failing or having issues in one or more areas.
At this point, we will update those areas. For instance, if I need to scale my database, I might
want to shard the data. If I am expanding geographically, I may consider replication. If I want to
handle queries in a better way, I may want to change from a standard MySQL to a NoSql
database.
But what drives these changes are a consideration of tradeoffs and how the system is being
used.
3. If we can solve the issue, what are the requirements/parameters of the solution?
We might know what we’d ideally like to do. The problem is that our solution needs to be
relevant to the matter at hand and we would like to be as intelligent as possible about it.
If we decide to use one solution, along what dimension should we use it? For instance, we may
want to cache some portion of the returned result. The question then becomes where this cache
should live, how long the cached data should live, and what our caching policy should be. And
all these factors and dimensions should be relevant to the end user’s behavior and how the
system will be treated.
A big mistake people make here is that they think the system should be robust to just handle the
initial problem and only the initial problem. What you really need to ask yourself is how the user
is using your system and what reasonable behaviors they may have.
For instance, it does not make sense to have a caching policy where the TTL is for two weeks if
we’re designing a instant messaging app as the cache constantly changes and people don’t
care about messages from more than three days ago. But this might be correct if we are
designing a news website like Reuters where people will look up recent news in the past few
days.
4. If we have some prerequisites, solve the prerequisites. Return to step 2 for each
prerequisite.
Of course, not every optimization is going to work out of the gate. You may have some prior
questions or blocking concerns preventing you from implementing the solution you want. In that
case, go back and repeat the exercise for that blocking factor.
Its more or less an extension of trying to unstuck yourself out of a problem or situation when
doing your data structures or algorithms. Work backwards and ask yourself what you need to
know and what is preventing you from knowing it until you can arrive at a simple matter of fact or
straight forward decision.
As you scale up, there are certain factors that will falter and fail inevitably. It might be multiple
writes to a database. It might be server load and need to replicate across regions. As your
audience scales, it necessitates these solutions.
Even at Facebook and Google’s scale where the fallibility of the processors themselves to fail
an instruction execution properly is a bottleneck; a problem that literally no other company is
handling. There will always be problems that need to be handled.
“But wait! I’d be revising my answer multiple multiple times in this case! There isn’t enough time
to keep iterating! And I can’t possibly know everything!”
Oh, contraire. This is basically what the engineering process is. You will start with an
assumption and that assumption will be wrong eventually. That’s inevitable because you aren’t a
fucking god (if you were and your name isn’t Linus Torvalds, then you can just Dunning Kruger
your way back to L3).
The fact of the matter is that for system design, a lot of the chain of decision making should be
kinetic, much like patterns. In fact, at higher levels of conversation and between people of
higher expertise, the conversation actually has a lot of implicit assumptions and goes by very
quickly and any clarifications can be drilled down extremely deeply.
For instance, if I talk about returning data to the user that requires a complex set of queries, I
can get away with mentioning offhand that the database is MySQL since there is a good chance
that simply doing relational joins is going to give me better performance. I don’t need to run
through the exercise of pain points since both parties already can already assume its going to
give the better performance. But such assumptions and knowing what to skip and what
decisions to actually dive deep into only come with experience.
The real question, like I said above, is in how you make those decisions. The branch points. The
negotiations. If I ask you why a component is in place, you should be able to provide a logical
answer based on factors and considerations, whether they are given to you or one you derive
yourself.
The only real difference between a top tier engineer and an L4 at this stage is how quickly they
can think about all these questions by prioritizing which decisions need to be made and already
having some idea of the answer. This usually happens because they are very well experienced
with it. This comes from experience but, as a general principle, the best decisions is almost
always the one that will give you the most options later on.
Just like everything in life, nothing comes for free. You are in a balancing act of complexity, time
to run, and size. And its not even that good to do the “here are 3 options, pick 2” meme.
If a system is massive and incredibly complex but runs fast, that system will die because its too
difficult to change.
In a similar vein, are there NoSQL databases that can act like MySQL? Yes. But using it in the
wrong situation will yield suboptimal results.
This is why being able to actually justify every decision is important. It shows your ability to plan
ahead and accommodate for the unknown and the most likely scenario. Nobody can instantly
know how to design a system from start to end. It is done by building pieces upon pieces slowly
and surely and making sure those pieces are flexible enough to handle the problems that
happen later on down the road. At the same time, they also perform optimally in our desired
situations.
And this need to actually build things one piece at a time is why we have such an iterative
process in engineering. And no where is this more apparent than in system design.
For your convenience, here are video examples of me walking through the system design.
Tinyurl
Realtime Like Counter
But here’s the text version for those who don’t want to watch a video. The problem we will solve
is “Design a Url Shortening Service”. The numbers and situations I will use here will be taken
from the Grokking the System Design for convenience.
User submits a url, we return back a hashed version of that url as the id of our domain url.
Ie. www.foo.com => tinyurl.com/abcde
When the user submits that url, we want to be able to redirect the user to the original website.
This requires us to have a DB.
Immediately we have some limitations to the problems. We need to know a few metrics that
require our attention.
We cannot possibly store every url in the universe. This can be a function of the size of the id
space. Which also leads us to what hashing algorithm we use.
In order to make room for more urls, we should purge by some rule, whether that is age or
popularity.
This is going to affect our read and writes and the server operations. We can even be more
specific and split the read and write operations separately.
Any other metric at this point is (arguably) irrelevant. This is because we don’t know enough
about the system just yet that we are building. As for additional features, they are built on top of
this so we can acknowledge them now (ie. user controlled deletion) but are not core to the
central feature.
This is also probably where a candidate might make the first mistake of naming a particular
hashing algorithm. They may roll MD5 off the tongue. But, as mentioned before, this means
you’re dismissing all the other hashing algorithms like sha256. If I asked why a candidate picked
this algorithm and they couldn’t at least list off the differences between at least four hashing
algorithms (time complexity, etc.) and say in detail how each worked, I would give that a red
flag. Why declaring a hash at this point in time is a mistake, we will discuss later.
Also we have the issue of hashing collisions but we can address that later. For now, let’s get the
bare bones on the table.
Let’s handwave some of the math and let's suppose you came up with (Note that the numbers
are arbitrary)
1) Let’s keep it simple and keep it to 6 characters, alphanumeric, upper and lower. (2*26+10)^6
combinations = 56GB for the ids pool. Every url is also going to have to be stored as well.
Approximate 500B = 15TB of url ids.
2) Let’s TTL by age of the URL.
3) 100:1 reads: writes. 500M urls shortened every month. So ~20k requests/second.
Now that we have the metrics out of the way, we know the scope and flow of data.
The most prevalent pain point is the # of requests/sec. 15TB of data is not bad in comparison. A
few trips to Best Buy will suffice.
This brings us to what the user can expect from us in terms of service. That is, what are the
problems associated with servicing a high number of requests?
We expect the service to, at least, have high availability, redirection should be done with
minimal latency, and the url should not be guessable.
With the guessable requirement, we may want to expand our ID pool and add two more dummy
bytes. This would increase our size to 200 TB or so. This would be the point whether we want to
ask the interviewer if this requirement is necessary, to discard it, or to dive deeper into our math
and see if we even need the dummy bytes. But this is also a tangent that can be tabled.
But we still need to address the high availability and low latency. We cannot answer these
currently because we need to be able to translate the url into its original url. And we cannot
certainly keep 15TB in memory. This means we need a database.
What is preventing us from fully coming up with a solution for these requirements, if
any?
The first two requirements (availability and redirection) both depend on what the
request/response contract looks like and how they are processed. Are there any crazy
processing/operations we need to support?
The requests to manipulate urls, at the bare basic level, should look like this
createUrl(url)
deleteUrl(hashed_url)
We can add more fancy options later like a custom_alias or a TTL. While deleting a URL is not
necessarily part of the system, it is reasonably expected. A user should be able to delete a url if
they own it. But let’s table that and come back to it.
So the requests look simple. There is nothing here that we need to consider as a blocking
factor.
Since the requests are relatively simple, the current pain point will be in the translation of the url
to url. That is, lookup to the DB. Another angle to look at this is to see if we need to shard the
DB as well as that can affect our solution.
If we can do this, we can also load balance the requests in some way. But this requires us to
know how we are going to set up the servers and therefore, how we will set up our DB backend
and sharding. Knowing this will allow us to choose the best policy for the LB.
What is preventing us from fully coming up with a solution for these requirements, if
any?
We must answer the preceding issue: what does our DB look like?
So what is preventing us from concretely coming up with a DB? Our data models: what kind of
relationship will they have? Let’s represent this first.
This is a suitable candidate for a MySQL DB. If we assume that we are receiving 20k read/write
requests per second and fills our high availability and minimal latency.
We could also go for NoSQL as well but this discussion requires a comparison of alternatives.
Again, don’t start naming databases unless you’re ready to have a deep dive discussion. But
you can at least define the characteristics of a good database.
Now, if we do need to shard the database, we can do this either by evenly distributing the urls
across databases. This can mean hashing the urls and sharding by alphanumeric groups.
Another potential alternative is that because some urls have higher traffic than others, we can
try to even out the traffic based on distribution. That is, the highest traffic urls will have their own
dedicated shard and the lowest traffic ones will be put on other shards.
For now, let’s assume the simpler solution: hashing the urls themselves that can resolve to a
particular db shard. If we find that this solution does not work, we have another one in mind.
Going Back
Now that we have this, let’s go back to our original: the requests. The replicated servers aren’t
going to be dedicated to a database shard. They could potentially be writing to the same db. So
let’s just have a round robin to even out the load between servers.
Now that we know how we’re going to be storing the urls and sending it back, the next pain
point is when we have enough traffic that our hashing may create collisions.
We could keep running the hash algorithm over and over, salting the url until there are no more
collisions but this could theoretically continue ad infinitum. There must be a better solution.
Let’s use a key pool instead. We will instead generate keys and create a pool of available keys
as ids. The server will withdraw from the pool and every time a url gets deleted per the TTL, that
key will be returned to the pool.
Now there are some issues with that. After all, if this pool contains all the keys that are used and
unused, we need to be very strict. That read and write should be maintained by the same lock.
After all, if a key is returned to the pool, it can be available for another server to use.
The pool will be limited in its capacity so as to not demand more keys than necessary. It will
keep some of the keys in memory and then keep the rest on disc. We can do some math here to
determine the amount but for our purposes, we can leave that detail out for now.
With all this said and done, we can now formally identify the error scenario. If a hashed url is
called and the url is expired, we send the user a 404 page.
With that, have we satisfied our requirements for have high availability, redirection should be
done with minimal latency?
Arguably yes. But we can do a little better. We can make the argument that caching should be
involved in order to reduce the latency time as well so that we don’t keep connecting to the
database.
Caching
Let’s use that idea about the most popular urls but instead of for sharding, we use it for urls. We
can have an in-memory cache that is akin to a hashmap and use a LRU eviction policy. As urls
become less popular, they get evicted from the cache.
When a url gets invalidated from the cache, we invalidate it in the DB and trigger returning that
key to the pool. This does lead to some weird issues where the key could be immediately used
again and redirect to a new url (leading to confusion with the existing url). But that’s a smaller
problem by balance of probability and we have bigger fish to fry.
With the basics set up, let’s tackle the cleanup. The last requirement.
Every time we receive a request that results in a expired url being retrieved, We can also purge
the url at the same time and send the key back to the pool. Ontop of this, we can periodically
run a service to query the database for expired keys.
And then we open the discussion for user login, management of urls, authentication, etc.
This is outside of the scope of the core functionality but this is worth at least mentioning.
Commentary
We have pretty much arrived at a similar answer as the Grokking folks (albeit with some help
from the math department) purely through logic. And all we did was just keep unblocking
ourselves as we tried to resolve the requirements of the system in a bare bones way,
acknowledging which were priorities and which were just bells and whistles to the core product.
This is just a simpler way of saying “what are the functional requirements and non functional
requirements”.
Notice here that at no point, do we commit ourselves to a specific technology until we know for
sure it fits our needs. Rather, we identify characteristics of the system/component or even
algorithm that we want. We make no commitment to it until we’ve sufficiently reasoned that our
system can achieve a stable state in its current form, provided it fits the mold and our
requirements.
Let’s use our hashing as an example. In the very beginning, we believed that we would use a
simple hashing algorithm. A noob would just jump into “what hashing algorithm do we use”
instead of seeing the bigger picture, namely “are we even going to use the hashing algorithm in
this way.” Choosing the algorithm does not give us more options in choosing our system
components or reveal any further insights into what it needs to do. At best, it distracts us. At
worst, it constrains us.
It is only when we reason that key collision may have an adverse effect on our system that we
invent our key pool. At that point, hashing algorithms almost become irrelevant. So whoever
started off the system design with a discussion on MD5 vs SHA1 failed the interview.
Secondly, calculations and estimations serve as a litmus test for the possible pain points you will
encounter. A system that requires 200TB and serves 10 people is going to be less painful and
less tedious than a system that requires 200TB and serve 10M people. That difference in the
number of people served will require you to have data server replication, load balancing, etc. It
changes what you need to think about.
Another thing to note is that every single decision I make is a consequence of the previous
decision. That a decision opens itself to questions and observations that immediately need to be
discovered and resolved.
Finally, the entire process was just a systematic, routine effort of “Let’s do the basics first and
then go back to add some more stuff and unblock ourselves.”
Here is an example:
Suppose you are in 2012. Facebook wants to internationalize its infrastructure. How would you
do this?
L6+/Staff Engineer
This is a more theoretical discussion as I have not actually tested at a L6 level myself. The only
way I can speak to this is by drawing upon my own work interactions with staff engineers at both
Uber and Apple.
But I firmly believe that if you are a L6 level, you actually don’t do things too differently. Instead
you just end up analyzing and justifying systems even better by deep diving and pulling back the
curtain on a good number of these tech stacks. You would be able to state how the data
structures and system organization driving these tech stacks at a fundamental level would work.
For instance, if you were talking about databases, then a staff engineer should be comfortable
talking about how B trees work in relation to a database and say what happens when things get
added and taken away off the top of his head. In fact, his line of work and title would suggest
that he has read the source code for a number of databases as well and can have a relatively
intelligent conversation about the underlying implementations and probably has written
something modifying them as well.
The approach is probably very similar. If the question is a matter of how deep someone can get
into the details, then the setup to that situation should not be any different.
Behavioral Interview
Believe it or not, there IS a RIGHT way and a WRONG way to answer these questions beyond
giving a politically correct answer. There is a hidden technical aspect to this portion.
I realized in the previous edition that this section was fairly lackluster compared to my other
responses. I did admittedly say that its hard to fail the behavioral interview because I kind of
figured that people weren’t stupid enough to walk into the behavioral interview unprepared. If
you prepare well and know what is going to happen ahead of time, you can get upleveled.
But I guess I overestimated the crowd. So I’m going to actually have to give you guys a
step-by-step to prepare for the behavioral interview.
Intro
A lot of behavioral interview prep out there is pretty half-baked because the response can vary
so widely that there is no consistent course/method of evaluation to fine-tune a response. A
basic non-technical interview practice coach is probably fine if you want to get comfortable
talking about your projects. But it can quickly spin into a technical discussion about a project on
your resume and I don’t think anyone other than another senior engineer can tell you whether
you gave the right or wrong answer.
If that’s the case, then what the heck are people looking for? If it was that easy and that
nebulous, then simply not being a douchebag would be enough, right?
Except it isn’t that easy. The expectations of you as an engineer at different levels vary greatly
and we expect a certain type of person who can understand their projects at different depths of
understanding.
The short version is that if someone tells you that they are a professional sushi chef, you would
expect them to have an in-depth discussion on the varying types of tuna found in oceans around
the world, the different cuts of tuna, how to prepare and source them, and so on.
If you’re talking to a home chef who happens to make sushi once in a while, you’d probably just
ask what supermarket they get their fish from.
Different levels of expertise command different levels of discussion. At each corner, you are
expected to defend your answer and provide an appropriate level of consideration and
response.
In the software engineering role, the projects you claim you do are the impetus for these
discussions. You need to talk about not only what you did, but why you did it and the decisions
and considerations you made along the way.
Ultimately, your job consists of fulfilling responsibilities. The purpose of the behavioral
interview is to figure out how much responsibility you can handle. We call this leveling.
Being entrusted with that level of responsibility requires a good understanding of details and
implementations. How code can be affected if we write things one way versus another. How
code can be affected if we let business development steamroll the developers.
And we want to make sure that the person who we’re hiring has acted and does tasks in a way
befitting of those responsibilities and expectations. We want to see moments and times when
you’ve performed in a manner that’s consistent with that.
We take the responsibility as a sign of that understanding. And knowing all these dimensions
and all these tradeoffs is what makes a software engineer good. Your job in the interview is to
show you can actually think about these issues and you are competent enough to understand
what your role is as an engineer. And based on those answers, we will judge your ability to take
on responsibilities of certain levels.
The more skilled you are, the more likely you are to make the right choice and the more
leverage you are trusted with to make sure that the code and project heads in the right
direction.
Oh yes, how do we pick what we want to talk about? At a bare minimum, you should
probably have a story for all your jobs and projects that you have listed on your resume.
Anything you submitted is fair game. But most likely, the talks will be limited to what you
did in the past 5-7 years.
What we really want isn’t just the surface level of what you did during your job but also
the details. The talking points. The thing that makes your job go from “boring corporate
slave” to “holy shit, that’s sexy as fuck.”
Because we want to hire a programmer who can write sexy projects, not just be a drone
who mindlessly shovels out code. Look for moments and projects where you can look
back and say, “Oh damn, I did that. That was fucking cool and I’m proud of that!”
Chances are, someone else will have that reaction. Look for times where you delivered
an actual impact or took autonomy and control beyond your expected duties. These can
be small moments during a project or large overall decision and discussions that lasted
for months.
You should have at least 5-7 well-rehearsed talking points and stories like these. Make
these topics and stories malleable and multi-faceted so that they can be morphed
into whatever answer you need.
Now that you have chosen your stories, each one should have a 30 second, 1-minute,
and 2 minute version. But you really only need to memorize the 30 second version.
Why, then, do we need the 1-minute and 2-minute version? Because we want to mine all
the details out of the experience. What people usually mess up on is rambling. They
don’t talk objectively. They insert a bunch of unnecessary details that bore the
interviewer (who the fuck cares if you wrote some code that was buggy and you
debugged it? Why is that part of your story?). There needs to always be some “Aha!”
moments in the story that keep people interested and make them want to dive deeper.
Doing that exercise will help you narrow in on the details that actually matter. I would
recommend you write it out on paper and read it. If you are having trouble trying to figure
out what details matter, then compare your story to the expectations to your level and
pull some traits from your level+1 on the Square Engineering Ladder. Make sure your
story shows at least 1-2 characteristics from these.
This is a rubric that Square uses to evaluate the quality of their engineers, but the basics
are still pretty similar at every company across the board. Any company will at least have
these expectations. I use the level+1 simply to ensure that we cover our bases and
more. It is good to show some signs of brilliance. But ultimately, we still want to ensure
we make no mistakes and sit very solidly at our level.
Because on the surface, the path forward is obvious: show you do the work expected of a
candidate at x level and get an offer for x level.
For one, the rubrics tend to be a little vague and you can see here in the square engineering
ladder that statements like:
“• Implements code that is clear, concise, tested, and easily understood by others”
Seems like a requirement of even a L3 engineer. Why are some attributes in one category but
not another.
Furthermore, if you want to try talking as if you’re a senior engineer and you spend all your time
talking about how you managed some factory or facade pattern in the code as your contribution,
then that sounds more like a L3 level role. This is despite the fact that even L5 senior engineers
are expected to code.
So make sure that you can tie back that detail to some expectation that is at your desired level.
When you present these stories, the important question is why you did something. The
reasoning, the steps, the thought considerations, and so on. Its not just the scope of the
project but the way you evaluate and think about these projects.
So we’re going to poke holes at it by asking you what the circumstances were, why you
made your decisions what steps you took to come to the conclusions that you did, and
any vague holes in your story that aren’t clear.
You need to anticipate these. Fortunately, if you follow the expectations ladder, you have
a very good idea of where these questions might come from. For instance, if you are
applying as an L4 engineer we can see one of the expectations is:
• Responsible for the entire lifecycle of their code: development, test, production, and
subsequent fixes and improvements.
So if you talk about writing a product, the most obvious questions would be whether the
project was your own initiative or something that someone told you to write. If you came
up with the testing. What problems did you consider or anticipate in the project. Were
there any requests from business development that you needed changed?
All these are questions that an experienced L4 engineer would be able to understand.
After all, when writing self-sufficient code, you probably will encounter many instances of
these (where you need to write the tests, where expectations change and you need to
pivot, and so on).
So make sure you have a detail or two that can cover as many of these as possible.
Example
How do you respond when you disagree with a coworker?
L3: I was working on fixing a bug. Me and my coworker disagreed on how to actually fix this. I
proposed writing a new class and moving the code to that class to encapsulate the feature or
using a dependency inversion since the class was probably going to be replaced at some point.
He wanted to refactor the code entirely and rewrite it. In the end, we decided that the impact of
the feature was not big enough to warrant a rewrite and we decided to move the code.
Engineering Ladder: • Writes, tests, and documents code according to Square Engineering
standards and practices. • Participates in software design for features and bug fixes under direct
supervision.
This is a fine answer. However, the topic of interest is really the code writing. People have lots of
disagreements about code anyway. But if that situation is the first thing you think of, then
chances are its representative of your day-to-day responsibilities. Fussing over details in code
tends to be junior-level work.
But the junior engineer does get bonus points for knowing these coding principles. At this point,
it becomes a technical discussion and I would quiz him on what a dependency inversion is and
why it may be useful.
L4: I wanted to write a service that would allow for automatic retrieval of a list of known bad
actors and IPs to block malicious services. My lead disagreed on how often the job should be
run. We decided to parameterize the frequency of retrieval. We would start at a low frequency
and raise it as we saw fit over time.
In the same vein, this one covers the scope of having to write a full-on independent unit of work
on your own. The interaction with the lead is most likely the only collaborative interaction that he
has. If it was an L3, then chances are he would be hand held—he would require a lead
constantly looking over his shoulder to make sure he doesn’t veer off track. Working with a
technical lead to do some self-sufficient unit of work is L4 work.
Engineering Ladder: • • Designs, develops, ships, and maintains features with guidance from
more experienced engineers. • Is responsible for the entire lifecycle of their code: development,
test, production, and subsequent fixes and improvements.
This conflict is very reasonable as well. I would follow-up by asking what the lead’s reasoning is
for disagreeing. If the candidate cannot say, then chances are he is not paying good attention to
detail and I would downlevel him. However, if the candidate can show a good thoughtful
explanation of the tradeoffs, then he is solidly a L4.
L5: I lead a feature that required the involvement of both the ios and backend engineer. We
disagreed on the backend/frontend contract and interaction. We had to get our respective staff
engineers involved since this was a systematic problem and question that was happening on
every feature. If a solution seems too complex for a simple problem, then chances are there are
much bigger issues at hand and its best to ask people who know.
Going back to before, the responsibility now is to lead a small team to complete a three month
project. He is also talking about reconciling issues between subprojects. This is squarely within
the L5 responsibilities.
Engineering Ladder: •• Is highly proficient in one or more technical areas. • [Shared] Works with
their team and adjacent teams to solve problems. Escalates problems that have wider scope.
What I like about this answer is that it is a mindful attention to systematic issues rather than just
trying to ship product out the door. I don’t think I would knock any points off for not knowing how
to address systematic issues. There is nothing wrong with asking people who know more than
you and have better exposure than you to validate a question or an issue. It can show a good
amount of care, especially if its out of your depth.
But suppose the engineer did not actually know or understand why the issue was systematic
after asking. To me, that demonstrates carelessness and an “I don’t give a shit” attitude. I would
not even entertain the idea of downleveling: I’d simply reject the candidate. I don’t want shitty
coders who do not give a crap about what’s going on in the ecosystem that they’re writing in to
be my coworkers.
Now you might be wondering what a L5+ answer would look like:
L5+: I was working on migrating our entire codebase to Kotlin and me and my coworker
disagreed on how much risk should be assumed during this migration. I hedged against the risk
by putting flags and reverts in place silently to not disrupt the developer experience and that any
disruptions could be immediately fixed. My coworker wanted to fully validate everything from the
code to binary size at each step manually before pushing a change out. In the end, we
incorporated our ideas together and automated the build and validation into a shadow pipeline
to warn us daily of any issues.
This one is a lot harder to explain because there are so many layers. On one hand, the size of
the code definitely seems L5 or L5+. On the other hand, if the migration is simple, it could be an
L5 level of work.
At some level of scale, hedging risk because of the code size, all the things that can blow you
up, and being entrusted to handle a large code migration that can affect hundreds of developers
is a very big responsibility you don’t hand to just anyone.
I would probably be apprehensive about hiring this person as a L5+. I would like to have him
actually be able to explain more about why he thought flags were enough of a hedge and what
convinced him to take even more precautions. There are only so many things you can protect
yourself against without dragging out the project.
Given those answers and if they are reasonable or not, this would be a good L5+ answer.
It really does not matter what question is being asked. It can be to describe a weakness. It can
be to describe a time when you did not work well with a teammate. What the question is asking
is irrelevant.
What IS relevant is the philosophy and bigger point about what you value in a project and
your job.
Software engineering is hard. And I mean hard. You are combining a bunch of heuristics and
rules that are not systematically ordered to try and create a system that millions of people rely
on. We are essentially cobbling together parts using past patterns we’ve encountered and that
we think will work based on feeling. In fact, if we built bridges the same way we built software,
everyone working on the project would be executed for incompetence.
To manage all of this properly, you need to understand what your job entails. You need to
understand how multiple different factors affect your code ranging from upper management to
design patterns to the very line you write.
Let’s take a hypothetical example. Suppose you are asked to write a new feature. However,
doing so requires you to rip the guts out of the old codebase to do so. How do you handle this?
You could refactor the codebase to be more accommodating for both the old and new features
which would take a lot of time you probably don’t have. You could fight to not do the new feature
which would expend the political capital of your group. You could just force it in but then your
codebase would be a mess and you might have to spend time later cleaning it up or, worst case,
force a rewrite.
Knowing all these dimensions and tradeoffs is what makes a software engineer good. Your job
in the interview is to show you can actually think about these issues and you are competent
enough to understand what your role is as an engineer.
In short, your job as an engineer is to understand and protect your code and system.
What does this mean? It means a lot of different things (including balancing “good enough” vs
“perfect” code) and talking about it goes beyond the scope of this guide. But the more you
understand and care for the code you’re working on, the more you will understand what it
means.
At any rate, all questions and stories you have should always lead to this principle or at least a
principle regarding your belief in how code should be organized/handled.
You did not get along with your coworker for a project? Maybe talk about how you did not see
eye-to-eye for the future of the codebase and you resolved it by making the architecture flexible
enough to accommodate for future plans. Your biggest weakness? Getting bored when new
features aren’t challenging to implement because it means the codebase you protect is clean
(humble brag).
No matter what answer you give, always, always, always tie it back to how committed you are to
the quality and protection of your code. It doesn’t have to be directly. It can be a bit roundabout
(ie. team cohesion). But if you cannot eventually tie it to the quality of your code, you are giving
a bad answer!
No company is going to value a guy who cannot think about the quality of their code or its future
impact. At best, they’re just going to be fixing bugs all day. So always try to eventually pivot to
your core values after answering the question.
If you’re asking about the culture, work-life balance, the environment, core values, switching
teams, etc., chances you are a candidate is prioritizing the comfort of the work environment
which means you are probably going to think like a typical salaried employee. This is very
common for L3 or L4. That is perfectly ok. Maybe that is what you prioritize because you have a
kid and work isn’t your #1 priority. You can have that and still be very competent.
But most engineers, including your interviewer, don’t pay attention to the company’s “core
values” or memorize them or really pay attention to the company perks—they’re focused on
trying to fix the damn regression that QA found two days ago and they have to fix it in the next
four hours.
However, if you’re asking questions about the codebase, product cycle, releases, migrations to
new languages, how new features get approved, etc., then it's a different ballgame. Engineers
who are looking to play a bigger role and expand their presence and skill have a natural
inclination to ask these questions because they understand how each of these elements affects
their work. They are more valuable to the company because it is more cost-effective to grow
talent than to hire new talent and spend six months ramping them up on a codebase before they
contribute anything meaningful.
Prioritization of features over code quality can lead to expensive rewrites down the road which
means a delay of future features. Migrations to new languages require teaching employees the
new language and also require an investigation/experiment into how the new language can
affect compilation times and the compiled code size. Issues like these will affect the end user
because nobody wants to download an app that has grown from 10mb to 60mb with no
significant new features.
To show you are the kind of engineer who seriously thinks of issues like this, an example
question would be, “How do you split your time between new features and refactoring old code?
Do you try to spend a chunk of time every cycle dedicated to code cleanup? Or are these
dedicated tasks in particular cycles?”
This question shows that you are aware of the dangers of bad code and are thinking about how
the company management prioritizes preventing this. It can spin naturally into a conversation
about the culture of the company and its attitude towards its own products and codebase. It can
lead to a discussion about how project proposals are done (ex: are they grown from the
developers or do they come from the top down?).
These are concerns you really only have once you have suffered through multiple scenarios and
different prioritizations where those concerns were ignored. So the best way to do this is to
really acquire meaningful experience where more senior engineers can tell you how and why
different issues occur. However, there are books where you can actually get to this point a lot
faster. If you can touch on the topics in the last 5 minutes, you will appear to be far more
experienced than your resume may let on.
It is very easy to come up with these questions if you have the experience and understanding of
how products and projects can fail because of management/code. The trick is knowing that you
need to ask them and what questions you ask can push you into a higher offer.
Here’s a novel concept: how about actually gitting gud your job? (LOOK AT THAT, GIT GUD). Or
at least what it takes to make sure you can do your job much better than you did yesterday.
Learn what it takes to get a proposal from start to finish. Learn how different patterns,
techniques, and philosophies affect a codebase and its future. Understand how these play into
discussions with your coworkers so you can actually participate in feature planning and bug
reviews.
In turn, you can actually have a meaningful discussion about your code, the future of the project,
and therefore be more competent in leading the future of a project which is what is expected of
a senior engineer. If you have demonstrated that very potential and effort, you’ll probably be
given a higher offer and an easier path to promotion.
Software engineering is more than just lines of code. It’s a craft that takes years to get good at
and what approaches you take to your craft depends on your professional environment. Your
high salary doesn’t mean anything if you can’t be better than a new grad. It all starts with taking
responsibility for the future of the code that not only you write, but your colleagues write.
Recommended Resources
I would recommend Clean Code, Clean Architecture, Pragmatic Programmer, and your more
senior colleagues. Not only should you read these for your interview (as a bonus after you
finished your technical interview preparation) but you should also actively apply these concepts
to your code as well in your everyday professional coding. You will feel like utter shit for six
months trying these new ideas at your work. But if you can at least explain to more senior
engineers that you are trying to better yourself by applying something you read, they can
probably fix and tweak your understanding. You will progress faster in your career for having
done that because you will build a rapport with them for being focused on quality. You will
improve the quality of your merge requests and you will build trust with your coworkers.
For those who want explicit book recommendations in a video format, here is one for career and
coding and here is one for learning from people’s merge requests. These recommendations are
not likely to change as the fundamentals are very solid.
Technical Interview Misc.
This section purely exists because most people believe that when they fail an interview, it's
because they failed on the behavioral interview or some crazy, weird unrelated factor. They
blame everything other than their own technical ability.
If people who couldn’t consistently get results would just kindly shut up, this section
wouldn’t exist. Because there are a lot of misconceptions and myths.
But for real, I think people are asking this question to try and narrow the topics they need to
study. Maybe looking for a way to skip dynamic programming or some topic they don’t
understand. Which is really fucking stupid in my opinion.
No matter who you are, your job is to be the best you can be at the interview, not to game the
interview by only studying specific questions.
Gaming the interview is probably the stupidest way to try and pass it because you’re basically
guessing what will be asked. This means you will be screwed if something is outside your
expectations. You’d rather be over-prepared than underprepared.
Everyone says “the interviewer cares about how you think.” That’s semi-true. To be honest, I
think the way that phrase is being used is just feel-good jargon to motivate you to keep trying if
you get stuck. But it only captures half the equation because not only do you need to have the
right thinking, you need to get the right answer.
At the end of the day, if you get the right solution with little or minimal help, you’re fine.
Interviewers will drop hints if and when you need them but if you can’t run far enough with those
hints, they will fail you.
Basically, just fucking do well and keep trying. You’re in that room for 40 minutes anyway. Might
as well use as much time as you can and push as hard as you can.
I will say that if you ace your technicals but still get rejected, 90% of the time the factors are
completely not your fault and these situations are few and far between. At that point, you’ve
done everything you can and it still wasn’t enough so don’t beat yourself up over it.
The other 10%? You’re “that guy”. You know, the guy who nobody can work with. The guy who
can’t take feedback. The guy everyone dislikes. Nothing in this guide is going to fix that because
you have bigger issues.
At Apple, you tend to interview with the team you’re going to work for so there is both a
character matching aspect during the technical interview (behavioral to an extent, even if you’re
not directly asked behavioral questions). Some teams are looking for a L5 dev but will settle for
an L4. Other teams are strict about their requirements. Some people might want a talker, other
people might not. Each team is different and wants a different combination of skill and
personality.
But Apple is probably the exception rather than the rule for large companies. Facebook, Uber,
and Google make you apply to the company as a whole and then team match you with whoever
is hiring. So realistically speaking, at that point, it’s just simply doing well on your technical.
I think this aspect is completely misunderstood. Why? It's kind of like when you’re talking to a
person. Their mannerisms or what they talk about either make them likable or piss you off. That
human element is a factor for sure. But at the same time, that isn’t what you should worry about.
Whether the interviewer helps you or not is a lifeline and can make or break you in your
moments of weakness. But your goal is to be in a position to never rely on this. You should be
good enough that you should never really rely on a lifeline or the interviewer’s help. Definitely, if
you have some spare time, you should refine your professional persona to be more likable. But
don’t worry too much until you’ve finished your technical interview study.
Being likable has nothing to do with whether or not you have the skill to solve the problem itself.
It can tilt the table about 5% in either direction. But you should be so good that the interviewer
has no choice but to acknowledge you.
This is an incredibly big problem in a real coding environment. It means the developer is not
thinking about the structure of their code, much less how much impact that code has on the
architecture or future development. After all, you cannot think of the bigger picture or how other
code works with your code if you don't even know where your code is heading or what you’re
doing. That kind of coding approach is just sloppy, irresponsible, and anyone in this habit should
not be allowed to touch more sensitive or critical material.
If you do this and continue to do this, you will be replaced in 2-3 years. You might make
L4…barely, but you’ll never be trusted enough to get to L5.
I would fail you immediately if you ever did this, even as an intern or L3 candidate. So don’t you
dare do this during your CoderPad phone screens. Spend at least a few minutes reading over
your own code and try to debug this without touching the keyboard.
Another red flag is when the candidate ignores help or fails to listen. I’ve been the guy who has
tried to help a senior candidate get an answer only for them to go “yeah, uh-huh, yeah” and go
off on their own while I’m talking and they still get nowhere. Dude, just shut up and let me try to
help you ace this interview.
You know what the least-bright red flag is? Saying “I don’t know” or “I’m sorry maybe I don’t
quite understand what you’re asking”. That’s actually ok. Nobody knows everything. Sometimes
things are confusing. Maybe you have indeed misunderstood what was being asked. What you
should do is show you’ve made an effort and gone down every single rabbit hole you have
found. Chances are there is something very simple you’ve overlooked and if I nudged you in
that direction and you took off and finished the problem, I wouldn’t care. Coding is a
collaborative craft and nobody has all the answers. But if you can go far with just a small hint,
that’s already a very good sign.
Saying “I don’t know” over and over again is a big fail though. In which case, yes, you do suck.
But you should study like you need to be the guy who knows everything.
Muh Signaling!
“Signaling” is a secondary requirement. It is the bullshit icing on the cake of “get the optimal
solution first.” Let’s look at Gayle McDowell’s answer (author of CTCI and now consult to many
different companies about hiring the best engineers) to this particular Quora question about
when a candidate answers a question suboptimally.
In all three of the hypothetical situations, she always mentions that the candidate has an optimal
solution, even if they start out with a suboptimal solution. What you should also notice is that if
you never get to the optimal solution, then you have no chance of getting hired! You can signal
all you want but at the end of the day, even though she says that “It's always about signal”, it's
ALWAYS about the right answer and the steps you take to get there.
Should it be about signaling? Yes. Are interviews about signaling in reality? No. Google has
thousands of other candidates so they can pick whoever they want so they get the luxury of
choosing candidates who signal AND get the optimal answer.
If you never get the right answer but you signal correctly, you will never get hired. On the flip
side, if you get the optimal answer but you don’t signal correctly, you can get underhired or be
asked for a follow-up interview. Once you get your foot in the door, it will usually take a year to
get calibrated to your actual level. For most people, getting through the door is the hardest part.
So not only do you need the optimal solution, you should understand the reasoning as to what
characteristics lead from a suboptimal solution to an optimal solution (which is exactly what I’m
recommending with the characteristics identification).
In short, start with the suboptimal solution, point out the characteristics you can leverage to
come up with an optimal solution, and code your optimal solution. Which is exactly what I’m
telling you to do. But always, always, always prioritize showing the optimal solution and the
steps to optimization secondary.
Should I Study X?
20% of the material you study will be relevant to 80% of the interview questions and vice versa.
That being said, there are a lot of small things you should know as well, not just how to
manipulate data structures.
While any common topic is usually worth knowing, it may not be worth your time to deep dive
and master that topic if it isn’t likely to help you solve your interview questions.
Bigger companies tend to be more standard and just ask whiteboard questions. For mid-sized
companies and some startups (Dropbox, Airbnb, etc.), knowing these non-LeetCode questions
is fair game and more common.
You should be well aware of how your standard libraries and how Collections work underneath
the hood. If you’re a Java programmer, you should understand how hashCode() and equals()
works in conjunction with your HashMaps and so on. You should have some understanding and
know how to work through the multi-threading problems (dining philosophers,
producer/consumer, etc.). AVL trees, A* search, Red-Black trees, Huffman encoding, etc. You
don’t have to know it hardcore as if it were for an exam; a cursory familiarity is good enough.
These are more conceptual questions, but they shouldn’t be your main focus since you’ll
probably see them only 20% of the time, despite how broad the topics can be.
The most important out of all of these are questions about the programming language. If you are
having a hard time understanding/remembering these programming language specific
questions, my suggestion is to get more experience with side projects where ignoring these
actually bites you in the ass.
For these specialized data structures, you should understand them but you won’t necessarily be
asked to implement them. You will probably never be asked to actually implement an AVL tree.
But some (like tries) will appear so you should be familiar with these structures to the point
where you can actively think about how to implement them when asked (between cognitive and
associative competency).
Try to focus on the things that help you solve the problems you’re more likely to encounter
faster.
Scheduling
So you’ve committed yourself to actually getting good. This is not a very easy investment to
make and is essentially a part-time, if not full time, job. If you are dedicated, you can easily get
to autonomous competency in a month to six weeks.
I’m not going over how to make time for your studying. That’s up to you. You don’t have time?
Then make time. I don’t care what it takes, you figure it out.
Here was my study schedule when I was practicing for interviews. When I’m not studying for
interviews, I’m spending the study time reading or doing something similar. This schedule
gives me 4.5 hours per weekday and 12 hours per weekend. I’m only asking you to do
half of this.
Weekday Weekend
0:00 Study
0:30
1:00 Sleep
1:30
2:00
2:30
3:00
3:30
4:00
4:30
5:00
5:30
6:00
6:30
7:00
7:30
8:00
8:30
9:00
9:30 Work Study
10:00
10:30
11:00 Break
11:30 Study
12:00
12:30
13:00 Lunch
13:30 Study
14:00
14:30
15:00 Break
15:30 Study
16:00
16:30
17:00 Break
17:30 Study
18:00 Gym
18:30 Gym
19:00 Dinner
19:30 Study
20:00
20:30
21:00 Break
21:30 Study
22:00
22:30
23:00 Break
23:30 Study
In my experience, it should take about 80-100 hours of prep to be good at these questions. The
standard for “good” is to be able to answer every question and get to the optimal solution almost
off the bat at least 50% of the time.
By the end of this, you should be at a point where you don’t have to think through the problem
and you go on autopilot to solve a majority of the questions. There are some questions where
no magic pattern/algorithm/data structure will save you (ie. rotate a 2D array) which you will
have to hand parse and do on your own. But those aren’t the ones you should specifically
prepare for because at that point it’s about your ability to break down the problem step-by-step
and translate that idea to code.
You should already have this if you haven’t been cheating on every single problem in your
practice.
At the end of every week, you should have finished about four sections of Grokking or three
chapters of EPI. When I say complete, I mean you’re actually able to solve each question fluidly.
This can mean repeating the sections and questions over and over again until you can get the
answer or “trick” simply by looking at it. You will probably want to barf and you might get very
angry at yourself as to why you’re not faster. Good, embrace it. It's going to be your new best
friend for the next few weeks.
Benchmarks
Do not obsess if you do not hit these benchmarks. I would say this section is practically
worthless to most people outside of curiosity. It took me two hours, on average, to get good at
each particular algorithm and about five hours for a data structure. I suspect it’s because I had a
natural intuition for a lot of these topics anyway.
Do not obsess about hitting these benchmarks. These are just checkpoints to see if you are
progressing in your studying and whether you need help or not. If it takes you longer, then it
takes you longer. Don’t force it! You will know when you are fluent and ready!
Hours 1-35: Grokking the Coding Interview: Patterns for Coding Questions (omit DP section, we
will do that later). I recommend doing every single problem except for the challenges (not
necessary). Devote at least 20 min to each problem before you look at the answer. After every
problem, try to redo them from the ground up. Focus on a systematic approach to the problems.
It might seem like a lot of initial investment of time in the first few problems, but since the
problems test the same technique, if you know how to practice right, you will be very fast at
applying them over and over again so invest your time to doing the first problem RIGHT. If you
do the first problem in the right way and think in the right way, the rest of the problems become a
five-minute exercise.
Hours 35-50: Grokking Dynamic Programming Patterns for Coding Interviews (separate
course). This was the hardest for me to start but once I got going, it was fine. There is a
systematic way to approach all these problems except for the substring/subsequence ones. I
just memorized those.
Hours 50-85: Elements of Programming Interview (EPI). Do the problem set for the "project"
turn around time (three weeks). Personally, I did pseudo code for every question; I didn’t even
write out the code because it was just far too time consuming and there wasn’t a way to check
other than to actually write the test cases which took away time from actually studying.
For the recommended questions, I spent an average of 20 minutes trying to figure those out.
For the non-recommended questions, I spent an average of 10. You can omit dynamic
programming chapter. You really only need to cover chapters 5-16 for technical material. The
first four chapters are introductory.
Hours 85-115: System Design study (omit if you’re aiming for L3). This one is particularly murky
for me since I’m a mobile developer and most of this time was me re-reading Android
documentation, system design patterns, what is new with Android versions, new components
available, etc. I think Grokking System Design is good for most people who are going to be
working backend or are full stack engineers. I bought the course, but it wasn’t particularly
relevant to me.
Hours 115-130: LeetCode. Fill in the gap. Do mostly medium-level problems but also try
random hard-level problems. These will tell you where you're missing knowledge. I recommend
sticking with the questions from the section for company interviews. This is your “ramp down”
period.
Note:
You will feel like absolute shit for the first 50 hours and you should. But you'll get over it. Briefly
re-skim through the problems and see if you remember how to solve them before moving on to
the next chapter.
Also remember to spend a good day or two before your on-site interview to wind down and not
focus so much on studying. Otherwise, you will go crazy and get paranoid about what
information you may or may not have forgotten.
If you don’t have as much time as I did, you can just do the Grokking series before your phone
screen (without the DP portion) and finish up the rest prior to your on-site. If you find there is not
enough time between now and your on-site, you can cut back on some of the EPI topics. Focus
on arrays, lists, sorting, trees, graphs in Grokking and EPI. The bitwise section can be put for
later (it is rare, but not unheard of to ask bitwise operation problems). Reduce the problems you
look at in EPI to just the essentials based on your timeline. They have a “schedule” of
recommended problems depending on when your timeline is so adjust accordingly.
@50 hours: you should be able to do easy/medium problems in under 20 min—10 min to get
the pseudo code/idea and 10 min to write.
@80 hours: you should start to peak. No problem should be difficult, except maybe some DP
problems. There are very few problems that you cannot solve optimally, but you should be able
to solve every problem naively.
@100 hours: you should start feeling stressed and overloaded. This is the time to ramp down.
Ramping down means to reduce your study time to about two problems a day (less than 40
minutes). If you are feeling still unprepared at this point, take a three-day break of not studying
and skim back over everything, then continue where you left off.
Personally, I hit the stressed and overloaded point at 100 hours. I just briefly skimmed and
re-read one chapter of EPI and browsed the chapter questions every night. I rarely put pen to
paper to try and solve questions after this point.
Am I Ready To Apply?
You are ready to start interviewing when you’ve hit these checkmarks:
● Completed the above Grokking and EPI curriculum.
● On 20 random problems in a row, you can solve 70% of them on the first try.
● If I gave you an infinite amount of time on any problem, you could potentially solve it.
● The only mistakes you are recording in your practice diary are weird situational facts.
Everyone has a varying amount of time it takes to get there. So this WILL take a while.
Interview Scheduling
If you are fortunate enough to have multiple offers for interviews, then take as many as you can.
This is essentially free practice. As mentioned before, people who are bad at an activity are bad
at self-critiquing at that particular activity. If you are practicing with someone who is as bad as
you, you’re going to learn the wrong things or receive the wrong feedback. Interviews with
people who are better than you/industry veterans is how you get practice with people who are
better and have a taste of what it is like before the real deal.
For most engineers, their goal is a FAANG job—everything else is just a stepping stone for that.
So why not use non-FAANG interviews as a chance to practice? At worst, they say no and
forget about you. At best, they extend a job offer to you which you can use to negotiate a higher
salary at another job and have a safety in case you don’t make it to FAANG. But you will at least
have a testing/practice partner.
If you have a bunch of phone screens, I would stagger them out every five days or so. The
fewer phone interviews you have, the more you should spread them out so you can receive
feedback on how you did and apply them to the next phone screen.
Suppose you are inundated with a bunch of phone screens and on-sites. Schedule no more
than two on-sites per week and do not make them back-to-back days. If you only have a handful
of on-sites, stagger them apart by at least two weeks so you can get feedback from one
interview to apply to the next.
Try not to do more than two phone screens a week. Your voice and mental energy will deplete
very quickly otherwise. You will be talking a lot (one hour straight for phone screens and five
hours straight for on-sites) so it is important to rest your voice.
However, if you want to be as crazy as me, I scheduled phone interviews every other day for
about three weeks straight. Granted, I passed all of them but one.
For my on-site, I tried to only do two a week, but due to some time and offer constraints, I did
my interviews with Google and Facebook back-to-back. On top of this, I had to fill in my eight
hours of work right after and review merge requests and help my coworkers with bugs. My
voice and social battery were completely shot for the next week because of that and I do
not recommend this.
A cookie cutter schedule might look like this image below. Please do not just copy this. Allow
yourself more time if you need to between on-sites and phone screens!
Offers
Out of all the companies, Google is the one that takes the longest to get back to you (over a
month). So try to book it early on in your interview process unless you’re playing for “Google or
bust” in which case, save the best for last.
If you are highly confident in your abilities and are likely to receive multiple offers, then you
should consider the impact receiving an offer has on you. For most people, any job at FAANG
will be sufficient and after receiving an offer, you will have zero motivation to continue practicing.
From what I can tell, after three offers, the motivation just drops to zero. This is what happened
to me. I simply lost motivation to continue to practice and decided to cancel the rest of my
interviews after 3 companies told me I passed.
On the flip side, if you are not certain of passing your interviews, you should try and stagger
them out so that you have enough time to receive the feedback and apply it to your next
interview on-site.
Therefore, if you are confident you will pass your interviews, you should schedule them weekly.
Otherwise, schedule them for every other week or every three weeks.
Day Of The On-Site Interview
So you’ve passed your phone screen. ‘Grats. Now on to the on-site.
Every HR recruiter sends you the same packet and tips and, to be honest, it's not that helpful.
It's pretty much all the same between companies and you might find some useful resources.
Some of the links should be skimmed so you know the history of the company. Nobody wants to
talk to a candidate who has no idea what they’re applying for.
You may have 4-5 interviews back-to-back with bathroom breaks and a lunch break in-between.
Pretty standard.
You should not be doing anything super special. Keep it simple. I don’t care if they’re offering
you caviar. Stick with what you are comfortable with and used to.
The more you stray outside your comfort zone, the less comfortable you are. If you have studied
while drinking water, then always go for the bottle of flat water. The more you can recreate the
conditions of your study, the more you will perform the same as during your studies.
Let’s say you need to wake up at 8am. Working backwards, you will want to be asleep by
10:30pm which means that you will need to take it at around 9:30pm.
Getting adequate sleep will mean you don’t need coffee. Also, definitely do a few test runs with
it so you know how your body reacts.
No Coffee
I drink a fuck-ton of Red Bull and take two scoops of pre-workout just about every other day, but
I won’t drink coffee before an interview. Interviews are high-stress and coffee adds to that by
adding more physical energy into my body. It does nothing for me but make me more anxious in
an an already anxious environment.
I am NOT advocating you take performance enhancing drugs, but the closest thing I’ve found is
ADHD/ADD medication. If you need a psycho stimulant, take Adderall or Ritalin since it keeps
you focused and mellowed out. 2.5mg is sufficient for most people (not the 5mg pills that are
usually prescribed). You SHOULDN’T do this if you have not practiced or studied while using it
or if you do not have a prescription.
Hype Yo Self
Your job right now is to be confident in your own abilities, the practice you have done, and
believe that your abilities make you the best candidate for the job. So act like it. If you second
guess your abilities, you will second guess your decision-making and that can throw off your
interview game.
I highly suggest you listen to 6ix9ine or some kind of hype, loud rap. Coming off as a little cocky
is ok as a self-hype mechanism long as you are humble and willing to listen to the interviewer.
Hardcore hip-hop and rap helps you get into the mood. I listen to Stoopid and Gummo and sing
scream listen to it in the car ride on the way to the interview. Also add in Rick Flair Drip by 21
Savage, Ether by Nas, and I Don’t Like by Chief Keef.
For instance, Facebook was born from a culture of “move fast and break things”. Now that
Facebook is a more mature product, does it still believe in that? How does it balance that
mantra with the need to keep its heavily-relied on product stable?
These are the kind of questions you should be asking. In-depth. Questions that spawn more
questions.
FAQ
1) You could’ve monetized this on YouTube as a full-on course or series—maybe a book.
There are a few reasons I’m not going down the mass-market route.
First, it wouldn’t sell well. People want things to be spoon-fed to them, but they don’t
want to put in any effort into the actual process. They want something in exchange
without creating something of value, whether that thing of value is a product or improving
themselves. They really just want the feeling of doing so and that's what they are paying
for. I am not writing this guide for those people.
Most people would rather complain about being offended by this guide than actually
trying any of the step-by-step procedures I’ve written.
Analyzing and walking through all the techniques and selling them defeats the purpose
of exploration and discovery (which is critical to actually learning and understanding
these algorithms). The practice of breaking down techniques and problems in a way that
makes sense is something that only comes from doing it yourself, not watching someone
else do it.
Gitting gud is about trying, making mistakes, and learning from them. It can mean
wasting tons of time and hours trying to figure something out, but most people do not
have the will to sacrifice that much to be exceptionally good at something.
2) I see you take on clients. How does that not contradict what you said before?
My view on this has changed over the past two years. Since I’m able to provide an
excellent service that I know is valuable, I have no problem staking my entire
compensation in someone’s offers. This is not unlike what other people do—except they
have much lower standards.
I would not be happy unless my client got a six-figure offer well above their
current salary and was able to achieve extraordinary results. If I’m going to sacrifice
six weeks of my life for someone, the results better be worth it. My goal is to maintain my
absurdly high success rate and accept no compromises; I hope that my clients feel the
same way. Plus, it's just straight up more rewarding to help people you believe in rather
than trying to make a buck off of someone you don’t believe in.
My god, being forced to do someone else’s bidding for a buck seems like a terrible way
to live. I guess that’s mass-market sales in a nutshell. What’s the point of money if you
can’t buy your own freedom?
To make it fair and transparent, I make every client’s session free to watch.
“The incompetent cannot know they are incompetent. The skills you need to
produce a right answer are exactly the skills you need to recognize what a right
answer is.” – David Dunning
I’m not against it, but I don’t recommend it unless your interview partner is a better
engineer than you. Even then, it is not a service worth paying for unless you’re looking
for some kind of mentor/connection in the industry. Most engineers are focused on their
own lives and stuff and don’t really have time for you anyway.
Chances are your interview buddy is probably someone who is a similar skill level as
you. Therefore, his knowledge of when and how to help someone on an interview
question is nil. He will probably be spoon feeding you answers unbeknownst to you. On
the flip side, if he doesn’t help you at all, you’re better off just doing the questions on
your own and using an anime figurine as an interviewer for practice.
I personally have never done mock interviews. I just write out my problems on pen and
paper and talk about my thinking to myself anyway.
4) Do I still need to send out resumes and apply a lot? If so, how many?
When you’re starting out, you need to do a lot of cold calls and sends. And I mean A
LOT. I got my first job after sending out about 80-90 resumes. I did maybe three phone
screens and two on-sites. I received one offer.
Referrals just move your resume to the top of the pile, but it doesn’t mean much if you
don’t have the right qualifications or a strong enough resume. That said, you should
always try to leverage one job or internship to get another job or internship and trade up,
using a good performance on the coding interview as a way to get yourself into the
company.
On my path, I started off doing unpaid internships at startups. I did an internship in Hong
Kong the following year that paid me about $5/hour. Afterwards, I worked at a start-up
while going to school. My salary barely covered my living expenses. Finally, I went to
Apple.
So do not overestimate how long it takes to get that first job or how much your offer will
be. A lot of people will tell you about amazing offers they get but I find it takes a few
cycles to get there. More often than not, those high offers with little prior experience are
a rarity.
A good rule of thumb is that for any given topic, technique, or data structure, if your code
is > 20 lines, you are doing something wrong. Either your approach is not efficient
enough (in terms of code length/meaningfulness) or the topic is not relevant. Try to strip
away the null checks as much as humanly possible. A lot of functions nowadays provide
ways to skip this so do try to find all of them.
I think most of them are nice people and you should be good to them. They are your only
friend in this process. But at the end of the day, they are still headhunters working for the
company that will hire you. They will be nice to you and try to get you the best offer they
can and you should treat them well in return. You should skim the materials they send
you but don’t take it too seriously. Your job is to ace the interview and represent your
own best interests.
But do send them a nice thank you after every interview. All the recruiters have been
super sweet and friendly to me and I almost wish I could send them a gift basket or
something.
They just need to work hard and be able to afford the fee. I will not take someone’s
money unless they can afford my fees and I can provide them an equivalent amount of
value. All the sessions I do are free to watch anyway.
Yelp, Facebook, Google, Apple, Microsoft, Amazon, Dropbox, LinkedIn, Paypal, Uber,
and the CIA. Out of all of these, Facebook, Google, Apple, LinkedIn, and Uber have
given me offers. The rest I either got rejected or I decided not to do the on-site for.
9) This guide sucks
Probably. But if you want me to take you seriously, then you write one. Also, show us
how many offers you got, what levels those offers are for, your interview success rate,
and so on. Just so we know you aren’t talking out your ass.
I’m just a stubborn egomaniac who hates losing. And when I say I hate losing, I really
really hate losing. If I’m not good enough, then I get pissed off at myself for not being
good enough and work until I am way better than good enough.
I’m fairly childish and most of my emotions teeter between being bored and pissed. In
my mind, Google giving me an L3 offer is me failing the interview, even though I
technically passed.
I’m also not that smart. I was a B+ student my entire life. I spend most of my free time
watching anime, playing video games, drinking, and pretending I’m Snoop Dogg (if you
know what I mean). If I was really smart, I’d have figured out how to consistently get L5
offers.
But if I want something, I do my homework and go full tryhard mode. Crank up the anime
motivational music and let the games begin.
I just come from the school of “git gud”; either you get it or you get kicked out.
Probably. But if you want a hugbox that will validate your every emotion, go somewhere
else. Seriously, just stop emailing me about what a dick I am. I honestly don’t care what
you think about me. Whether you like me or not, you’re probably just wrong about me.
But hey, if you want to talk shit, at least have a better record than me before you open
your mouth. What’s your success rate at ANY interview again?