Embarrassing code I wrote under stress at a job interview

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

(Note: this post was, in part, inspired by John Lawrence Aspden’s post about FizzBuzz.)

I write terrible code when I go to a job interview. That’s mostly because, when they ask me to solve a coding question, I get nervous. I thought it might be entertaining if I wrote about one such encounter.

Recently I went to a job interview, at a company in New York that had once built their stack (for managing online advertising) in Ruby but who are now transitioning all of their stuff to Clojure (Adaptly).

On this particular day, I was to talk to 3 of their senior developers, and 2 of them would hit me with their favorite questions about how to find something in a given number series.

When I got to the company office, they put me in a conference room and sent in one of their senior engineers. He asked me to fire up a REPL, so I launched Emacs and then “nrepl-jack-in”. I was ready to go, but I was also very nervous, because of these 3 reasons:

1.) I’ve got a guy looking over my shoulder and he’s watching every mistake I’m making

2.) I’m suddenly self-conscious about the prettiness of my code — it doesn’t matter if I can solve the problem, since I’m also competing against candidates who can solve the problem, so the real competition is probably about how elegant and idiomatic our code is

3.) this isn’t just about getting the job done, it’s also about being quick — the pressure to move fast is strong

I was typing code into a live environment, which would then execute my code when I hit “enter”. For those of you who haven’t seen the REPL in Emacs, my screen looked like this:

In addition to the above problems, one extra problem I eventually faced wasn’t Clojure or math, it was English — we had a misunderstanding in our native language!

He asked me if I knew what the “Happy number sequence” was. I said I had never heard of it. He described it like this:

“Take each digit in a number and square it, then add the sums together. Keep doing this recursively.”

The next sentence of English is where the problem started. I thought he said:

“The sequence will either go to one or to infinity.”

but he actually said:

“The sequence will either go to one or infinitely.”

He then offered an example of how to find the next number in the sequence:

“Suppose you start with the number 31. Well, 3 squared is 9 and 1 squared is 1, and 9 and 1 added together is 10, so the next number in the sequence, after 31, is 10.”

And then, with “10”, the “1” squared is 1 and the “0” squared is 0, and when you add 1 and 0, you get “1”, therefore we know that “31” is part of the Happy sequence, because, after 2 iterations, it ended up being “1”.

That much was obvious, but I could not figure out how to test a sequence that goes to infinity — how would I know when to stop testing for more numbers? I mean, I might get to a septillion, but I’d only be taking a small step toward infinity — and then I would need to keep testing till I get to infinity… which is impossible? But I wanted to appear smart, so I didn’t express any confusion. I was thinking that if I dove into the problem, then it would eventually make sense to me. So I started coding in the REPL. The first thing I needed was a function that would find the next number in the sequence, and this would surely be easy:

user> 
(defn [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc)
   (recur
      (rest snc)
      (+ (+ (* (first snc) (first snc)) sum)))


but that got me:

     
RuntimeException EOF while reading, starting at line 4  clojure.lang.Util.runtimeException (Util.java:219)


Oh hell, why am I so clumsy? I tried again:

user> 
(defn [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (+ (* (first snc) (first snc)) sum))))))


and that got me:

      
IllegalArgumentException First argument to defn must be a symbol  clojure.core/defn (core.clj:277)


Embarrassing! And now I felt even more nervous! So I tried:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (+ (* (first snc) (first snc)) sum))))))


and I got:

      
CompilerException java.lang.RuntimeException: Unable to resolve symbol: sum in this context, compiling:(NO_SOURCE_PATH:7:10) 


Ugh! What a stupid typo! I tried again:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (+ (* (first snc) (first snc)) snc-sum))))))
#'user/happy


Finally! At least it compiled! Now maybe I can repair my damaged credibility! Let’s try to use it:

user> 
 (happy 31)
ClassCastException java.lang.Character cannot be cast to java.lang.Number  clojure.lang.Numbers.multiply (Numbers.java:146)


Oh, good lord. This was the only way I could think to get each digit in a number:

snc (str x)

but this actually gave me a sequence of Characters, not strings. In the real world, I would have stopped at this point, gone to look at the documentation, and maybe found a more elegant way to do this. However, under pressure of time, I just started piling on bandaids to try to fix things enough to get it to work:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (+ (* (int (first snc)) (int (first snc))) snc-sum))))))
#'user/happy
user> 
 (happy 31)
nil


Damn. I forgot to return any value from the loop. So I tried again:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (+ (* (int (first snc)) (int (first snc))) snc-sum))))   snc-sum))


And I get:

CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position, compiling:(NO_SOURCE_PATH:5:4) 


Now I am angry with myself. This whole process is dragging on too long, and I keep making simple mistakes. And it’s a job interview!

I try again:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (+ (* (int (first snc)) (int (first snc))) snc-sum))) snc-sum)))
#'user/happy


Finally! It compiled! Let’s see it work:

user> 
 (happy 31)
2601


Ah, damn! The correct answer is “10”, but I am getting 2601! What the hell went wrong?

At this point, the guy interviewing me is starting to get bored. I can see his eyes drifting elsewhere, he starts checking his phone for messages. I am thinking that in his mind he has already decided that I’m a pathetic loser. In a different industry, he might simply shout “Next” and kick me out and drag the next applicant in, but our industry is slightly more polite than that. So he waits for me to stop screwing up. But he looks very bored.

I notice that I am, for some strange reason, summing twice, so I take out the redundant “+”:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (* (int (first snc)) (int (first snc))) snc-sum)) snc-sum)))
#'user/happy
user> 
 (happy 31)
2601


Damn! Damn! Damn! What the hell is wrong with my code?

In retrospect, the error is obvious, but under the pressure of time, my mind was racing and I was not seeing the problem, so I added some print statements so I could better understand what was happening:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]  (print (rest snc))  (print (first snc))
 (if (seq (rest snc))
   (recur
      (rest snc)
      (+ (* (int (first snc)) (int (first snc))) snc-sum)) snc-sum)))
#'user/happy
user> 
 (happy 31)
(1)3()1
2601


Hmm, so, the sequence of numbers is exactly what I thought it would be, so everything should work perfectly, but it is not because…

Oh, right! I am casting a string to an integer, and getting back the ASCII value of the string. Awful!

So, I try to fix that:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  
      (+ (* (.parseInteger (first snc)) (.parseInteger (first snc))) snc-sum)) snc-sum)))
#'user/happy
user> 
 (happy 31)
IllegalArgumentException No matching field found: parseInteger for class java.lang.Character  clojure.lang.Reflector.getInstanceField (Reflector.java:271)


Hell! What is the right method call? I’ve done this many times before, I just can’t recall, right now, the way to write this. I turn to the guy and ask him permission to look this up in my old code. He gives me permission. I look at some code that I’ve written and see the correct way, and so I can do this correctly:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  
      (+ (* (Integer/parseInt (first snc)) (Integer/parseInt (first snc))) snc-sum)) snc-sum)))
#'user/happy


It compiles! So I try it:

user> 
 (happy 31)
ClassCastException java.lang.Character cannot be cast to java.lang.String  user/happy (NO_SOURCE_FILE:7)


FML!!!!!

I know there must be something elegant I can do here, but for now I go for the fastest shortcut I can think of:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum)) snc-sum)))
#'user/happy


Holy hell, this code is ugly! If someone came into a job interview, and I was the one doing the interview, I would have some doubts about the candidate if I saw them write this code.

But anyway, I try it:

user> 
 (happy 31)
9


Hmm, very close! I want “10” but I get “9”. What did I do wrong? Oh wait, I forgot to add in the “false” clause of the “if” statement! That is, what happens if we don’t recur?

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  (do (print (first snc))
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum)) snc-sum))))
CompilerException java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 2 args, got: 3, compiling:(NO_SOURCE_PATH:5:4) 


Uh, so, I have a bracket in the wrong place? I have keybindings set up so that Control-1 takes me to the start of any form, and Control-2 takes me to the end of any form, so I can usually, rather easily, see how the brackets line up. But that doesn’t save me from bad thinking under stress.

Maybe I’ll add in a print statement again:

user> 
(defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (first snc))
   (recur
      (rest snc)  (do (print (first snc))
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum))) snc-sum)))
#'user/happy
user> 
 (happy 31)
3
9


So, this never gets to the “1”, it only does the “3”. Why is that? Maybe instead of this:

(seq (rest snc))

I should do this:

(seq (first snc))

So I try:

user> 
 (defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (first snc))
   (recur
      (rest snc)  (do (print (first snc))
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum))) snc-sum)))

#'user/happy
user> 
 (happy 31)
IllegalArgumentException Don't know how to create ISeq from: java.lang.Character  clojure.lang.RT.seqFrom (RT.java:505)


Yeah, what a stupid idea. I change it back, since that is obviously not where the problem is:

user> 
 (defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  (do (print (first snc))
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum))) snc-sum)))

#'user/happy


But where is the real problem? Oh, I see, in the “false” clause of the “if” statement, I need to sum the results:

user> 
user> 
 (defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  (do (print (first snc))
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum))) (+ (first snc) snc-sum))))

#'user/happy
user> 
 (happy 31)
ClassCastException java.lang.Character cannot be cast to java.lang.Number  clojure.lang.Numbers.add (Numbers.java:126)
3


FML!!!! FML!!!! FML!!!! FML!!!! FML!!!! FML!!!! FML!!!! FML!!!! FML!!!!

The guy who is interviewing me has mostly stopped watching, because he’s already decided there is no chance in hell that they will be hiring me. He is looking at his phone. I half expect him to start playing Flappy Bird.

I realize now that the verbose and horrible “Integer/parseInt (str” needs to be applied to the sum at the end of the “if” statement:

user> (defn happy [x]
 (loop [snc (str x) snc-sum 0]  
 (if (seq (rest snc))
   (recur
      (rest snc)  (do (print (first snc))
      (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum))) (+  (Integer/parseInt (str (first snc))) snc-sum))))

#'user/happy
user> 
 (happy 31)
3
10


Awesome! I finally got “10”! I finally got the right number!

And yet, I am only at the start of this problem. Writing a simple function to find the next number in the sequence was suppose to be the easy part, and I just wasted 10-15 minutes on it!

So, how do I find out if a number is in a sequence, when the algorithm might lead to infinity? I had previously postponed the question, but now I came back to it. Again, our problem was English, not Clojure.

Thinking out loud, I said: “If the sequence can go to infinity, then I could create a generator that returns the numbers lazily… but at some point we would need to realize those values, to check them… ”

“That is one way to think about it,” he said.

I kept thinking out loud: “…and if they go to infinity, then they will crash my machine…”

“Are you sure?” he said.

I thought long and hard about that. Was I missing something obvious? Was there a way that my machine could count to infinity?

I was quiet a long moment because I was worried that whatever I said next would make me look stupid.

“Well, I could pass in some bound, like perhaps 10,000. I could pursue a finite number of tries before giving up.”

“Are you sure?” he repeated. “Are you sure the numbers go to infinity?”

“Didn’t you tell me that they go to infinity?”

“No,” he said, “I said they go infinitely.”

Finally, the light dawned in my head.

“Oh,” I said, “So they loop?”

“Yes.”

“Oh!” I finally got it. “So they loop over a finite set of numbers?”

“Yes.”

“Ah! So I only need to store the numbers in a set and then I can see if I start to loop?”

“Yes.”

Okay, so now I got it. I could store the numbers in a set, and if I ever saw the same number twice, then I knew that the number that I started with was not in the Happy sequence.

user> 
 (defn happy [x]
  (loop [snc (str x) snc-sum 0 seen-so-far #{}]  
    (if (seen-so-far (Integer/parseInt (str (first snc))))
      nil
      (if (seq (rest snc))
        (recur
          (rest snc) 
          (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum))
          (conj seen-so-far (Integer/parseInt (str (first snc))))
        (+  (Integer/parseInt (str (first snc))) snc-sum)))))

CompilerException java.lang.RuntimeException: Too many arguments to if, compiling:(NO_SOURCE_PATH:5:7) 


Ah, stupid! I’ve got a bracket in the wrong place for that second “if” statement. I’ll try again:

user> 
 (defn happy [x]
  (loop [snc (str x) snc-sum 0 seen-so-far #{}]  
    (if (seen-so-far (Integer/parseInt (str (first snc))))
      nil
      (if (seq (rest snc))
        (recur
          (rest snc) 
          (+ (* (Integer/parseInt (str (first snc))) (Integer/parseInt (str (first snc)))) snc-sum)
          (conj seen-so-far (Integer/parseInt (str (first snc)))))
        (+  (Integer/parseInt (str (first snc))) snc-sum)))))

#'user/happy


Great! It compiles! So now I can:

user> 
 (happy 31)
10


Wait, what am I doing? The “happy” function simply finds me the next number in the sequence. I need a function, higher up the chain, that can loop through the whole series, and call “happy” on each iteration of the loop. I reset the “happy” function to the previous version that was working, and then I start on a new function:

(defn find-happiness [starting-integer]
	(loop [test-integer starting-integer seen-so-far #{}]
	      (let [next-integer (happy next-integer)] 
	      	   (if (seen-so-far next-integer)
		       nil
		       (recur
		         next-integer
		         (conj seen-so-far next-integer))))))


which gives me:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: next-integer in this context, compiling:(NO_SOURCE_PATH:3:27) 


Sloppy! I am not impressing myself.

I have to take a deep breath and think, for a moment, about what I want to return. By this point, so much time has dragged by that the guy who is suppose to be interviewing me has largely stopped paying attention to what I’m doing. I figure the interview is a bust anyway, so I can take a moment and think about things clearly. The pressure of time disappears once I realize there is no chance I am getting this job.

I know I want this:

	        (if (= next-integer 1)
		   true


That is, if the next number in the series is ever 1, then we know the starting number does belong to the Happy sequence, so we should return true.

And I know I want:

	      	   (if (seen-so-far next-integer)
		       nil


That is, if I have a set called “seen-so-far”, and I store all the numbers that I discover as I iterate through sequence, then if ever I see a number twice, I can conclude that the series is settling into an infinite loop, and I can return false, because then I will know that the starting number is definitely not part of the Happy sequence.

So this should work:

(defn find-happiness [starting-integer]
	(loop [test-integer starting-integer seen-so-far #{}]
	      (let [next-integer (happy test-integer)] 
	        (if (= next-integer 1)
		   true
	      	   (if (seen-so-far next-integer)
		       nil
		       (recur
			 next-integer
		       	 (conj seen-so-far next-integer)))))))
#'user/find-happiness


So I try it:

user> 
 (find-happiness 31)
true
user> 
 (find-happiness 32)
nil
user> 
 (find-happiness 33)
nil
user> 
 (find-happiness 34)
nil


Nice!!!! Very cool!!!

How about the first 100 numbers?

(map find-happiness (range 100))
(nil true nil nil nil nil nil nil nil nil true nil nil nil nil nil nil nil nil true nil nil nil nil nil nil true nil nil nil nil true nil nil nil nil nil nil nil nil nil nil nil true nil nil nil nil nil nil nil true nil nil nil nil true nil nil nil nil nil nil nil nil nil nil true nil nil nil nil true nil nil nil nil true nil nil nil nil nil true nil nil nil nil true nil nil nil true nil nil nil nil true nil nil)


Hmm, cool, but that is hard to read. Let me add in some print statements, so I can better understand the output:

user> 
 (defn find-happiness [starting-integer]
        (println (str starting-integer))
	(loop [test-integer starting-integer seen-so-far #{}]
	      (let [next-integer (happy test-integer)] 
	        (if (= next-integer 1)
		   (do
		     (println " true ") 
		     true)
	      	   (if (seen-so-far next-integer)
		       (do 
		         (println " false ")
		         nil)
		       (recur
			 next-integer
		       	 (conj seen-so-far next-integer)))))))
#'user/find-happiness


And then:

user> 
 (map find-happiness (range 100))
(0
 false 
1
 true 
2
 false 
3
 false 
4
 false 
5
 false 
6
 false 
7
 false 
8
 false 
9
 false 
10
 true 
11
 false 
12
 false 
13
 false 
14
 false 
15
 false 
16
 false 
17
 false 
18
 false 
19
 true 
20
 false 
21
 false 
22
 false 
23
 false 
24
 false 
25
 false 
26
 true 
27
 false 
28
 false 
29
 false 
30
 false 
31
 true 
32
 false 
33
 false 
34
 false 
35
 false 
36
 false 
37
 false 
38
 false 
39
 false 
40
 false 
41
 false 
42
 false 
43
 true 
44
 false 
45
 false 
46
 false 
47
 false 
48
 false 
49
 false 
50
 false 
51
 true 
52
 false 
53
 false 
54
 false 
55
 false 
56
 true 
57
 false 
58
 false 
59
 false 
60
 false 
61
 false 
62
 false 
63
 false 
64
 false 
65
 false 
66
 false 
67
 true 
68
 false 
69
 false 
70
 false 
71
 false 
72
 true 
73
 false 
74
 false 
75
 false 
76
 false 
77
 true 
78
 false 
79
 false 
80
 false 
81
 false 
82
 false 
83
 true 
84
 false 
85
 false 
86
 false 
87
 false 
88
 true 
89
 false 
90
 false 
91
 false 
92
 true 
93
 false 
94
 false 
95
 false 
96
 false 
97
 true 
98
 false 
99
 false )


Very cool!

And then I wanted to test one of these by hand just to make sure everything was working the way it should. It says that 97 is in the Happy sequence, so I tested to see if that was true:

user> 
 (happiness 97)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: happiness in this context, compiling:(NO_SOURCE_PATH:1:1) 


The level of self-sabotage is impressive, isn’t it?

Let’s try that again with a function I actually wrote:

user> 
 (happy 97)
88
user> 
 (happy 88)
72
user> 
 (happy 72)
51
user> 
 (happy 51)
26
user> 
 (happy 26)
10
user> 
 (happy 10)
1


Cool! It does work! And this is a cool number series!

Needless to say, the job interview was a disaster. I think they want someone who can write something like this in 5 minutes, but between the English-language confusion over the definition of the sequence, and the many mistakes I made, this dragged on for almost 30 minutes.

After I was done, the guy said something polite about “good effort” and then he said he would sent in the next engineer.

A few minutes later the next engineer came in. He asked me if I knew what the Collatz number sequence was. I said no. He drew a definition of it on the white board. He asked me to write some functions that would allow me to find the longest iterations through the Collatz sequence, given a set of starting numbers.

What followed was similar to the above: for more than 30 minutes I stumbled through the process, making dozens of mistakes as I went.

Interviews are weird: the pressure of time, and not being able to look things up, distorts the code. I am aware that the code I wrote was ugly, especially the atrocious casting of the number to a string, then to a sequence of Characters, then back to a string, and then back to an integer. But now that I am back in the real world, and able to do a Google Search, a quick search brings a top result that points me to an example by Saul Hazledine:

(map #(Character/digit % 10) (str number)))

So, in the real world, that is what I would have gone with and my code would have looked a bit better.

I guess there are engineers who stay calm in situations like this, and I assume this must be true of all the programmers who got hired at the place. I guess this is a bit like the situation in basketball, do you panic when a defender is faster and taller than you, or do you stay calm and pass the ball in an intelligent way. These tests perhaps bring out how I write code under pressure, like if its the end of the sprint and I’m working on a task that absolutely has to go out during this sprint, though hopefully such crunch-mode programming makes up less than 5% of our programming, since this is when the worst kinds of “technical debt” gets created. Or, possibly, tests like this allow them to hire those programmers who never create “technical debt” no matter how much pressure they are under — but I would be skeptical of that claim.

[UPDATE] David Tuite writes:

Expressing confusion in an interview doesn’t make you appear dumb. In reality it’s quite the opposite.

I would take that advice with a grain of salt. Some interviewers are good and some are bad. David Tuite’s advice is common, but sometimes it is wrong. Multiple studies show that your questions can have a subconscious effect on the person interviewing you. Even if they say “Please feel free to ask questions” if you phrase the question the wrong way, or ask a question outside the bounds of what they were expecting, it becomes a mark against you. The effect can be subconscious, but the bias will cost you a job. When you are in the presence of someone who really wants you to ask questions, that typically becomes apparent after a few minutes of talking to them, and in those cases you can feel free to ask what’s relevant — however, it is unwise to start with the assumption that the person who is interviewing you will automatically give you the benefit of the doubt — it is much wiser to start off cautiously and figure out who you are talking to, before you ask too much.

[ [ UPDATE ] ]

Conversation at Hacker News

Source