The Average Test
I was conducting technical interviews recently for a senior developer position. I like to talk about a bunch of things, but there is one question I particularly enjoy asking each candidate. Write an implementation for this:
1 | int Average(int a, int b) |
It’s a great question because it’s a simple problem, and anyone that comes through the door should know the formula without having to ask. It’s interesting how people react to it sometimes, but I chalk it up to interview jitters. About half the people interviewed answer like this:
1 | int SimpleAverage(int a, int b) |
It’s a good first answer too, except that it doesn’t always work.
“What will be the result of your function if it’s called with the values Int32.MaxValue and Int32.MaxValue?”
The answer I want to hear is that it won’t work. Some people think (like I did), that it will throw a System.OverflowException, but it actually returns -1. The default for C# projects is to skip overflow checking for arithmetic.
This is another answer that I get from time to time:
1 | int SystemMathAverage(int a, int b) |
I respect this answer; it indicates that a developer wants to leave low-level details to the framework, which is a perfectly reasonable attitude when developing business-class software. Unfortunately there are two problems with this answer: it defeats the purpose of the test, and it doesn’t exist in the framework. There is the Linq average statement which can be used like this:
1 | int LinqAverage(int a, int b) |
After the first attempt, I indicate that I want a function which will work correctly for all values of a and b. The way candidates tackle it tells you a lot about them as a developer. Here are some behaviours I’ve noticed:
- Very few candidates stop to consider a solution before answering.
- About 3/4 of candidates try to use an if statement.
- About 1/4 of candidates write the “if” on the board before deciding what to use it for.
- Occasional candidates will challenge the necessity for handling high values.
A clean correct answer is hard to pull off on the spot, especially under pressure. I’ve never seen it, and I wouldn’t be surprised if I never do. This doesn’t bother me either. The key thing I’m looking for is the ability to understand questions and discuss the source code. These skills are essential to working in a team.
How did I come up with such a brilliant interview question? I didn’t. Once upon a time in a company far far away, someone asked me the very same thing. I was pretty proud of my answer too, until I tested it. This was what I came up with:
1 | int JessesAverage(int a, int b) |
I wrote a test harness in some spare time after the last round of interviews. Here’s how each of the answers measure up:
Results for SimpleAverage():
Near Zero: 121 of 121 passed.
End of Range: 0 of 3 passed.
Failed Values:
- SimpleAverage(2147483647, 2147483647) returns -1, but should return 2147483647
- SimpleAverage(-2147483648, -2147483648) returns 0, but should return -2147483648
- SimpleAverage(2147483647, 2147483645) returns -2, but should return 2147483646
Results for LinqAverage():
Near Zero: 121 of 121 passed.
End of Range: 3 of 3 passed.
Results for JessesAverage():
Near Zero: 109 of 121 passed.
End of Range: 3 of 3 passed.
Failed Values:
- JessesAverage(-4, 1) returns -2, but should return -1
- JessesAverage(-4, 3) returns -1, but should return 0
- JessesAverage(-3, 4) returns 1, but should return 0
- JessesAverage(-2, 1) returns -1, but should return 0
- JessesAverage(-1, 2) returns 1, but should return 0
- JessesAverage(-1, 4) returns 2, but should return 1
- JessesAverage(1, -4) returns -2, but should return -1
- JessesAverage(1, -2) returns -1, but should return 0
- JessesAverage(2, -1) returns 1, but should return 0
- JessesAverage(3, -4) returns -1, but should return 0
- JessesAverage(4, -3) returns 1, but should return 0
- JessesAverage(4, -1) returns 2, but should return 1
This was the best answer I could come up with after some experimentation. I’m not sure why, but the need for type conversion bugs me.
1 | int ConversionAverage(int a, int b) |
Results for ConversionAverage():
Near Zero: 121 of 121 passed.
End of Range: 3 of 3 passed.