Coding Interviews and the Importance of Perfection

Your algorithm was correct, your code was correct, but you still got rejected. This is not only possible, but incredibly common.

Candidates are routinely surprised when it does because they don’t quite understand the interview process and how they are evaluated.

>> Read this on Medium.

Let’s start with just the part about the code being “correct.” Is this necessary? Is it sufficient?

It’s neither, in fact. Consider the real world (which, after all, is what interviews are attempting to be indicative of). Is it okay to have a bug in the real world?

You probably find it a little difficult to answer that question. Bugs are never “okay.” All else being equal, you’d prefer the bug-free code over the buggy code. But one bug doesn’t usually get you fired. If it did, then none of us would have jobs.

Likewise, in an interview, the ideal is obviously to not have bugs. Bugs are never “okay”. If you can avoid a bug, you should. And clearly your interviewer prefers to see fewer bugs. That doesn’t mean that you’ll get rejected just because you have a bug.

In fact, I rarely conduct an interview where the candidate doesn’t have at least one bug. What matters is what the bug indicates and how they fix it.

What the Bug Indicates

Here’s what you need to understand about evaluation: Your code is a code sample from which your interviewer extrapolates information about your coding skills. Correctness is not important, in and of itself.

Consider Java code like this:

LinkedList<String> list = new LinkedList<String>();
list.insert(“hello”);

This code has a “big” bug (or, rather, compilation error) that will 100% break if you were to compile it. In line 2, you should have written add, notinsert. If you released this code, your product would be 100% dead (until, of course, you fix it).

But interviewers aren’t crazy and they won’t reject you for this. They probably won’t even notice.

Your whiteboard code is merely a code sample from which your interviewer will derive information. The information that I derive here is that you probably don’t use the built-in linked list class much. Do I care? Not even a little bit.

What about code like this?

int getMax(int[] array) {
 int max = array[0];
 for (int i = 0; i <= array.length; i++) {
if (array[i] < max) {
 max = array[i];
}
 }
 return max;
}

We have a few issues here.

  1. This code will crash at line 2 if the array is null or empty.
  2. The for loop starts at 0 when there’s no need for it to. It should start at 1.
  3. The for loop goes through array.length. This will cause an exception every time.
  4. The comparison on line 4 is backwards.

To have four bugs in such a simple piece of code worries me. I will conclude that you probably write buggy code. In fact, bug #3 will cause me to wonder how often you write code; it’s pretty strange to mess up a very basic for loop pattern like this.

Now, what about this? (Yes, this is real code that I saw an interview.)

int getMax(int[] array) {
 int max = array[0];
 for (int i = 0; i < array.length — 1; i++) {
if (array[i] > array[i + 1]) {
 max = array[i];
}
 }
 return max;
}

This isn’t just carelessness. The code doesn’t even make sense algorithmically. I’m very concerned about that.

If you find and fix these bugs easily, I’ll be slightly less concerned (but of course it’s still better if you didn’t even make them). This brings me to the second part.

How You Identify and Fix Your Bugs

Many candidates panic when they find a bug. A particular test case reveals a bug, and then they make a quick fix. The “fix” resolves it for that test case, but perhaps doesn’t fix the true issue.

For example, consider this code to locate all instances of a string s within a string b:

int countSubstrings(String s, String b) {
 int count = 0;
 for (int i = 0; i < b.length() — s.length(); i++) {
String bSubstring = b.substring(i, i + s.length());
if (bSubstring.equals(s)) {
 count++;
}
 }
 return count;
}

At first glance, the code basically looks correct, but it’s not. (Did you notice the bug yet?)

Many candidates spot the bug when they throw in a test case like a = “xyz” and b = “xyz”. That’s when they notice that the for loop never gets executed at all.

The weaker candidates will jump immediately to a fix. They’ll think that there’s a special case when a and b are the same length, and they’ll throw in an if statement at the very beginning to handle this.

That’s not the bug though. There’s always an off-by-one error. The if statement comparison should have read b.length() — s.length() + 1.

I am less concerned by the existence of this bug than I am by a bad fix. Bad fixes don’t correct the issue and they add unnecessary code that might linger (even after the true bug is discovered) and they cause us to think we no longer have a bug and sometimes they even introduce new bugs.

When you find bugs, think before you fix!

But that’s not all…

Finally, understand that correctness is just one thing an interviewer is evaluating in your code. Depending on the interviewer and the problem, the interviewer might be looking at technical knowledge, code style, organization / modularization, compactness, and other attributes. The code you write is offering a data point about your coding skills, and it is evaluated as such.

In fact, the same can be said about the algorithm / problem solving portion. It too is not evaluated on a binary “did you get it correct” basis. Rather, it’s a data point about your analytical skills, computer science knowledge, and other attributes.

Some candidates are surprised to get rejected even though they got a problem “correct”. What they don’t realize is that correctness is usually not sufficient (and may not even be strictly necessary). You can get a problem “correct”, but still demonstrate weak analytical skills or weak coding skills. Everything that happens in the interview offers data points from which I extrapolate. It’s not about “correct” or “incorrect.”

>> Read this on Medium.