Java Developer Interviews

Originally posted 2006-04-17 21:37:35

Java developer resumes all look alike. They’re all too many pages, and list all the buzzwords: Eclipse, EJB, Struts, Hibernate, Spring, WebSphere. I can tell almost nothing from the resumes that cross my desk, which means I have to interview a lot of candidates for any job opening. Consequently, I’ve conducted more than my share of programmer interviews in my career–for a long time as a potential peer, and more recently as the hiring manager. I’ve written quizzes for screening, asked what their strengths and weaknesses are, drilled them with questions about polymorphism and reflection and collections–everything you’d expect to encounter in an interview. I find, though, that all I really care about is:

  • They’re clean and presentable
  • They don’t swear in the interview
  • They seem easy to get along with
  • They don’t completely blow the soft-skill questions
  • They can write good code

The last one is the hardest to measure, but if they can’t write good code it doesn’t matter if the business partners love ’em or they smell like Irish Spring. Early on, I started asking candidates to write code on the white board to try to get a sense of their coding abilities. I probably borrowed this idea from Joel Spolsky’s The Guerilla Guide to Interviewing, though I don’t remember for sure. I reassure candidates that I understand that the white board has no compiler nor Intellisense/code completion, so I’m not going to grade them on curly braces. I’ve discovered myself becoming annoyed, however, that I go through that explanation–do I really want to hire someone who can’t figure out that I don’t know the limitations of a white board? Interviews are stressful enough, though, so a little reassurance can’t be a bad thing.

I’ve come to rely so extensively on the \”white board\” portion of the interview that I keep pushing it earlier and earlier in the interview, trying to determine if we need to take the time to do the soft stuff (\”What maxims do you live by? I really don’t care if you can’t code a for loop!\”). While the results from the code-on-the-board haven’t been perfect, they’ve been a pretty good indicator for future success.

The problems I pose for potential hires to code on the white board follow. I realize that people I interview in the future may find this blog entry beforehand and walk into the interview fully prepped. That’s a risk I’m willing to take, for two reasons:

  1. If they’re reading my blog, they must be smart, and
  2. I can always whip out another problem or two if they completely ace my three problems.

The first problem I give is to write a snippet of code that prints the numbers 1 through 10 on the screen. I expect something like this:

for (int i = 1; i < 11; i++) {
  System.out.println(i);
}

You'd be surprised at how many people find this a challenge. About 1 in 10 people get it right. Most print 0 through 9 on the screen, which in itself is a good opportunity to see how they react to criticism. \"You have a bug,\" I say. \"Find it and fix it.\" The haughty ones who look at me as if, as a manager, I have no clue about technical things get a strike against them. Some start adding curly braces here and there, as if doing something--anything--is better than just staring at the board. A few people discover their off-by-one bug, but usually I have to give them a bigger hint: \"You run the program and, instead of seeing 1 through 10, you see 0 through 9.\" Then, they get it.

The \"0 through 9\" folks are still in the running. At least 30-40%, however, botch this problem completely. I'm not kidding. The better ones in this group create an array, loop through it once to fill it with the values 1 through 10 (or, more often, 0 through 9), and then loop through the array again to print the values on the screen. I ask probing questions about why they bothered to put the values in a loop first, trying to help them uncomplicate things, but they staunchly defend their positions because \"arrays are good for holding lots of values.\" You wouldn't think that more than one developer could come up with this gem, but I've heard it many times.

Some can't even do that array mess, and completely blow this. I once had to ask someone what language he was using, and he looked at me disapprovingly and intoned, \"Java, of course.\" His \"code\" could have almost passed as perl, I suppose, but it used a syntax heretofore unknown to man.

I've had a couple of people write something like this:

System.out.println(\"1 2 3 4 5 6 7 8 9 10\");

This could have led to a good discussion of requirements gathering and simple solutions, except in each case the developer has been far too hasty to point out how clever he was to do something like this, before I could even open my mouth, so I just rewarded the attempt with a dull nod, beleaguered smile, and a request to rewrite with a loop.

For problem #2, I steal something directly from Joel's interview guide (making it all the more likely that I stole the code-on-a-whiteboard idea from him) and ask people to write a method that reverses a String. I give people bonus points for remembering that StringBuffer has a reverse() method, then tell them to ignore that method and write the thing for themselves. I keep hoping to see something like this:

public static String reverse(String str) {
  if (str == null or str.length() == 0) {
    return str;
  } else {
    StringBuffer buf = new StringBuffer(str);
    char c;
    for (int i = 0, j = buf.length() - 1; i < j; i++, j--) {
      c = buf.charAt(i);
      buf.setCharAt(i, buf.charAt(j));
      buf.setCharAt(j, c);
    }
    return buf.toString();
  }
}

I have yet to see people reverse the String \"in place,\" however, or as \"in place\" as Java will allow by first putting it in a StringBuffer. I don't know, either, if looping halfway through the String and swapping values is any faster than looping through the whole String, but it looks like it should be. The good developers, however, at least figure out to iterate through the String from the last character to the first, appending each character to a StringBuffer, like this:

public static String reverse(String str) {
  StringBuffer buf = new StringBuffer();
  for (int i = str.length() - 1; i >= 0; i--) {
    buf.append(str.charAt(i));
  }
  return buf.toString();
}

They'll often have off-by-one errors, like starting at str.length() instead of str.length() - 1 or ending at i = 0, or forget the charAt() method and go for the C brackets (buf.append(str[i])), but those are forgivable and I walk them through those.

Once they get that method looking somewhat like the one above, I ask what exceptions the method could throw. I had one person look at me as if I'd urinated on his shag carpet and say, \"Exceptions? There's nothing wrong with this code!\" Most, however, go to the IndexOutOfBoundsException, even though that ain't gonna happen. I just ask under what conditions that could be thrown, and they say, \"that happens if you try to access an index that's less than zero or beyond the length of the string.\" Thanks for the definition, Webster. I ask them when their method could throw it--what values for str would cause an IndexOutOfBoundsException? Most look at me blankly, but some look at their method long and hard and realize that they won't get an IndexOutOfBoundsException.

About then, they realize that, for str = null, a NullPointerException would be thrown. This sometimes leads to a good discussion about how to handle that--do you simply allow the NullPointerException to be thrown? Do you throw one yourself, with your own custom message? Do you throw an IllegalArgumentException? Do you throw a custom exception? Do you return null? I really don't care what their answer is. I just want them to make a decision and defend it.

Alas, few people get this far. Most simply cannot reverse a String. Period. End of story. Most churn out code for awhile that loops through the passed String a minimum of twice, storing values in arrays and Vectors and concatenating Strings and enough creating enough obfuscation to make my stomach churn. I even had one applicant stare at the white board for a full three minutes, without moving, before whipping around, marker in hand, to say, \"If you hire me, I know I can do the job.\"

The final question I ask starts with having them create an interface called Shape that declares a single method that returns the area of the shape. I expect this:

public interface Shape {
  double getArea();
}

Almost invariably, I get this:

public interface Shape {
  public int getArea(int width, int height);
}

I ignore the parameters passed to the getArea() method, thinking they'll figure that out later, but ask why they chose to return an int. They usually say something about it being simpler, though I don't see how an int is simpler than a double or a float, so I push them to change the return type to some floating point type.

Then, I ask them to create a class called Rectangle that implements their Shape interface. They smugly work this up:

public class Rectangle implements Shape {
  public float getArea(int width, int height) {
    return width * height;
  }
}

I let them bask in the glory for a moment before asking them to create a class called Circle that implements Shape. They freeze. They look at their getArea() method with its parameters. Then they change getArea() in both Shape and Rectangle to read getRectArea(), then create a new method in Shape called getCircArea() (taking a radius as a parameter) and implement it in their Circle class. I gently remind them that neither Rectangle nor Circle will compile, since they don't implement all of Shape's methods, and they hastily scrawl in zero-returning implementations for the unimplemented method in each class. One person had the bright idea to avoid implementing getRectArea() in his Circle class by having Circle extend Rectangle. I knew we'd talked about inheritance earlier in the interview, so I asked what the relationship was between Circle and Rectangle. \"Is-a,\" he said. \"Do you see a problem with that?\" I asked. \"No,\" he responded. \"Is a circle a rectangle?\" I asked. \"Yes, \" he said, \"in this case, yes. This is just maths. It's not real-life things.\"

Richard Feynman, the Nobel-winning physicist, once wrote about working with students and educators in Brazil to improve the state of physics instruction in Brazil's schools. He wrote that students knew the definitions of terms, but not their application. At this point in the interview, I usually feel the same way. We've already talked about polymorphism and encapsulation--why doesn't this person use them? So I ask, \"How could polymorphism help with your design?\" They usually respond by telling me that polymorphism means many forms, that disparate objects react appropriately to the same message. Great definition, just like Richard Feynman used to get from Brazil's physics students, but it's not an answer to the question I asked. If the candidate did particularly well on the technical questions and soft skills (and remembered to shower and wear deodorant for the interview), I'll try to lead them through the morass of their design. \"Imagine you had a collection containing multiple Shapes, both Circles and Rectangles, and you had to iterate through them and print their respective areas to the screen,\" I ask. Their responses are filled with ifs and instanceofs and casting to the actual class, and are void of simply using the Shape interface because they've boxed themselves into a corner by shunning polymorphism.

Hard core hackers and computer science mavens probably deem my questions elementary at best. I don't deny that. Understand, though, that I work in a corporate development world. We don't write journaling filesystems or Fourier transforms or even sorting algorithms. We get data from a database and put it on a web page. I'm just trying to separate the cut-and-pasters from the developers, all right? Besides, I find that the people who can adequately answer all three of my coding questions know plenty. They know about design patterns and refactoring and JUnit and exception handling and POJOs and inheritance and encapsulation and polymorphism and all the stuff I need them to know. It seems that these three problems separate the people who know what they're doing from the ones who copy someone else's code and make modifications to it until it performs some new task.

For the record, the Shape/Rectangle/Circle answer should look something like this:

public interface Shape {
  double getArea();
}

public class Rectangle implements Shape {
  private double width, height;

  public Rectangle(double width, double height) {
    this.width = width;
    this.height = height;
  }

  public double getArea() {
    return width * height;
  }
}

public class Circle implements Shape {
  private double radius;

  public Circle(double radius) {
    this.radius = radius;
  }

  public double getArea() {
    return Math.PI * radius * radius;
  }
}

That wasn't so hard, was it?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.