Last week we had the first post in our technical interview series, where we covered the knowledge you need to succeed in technical interviews. Today, we’ll be looking at the coding portion of the interview—strategies on writing correct and quality code.

Let’s get started!

Coding

The other major aspect of software engineering interviews is the coding aspect. We want to see that you’re familiar and comfortable coding.

Best Practices

Let’s first go over some general things that you should be aware of when it comes to coding in an interview.

First, know what language you want to code in. Java, Python, and C++ are the usual suspects, but a company might allow you to code in other languages as well. At Yext, we also allow Go in our computer interviews.

Although the company (or your interviewer) might have a preferred language, realize that this is your choice of language. Picking a language isn’t a test; you should choose the language you are most comfortable with. As an interviewer, I don’t care whether Java or Python would be a better choice for solving a problem. It matters most to me that you can solve and implement the problem, so use the language that you’re best at!

Second, don’t rely on an IDE or your normal development environment. Unfortunately, coding in an interview is often not like coding in school or on your own. In particular, the coding environment that you use in your interview will be different from the one you’re used to. Oftentimes you’ll be asked to write code in a text editor or on a whiteboard. Many candidates are used to coding in an IDE, such as Eclipse. This leads to a number of factors that will differ from what you’re used to—you probably won’t have access to error highlighting, autocompletion and suggestions, custom hotkeys and macros, etc.

We understand that you might not be as comfortable with a different environment. At the same time, you shouldn’t be crippled because you have to use a different one. If you feel that you’re overly-dependent on your development environment, here are two suggestions:

Remember that a lot of nice features, such as autocompletion, are supposed to be tools to help you code more efficiently, not to be crutches. If you find yourself relying on one of these features, try coding without using it for a while. Code more. As with many things, one of the best ways to improve is to practice.

Third, determine your approach before you begin coding. Diving into the code before considering how your solution or your code will be structured tends to result in unanticipated problems. Successful candidates usually outline their approach and confirm that it’s sound before actually coding.

With that out of the way, let’s look at what your interviewer wants to see in your code.

Working Code

There are two main things that I want to see in your code. The first of these two things is that your code works.

It’s usually pretty clear when your code works—it produces correct answers within the specified constraints. So, in this section, we’ll instead focus on typical reasons why your code might not work correctly.

Edge Cases

The most common reason why your functional code isn’t acceptable is because it fails to address possible edge cases. When it comes to day-to-day work, engineers are responsible for making sure that their code will work in as many situations as possible. So, we expect the same from you in an interview. You’ll look good in your interview if you show that you can handle edge cases without being prompted.

Fortunately, the edge cases in interview questions are usually fairly straightforward. Here are some starting points to think about in order to determine the edge cases in your problem.

  • Inputs. The simplest edge case to consider is when your method takes in null or empty inputs. The next step to consider is if there are any special inputs to handle.
  • Degenerate inputs. These are inputs that usually cause your problem to break down to a simpler, often trivial, problem. For example, if your method takes in two objects, what happens if you pass in the same object twice?
  • Practical limitations. Although problem solutions tend to be theoretical, your actual code has to deal with real-world concerns. For example, do you have to worry about floating point errors? Will your code run out of memory?

A simple technique to find edge cases is to go over your code once you’re done writing it. Look for ways that your code might break and check your assumptions!

Logic Bugs

Logic bugs occur when your code is functional but produces incorrect outputs. The more complicated your code is, the likelier it is that you’ll introduce a logic bug. So, keep your code neat and simple.

There are many things that can lead to a logic bug, but I find that logic bugs tend to happen in code that tries to do too many different things at once. I was guilty of this when I first learned to code—I would try to be clever and combine multiple steps into a single line. I thought it would be theoretically nicer, and therefore better code.

As it turns out, the only “good” thing about doing this is that it optimizes for fewer lines of code—which is generally not an important metric. It’s essentially the coding equivalent to writing a run-on sentence.

Instead, write simpler and clearer code. That way the code is easy for you to read and debug, and easy for your interviewer to read and understand what you’re doing.

In a similar vein, you’ll often need to iterate on your code. This might happen when your interviewer asks a follow-up question, or when you need to handle an edge case that you forgot about. It’s tempting to try to massage your code to handle cases and do things that you didn’t originally intend it to do. However, be careful when you do this since this is another way to write complicated code and accidentally introduce logic bugs.

Syntax Errors

This is the last major class of things that causes your code to not work properly. Hopefully, this will be the least common reason that your code doesn’t work correctly. Syntax errors are usually the result of not being familiar with the language you’re using.

Basic syntax errors are generally red flags. This could include things such as:

  • not knowing the syntax for a while loop
  • not knowing the modulus operator
  • not knowing whether you should be using null, nil, or None

These syntax errors can also occur due to being rusty in the language you’re using—which is why I recommend using your strongest language. If you know you’ll be using a language that you haven’t used in a while, take some time before your interviews to refresh your memory.

Here’s a less obvious example of a (Java) syntax error mistake that I’ve seen during one of my interviews.

public static int absValue(int n) {
    return Math.sqrt(n*n);
}

If the candidate had checked the documentation first, they might have caught this mistake before I had to point it out. (If you’re not sure about something, ask if you can look up the documentation!)

Although syntax errors are undesirable, the level of detail required depends on context. Generally, precise syntax is less important for whiteboard interviews and more important for computer interviews.

For whiteboard interviews, I personally don’t hold the occasional typo or brain fart against you—mistakes happen. However, if your code is full of typos and syntax errors, I’ll regard that as carelessness or a poor understanding of the programming language. This is in the same spirit as the earlier points on your development environment—it should help you catch the occasional mistake, but it shouldn’t be something that you’re dependent on to avoid making mistakes.

Debugging

While it’s preferable that your code works as expected, you’ll sometimes find yourself in scenarios where you need to do some debugging. Debugging techniques are a bit outside the scope of this post, but here are some things to keep in mind.

Read your code carefully. It can be tricky to find bugs, especially if you can’t compile or run your code. Don’t assume every part of your code works! To debug effectively, you’ll need to be able to scrutinize your code and think about exactly how your machine is interpreting it.

It’s fairly common for candidates to mistakenly believe that their code does something that it does not. Even when they go line-by-line through their code, I’ve seen candidates gloss over the offending line because they are focused more on what each line of code should do rather than what it actually does.

As an example, I’ve had a candidate write code similar to this:

List<Integer> sequence = new ArrayList<>();
...
// other code
...
List<Integer> reverseSequence = new ArrayList<>();
for (int i = sequence.size() - 1; i >= 0; i++) {
    reverseSequence.add(sequence.get(i));
}

When they had finished coding, I let them know that their code didn’t work as they expected it to. My candidate had trouble finding the bug, so I gave some hints as to what was going wrong. I eventually had to point out the problem line—but they still didn’t find it!

In the end, I had to point out the exact bug (i++ should be i--). Once you notice it it’s obvious, but it’s not so easy to spot during an interview.

Quality Code

The second thing I want to see is that your code is quality code. The exact standard for quality will vary between companies and between people, but generally your code should be readable and understandable. This is done by making your code clear, clean, and concise.

Clear Code

Your code is clear when the logic is easy to understand and follow. There’s a lot that goes into writing clear code; here are some guidelines.

Clear code is not the same as commented code. If your code is complicated, writing comments doesn’t make it any easier to read. Ideally, your variable names and code structure should describe what you are implementing. At most, you would need a few one-line comments to denote more complex ideas in your method or algorithm.

Here’s an example from Robert C. Martin’s Clean Code. First, a piece of “complicated” code that is explained with a comment:

// Check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

Here’s how the code could be rewritten to express intent:

if (employee.isEligibleForFullBenefits())

Don’t be clever. We addressed this idea under logic bugs above, but it also applies to writing quality code. Candidates sometimes mistakenly believe that good code consists of (a) combining a lot of ideas into a single idea or (b) trying to reduce the number of lines in their code. However, the notion of quality code in industry is very different.

In industry, your code will be read by many other people. It will be read both in the present and at arbitrary times in the future. It also needs to be maintainable so that other people (including future you) can make any necessary changes in the future. In order to accomplish this, we optimize for clarity and not for cleverness.

As such, don’t try to combine too many things together in your code. Feel free to define extra variables or to make additional if-else blocks as needed. Remember that you want to optimize for practicality, not for theoretical elegance.

Let’s look at a slightly modified version of one of our previous examples. Imagine if you came across this method:

public static void someMethod(double d) {
    d = Math.sqrt(d*d);
    // remainder of method
}

Without context, it’s not obvious what the purpose of this line is. That could lead to trouble if someone later assumed the line was unnecessary and deleted it. By using a clever method to get the absolute value, we actually make our code less clear. It would be better to use Math.abs, an if statement, or a ternary operator.

Name things properly. The names of your variables, classes, and methods should reflect what they are intended to do or represent. For example, if you are counting the number of words in a paragraph, you might create an integer variable and name it numWords. Don’t name it something like x, because that doesn’t describe what the variable is.

Handle edge cases simply. This is similar to don’t be clever. You’ll often run into situations where your core logic can be easily tweaked in order to handle an edge case. But other times, an edge case will not fit neatly into your existing code. In the latter case, don’t try to force your code to handle that edge case!

Instead, just write an appropriate if statement and handle the edge case separately. For example, if your method shouldn’t take in null variables, just add a null check at the beginning of your method.

Clean

Writing clean code means that you take the time to polish up your code. Just as clear code is easy to read because the logic is easy to follow, clean code is easy to read because it’s well-formatted.

Eliminate your scratch work. Get rid of all the stuff that didn’t work and all the comments that you no longer need. Keep your code tidy. This might not matter to your interviewer, but it’s still a nice thing to see.

Have consistent formatting. This largely consists of the time to do the things that an IDE would do for you—eliminate excessive white space, maintain proper indentation, etc.

Concise

Your code is concise if your code structure is well-organized. Most commonly, this will mean not duplicating code. If you find yourself writing the exact same piece of code multiple times, that’s probably an indicator that you should pull that code out into a helper method.


You can find the third and final part of this series here!