Friday, January 23, 2009

Use Python or Ruby for your next job interview

You're at a job interview at some big software company and you need to write code that...oh I don't know... reverses the order of words in a string.

What language would you use for the job? Sure, you spend all day coding in Java, but you also work on enterprise software that runs on app servers, parses XML, connects to relational databases, and does other typical enterprisey stuff. You design complicated class hierarchies and testing frameworks. Your dependencies are all injected, control all inverted. Your frameworks and APIs are legendary.

But here you are...it's 10 am and you have a whiteboard, some markers, and some simple string manipulation to do (when was the last time you had to manipulate strings anyway?). You're nervous, the clock's ticking, and now that you've got the O(N) solution in your head, you need to express it in working, readable code... and fast!

So you break out your best Java, and suddenly the language presents you with a number of distracting questions. Do you return an array of Strings or a String of words? Do you use a List or an array (and possibly arraycopy)? Since it won't technically run without one, do you bother defining a class? Is the method public, private, protected, or default? You know static is evil, but should you just make it static for the sake of the interview?

Here's how I envision a typical* interview for an entry-level position...

Java Jimmy

JJ: "OK, I think I have a working solution in my head, I'll just write it up as a method...I'll have it take a String and return a String, and I'll call it reverseWords."

public String reverseWords(String words) {

JJ: "Oh, wait, is it ok that this isn't in a class? I know it won't run outside of a class, so maybe I should play it safe."

class StringUtil {
public String reverseWords(String words) {

Me: "Hey, don't worry about that, a simple method is fine."
JJ: "And I can skip main?"
Me: "Yes, that's fine, this is just a utility method somewhere."
JJ: "OK, I should probably make it static, even though static is evil."

public static String reverseWords(String words) {

JJ: "Do you care about being super efficient? I could use String concatenation or StringBuilder."
Me: "Whatever you prefer."
JJ: "Ok, since using StringBuilder.reverse is probably cheating, I'll use Strings and an index."

public static String reverseWords(String words) {
String reverse = "";
for (int i = words.length()-1; i>=0; i--) {
reverse += words.charAt(i);
}
// now reverse words...

JJ: "Should I use StringTokenizer or regular expressions?"
Me: "Whatever you prefer."
JJ: "Hm, OK, StringTokenizer is pretty easy, so I'll use that. Do you want me to write the import statement?"
Me: "No, that's fine."
JJ: "Great."

String result = "";
StringTokenizer st = new StringTokenizer(reverse);
while (st.hasMoreTokens()) {
String word = st.nextToken();
for (int i = word.length()-1; i>=0; i--) {
result += word.charAt(i);
}
if (st.hasMoreTokens()) {
result += " ";
}
}
return result;
}

Me: "Great, now how does it perform?"
JJ: "Oh, well jeez, Strings are immutable, and I'm using string concatenation, so I should probably use StringBuilder...I'd never actually write it this way in production."
Me: "Sure, go for it."

public static String reverseWords(String words) {
String reverse = new StringBuilder(words).reverse()
.toString();

// now reverse words...
StringBuilder result = new StringBuilder();

StringTokenizer st = new StringTokenizer(
reverse.toString());

while (st.hasMoreTokens()) {
String word = st.nextToken();
for (int i = word.length()-1; i>=0; i--) {
result.append(word.charAt(i));
}
if (st.hasMoreTokens()) {
result.append(" ");
}
}
return result.toString();
}

JJ: "How's that?"
Me: "OK, great, now that you're warmed up, let's get to the main question."
JJ (inner monologue): "Holy crap, I only have 25 minutes left!"


Peter Python

PP: "OK, I'll define a simple method and use Python lists freely."

def reverse_words(words):
result = []
words = words[::-1]
for word in words.split():
result.append(word[::-1])
return ' '.join(result)

Me: "Great, can you make it even shorter?"
PP: "Oh! Of course!"

def reverse_words(words):
return ' '.join(words.split()[::-1])
Me: "And how does it perform?"
PP: "O(N), assuming that reverse, split, and join are all O(N), and why wouldn't they be?"
Me: "Nice work."

Rebecca Ruby

RR: "Oh fun! This'll be easy to code up in Ruby, is that ok?"
Me: "Sure, whatever you prefer."
RR: "OK, let's see, I want to reverse the words, split them, reverse each item, and then join them again."

def reverse_words(words)
words.reverse.split.map{|w| w.reverse}.join(' ')
end

RR: "I think I'm done. I hope I did it right. I just used some pretty basic ruby."
Me: "Wow, that was fast. What's the runtime?"
RR: "Assuming that reverse, split, and map are O(N) (and why shouldn't they be), this is O(N)."

Here are a few other things you'll have an easier time doing on your interview with Python or Ruby:
  • Easily work with typical interview datatypes like strings, numbers, and lists.
  • Slice and dice collections and strings
  • Read to and from files
  • Dynamically grow or shrink lists (it's amazing how often you actually need this in an interview)
  • Create and manipulate hashtables (also comes up a lot in interviews)
And if the interview's at a workstation you can easily:
  • Generate test data
  • Iterate over your solution and profile how long it takes for various datasets
  • Just run your program. You don't need to compile it, you don't need "public static void main", you don't need classes, and you don't need to explain how you're using static even though you know it's a Bad Idea. And you don't need to choose between complicated command-line classpaths and big IDEs.
Give Python or Ruby a try at your next interview. And if you don't know either, what are you waiting for?

Tuesday, January 13, 2009

Teach students to write open source software

A few years ago I proposed a new course at NYU: Production Quality Software. It requires students to complete weekly assignments, conduct code reviews, write unit tests, think carefully about API design, and repeatedly iterate on their solutions based on peer feedback. Even the submission process was reworked with real software engineering practices in mind...every student gets a Subversion repository. There's no reason to use email when svn export works just fine. (And why not teach them another tool?)

To drive home the idea that sharing code is a Good Thing, and to teach them the joys and perils of open source, the final project is a contribution to an open source project of their choice.

Fall 2008 marked the third time I've taught this course. Student reviews came back positive, and the list of open source project contributions continues to grow:
If you teach software engineering, consider asking your students to share code with each other (after you've collected it) and to work on open source. Give them the satisfaction of knowing that their work is not an academic exercise, but a real contribution to a living breathing product.

If you're a student, consider doing code reviews with your peers once your assignment is over. A session with half a dozen other programmers will surely yield more suggestions than your TA or Professor (you're probably the 42nd implementation they're reading). Make it a regular habit and you'll find your code gets better with every assignment.