If someone successfully works shipping code but can't reverse a linked list, does that mean they're lying about being able to code? I've seen way to many jobs that test for expert level skills for a job that is just to add small enhancements.
If someone successfully works shipping code but can't reverse a linked list, does that mean they're lying about being able to code? I've seen way to many jobs that test for expert level skills for a job that is just to add small enhancements.
On a whiteboard? Probably not.
In front of a computer, with a compiler and manpages handy, being able to test and debug it right away? Absolutely, yes.
No, I think it means they didn't prepare. But, short of examining git commits, how can you know that someone successfully "shipped code"? References, maybe (though that is harder for folks who may have fewer references from past jobs). If it is open source, sure. But how else?
> I've seen way to many jobs that test for expert level skills for a job that is just to add small enhancements.
Yes, I agree! That is a bigger issue, where the job desc/interview are disjoint from the actual work. That's a red flag. It's hard, as a hiring manager, though, to keep the job desc real. I find that I sometimes throw wish list items in ("knows <tech> at an intermediate level") because the alternative ("can learn <tech>") is so hard to test for. But as a candidate I find it extremely aggravating.
I don't know how to square that circle. If you figure it out, please let me know.
My favorite kind of interview (on both sides of the table) is where I can do the work that is typical, using the resources that would be available on the job.
Obviously yes? That's a basic assignment for a data structure class given to freshmen and sophomores. One would expect that a "senior" developer (degreed or self-taught) would be better than a college sophomore. At the FAANG level, it would be a phone screen question and a softball one at that.
I think the problem is that there are over a dozen different kinds of "seniority" but we collapse them all into one term. I got so frustrated I wrote a blog post about it: https://www.mooreds.com/wordpress/archives/2812
Also there are ancillary skills that make the problem much less difficult, so if you can't solve it, you don't have them. Joel Spolsky famously said that nobody can reverse a linked list without drawing diagrams on paper, and that's probably true if you're doing it imperatively in C. But it's in the category of "problems that become much simpler with functional styles of programming." The following took me 5 minutes, most of which was struggling with the syntax of OCaml that I'd forgotten:
$ ocaml
OCaml version 4.08.1
# type 'a list = Cons of ('a * 'a list) | Nil;;
type 'a list = Cons of ('a * 'a list) | Nil
# Cons(3, Cons(41, Nil));;
- : int list = Cons (3, Cons (41, Nil))
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' (Cons(x, ys)) in rev lst Nil ;;
val reverse : 'a list -> 'a list = <fun>
# reverse(Cons(3, Cons(41, Nil)));;
- : int list = Cons (41, Cons (3, Nil))
The full session with all my dumb errors: $ ocaml
OCaml version 4.08.1
# type 'a list = Cons of ('a * 'a list) | Nil;;
type 'a list = Cons of ('a * 'a list) | Nil
# Cons 3 (Cons 41 Nil) ;;
Error: Syntax error
# Cons(3, Cons(41, Nil));;
- : int list = Cons (3, Cons (41, Nil))
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' Cons(x, ys) ;;
Error: Syntax error
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' Cons(x, ys) in rev lst Nil ;;
Error: The constructor Cons expects 1 argument(s),
but is applied here to 0 argument(s)
# let reverse lst = let rec rev xs ys = match xs with Nil -> ys | Cons(x, xs') -> rev xs' (Cons(x, ys)) in rev lst Nil ;;
val reverse : 'a list -> 'a list = <fun>
# reverse Cons(3, Cons(41, Nil));;
Error: This function has type 'a list -> 'a list
It is applied to too many arguments; maybe you forgot a `;'.
# reverse(Cons(3, Cons(41, Nil)));;
- : int list = Cons (41, Cons (3, Nil))
#
(These error messages look appalling, but the OCaml interpreter was underlining my errors, which of course didn't paste.)Similarly, there are problems that become much easier with other paradigms of programming, such as backtracking search.
Anyways it is a bad question to ask because it is very easy and is frequently studied so candidates just memorise the answer.
As it turns out, designing a fast algorithm involves a lot more than what is covered in the textbooks. This is why something like Timsort can generally outperform quicksort even though a naive algorithms course overview may well come to the opposite conclusion.
If my job is not to design these algorithms, why does it matter if I can write a naive implementation of one?
I mean, it's true that there's a lot of code that really is at the FizzBuzz level:
switch (c) {
case 'h': case 'D': menu.left(); break;
case 'j': case 'B': menu.down(); break;
case 'k': case 'A': menu.up(); break;
case 'l': case 'C': menu.right(); break;
default: /* no op */ break;
}
def find(self, name):
for thing in self.contents:
if thing.is_a(name):
return thing
vaders.hits(bullets, (vader, bullet) => {
blam(vader, 2.5)
score += vader.bounty
vader.die()
bullet.die()
})
if (vaders.where(({y}) => y > 400).count()) startGame(120)
if (!vaders.count()) startLevel()
And I think you should write as much of your code as possible in such a painfully obvious way. Every once in a while, though, you do need to write something that can't be written quite that simply. Often, the reason why is something in your problem domain: async def take_token(self):
self.update()
while not self.tokens:
await asyncio.sleep(self.recharge / 10)
self.update()
self.tokens -= 1
// Animate explosions.
poof.move()
poof.each(p => {if (--p.ttl < 0) p.die()})
// Chain-reaction explosions.
poof.about_every(64).each(p => {
if (poof.count() < 1024) blam(p, p.w/12)
})
// Limit number of explosion objects to guarantee responsivity
if (poof.count() > 1024) poof.about_every(2).die()
You need to be able to reason through what your code will do once you've written it, and you need to be able to come up with possible ways to write it that might work. Often the code you need for your problem domain isn't already in the standard library, so you have to write it. If it's on npm, you have to be able to read it to see whether it might work.Reversing a linked list is just about the simplest task that requires that kind of reasoning, and it has the advantage (or disadvantage) that understanding the puzzle doesn't require, or benefit from, any problem-domain knowledge. You could substitute binary search or insertion sort or something. Another example I got, which takes a few more lines of code, was "implement an integer stack ADT which can always tell you the maximum number on the stack in constant time".
I agree Leetcode questions are generally not a good signal because they're often removed from everyday problems one may encounter. Though they may be fun to do. If I could get a job just solving Leetcode problems, I might chose it over refactoring messy codebases or joining disparate APIs.