It’s widely repeated that “great people are 100x more productive than average people.” But while everybody says it, most companies just hire 100x more average people. At Expensify, we try very, very hard to hold the line and only hire people we think are truly great. This means that despite ample resources and more than enough work to go around, we hire extremely slowly — and spend an enormous amount of energy doing it. A lot of that energy is directed toward refining the hiring process itself, with a major recurring topic being: what makes someone great?
Last week we had a long conversation on this within the engineering team, and the main conclusion was — there is nowhere near consensus on this topic. Strong, conflicting opinions were held across the team. I don’t know that there was a single tangible item that everybody agreed was a true minimum requirement, or a true showstopper.
Accordingly, so far as our “official hiring policy” goes, we’re still far from having a checklist evaluation of candidates. For better or worse (and I’d argue “for better”), everybody is evaluated on an individual basis, and pretty much any single attribute is fair game. However, it seems to me that there are three general themes we use to assess the “minimum viable employee” — whether someone meets the most baseline requirements for us to say yes:
Aspires to Excellence
In our team conversation last week, we reviewed a series of programmer applications to share thoughts on each. To everybody’s surprise, by far the most discussion was on something that seems so trivial on the surface: grammar, punctuation, and capitalization of the cover letter. Everybody kept saying “ok, we’ve talked about this enough”, but then everybody kept talking. It was strange, and we intentionally stopped to meta-discuss why we were so concerned by this. We tossed around a couple possible explanations for why this seemed so important (to some of us), and like everything, there was no consensus. But my view is this:
Someone who doesn’t proofread and spellcheck something like a cover letter simply isn’t trying their hardest. Whether they don’t care enough to try, or don’t try even when they do care — either way, this is someone who isn’t putting in the effort to excel, and that’s not someone we want on the team.
To be clear, this isn’t to say that grammar is the sign of excellence. It’s only saying that poor grammar signifies a lack of excellence. Other items can too, including:
- Being late for a call (even by a few minutes)
- Being slow or failing to respond to an email
- Inconsistent indentation and other style
- Poor variable nomenclature
- Terse answers to open-ended questions
- Lack of enthusiasm or curiosity
Note how none of this relates to skill: everybody “knows” how to do all these, and to not do them is a choice. Because in practice these aren’t individual choices, but one large choice — and this decision to consistently excel in everything under your control is very nearly an innate quality. Either you do it, or you don’t, and no amount of cajoling is going to change that (at least, not fast enough for our needs). And for this reason, I’d say any indicators about the candidate’s aspiration to excellence weigh extremely heavily on my go/no-go decision, and for many others on our team.
Knows Enough to Learn Fast
It’s hard to say that this second item is less important as the first, as both are necessary but insufficient: you need to both demonstrate that you aspire to excellence, and have also acquired the basic skills to enable you to achieve it. As for what exactly those minimal skills are is a subject of intense debate. Most of the team would generally say we shouldn’t hire anyone with less skills than they had when they joined, and some would even say their own skills are too low a bar. But from where I sit, I think the minimum set of skills — for a programmer, at least — is something like:
- Can create something from scratch. There are a surprising number of pretty good programmers that haven’t ever started a project from scratch, and don’t actually know how. This means setting up some kind of development environment and getting “Hello world” to successfully run in some relevant language.
- Doesn’t need a framework. Don’t get me wrong, the right framework can be a huge accelerant to a project. But it’s important to avoid becoming overly dependent on any particular framework otherwise you lose objectivity — and use it not because it’s actually right for the problem at hand, but because you simply can’t do anything else. So knowledge of a framework gets bonus points, but the ability to work outside a framework is a prerequisite.
- Successfully finishes the challenge. It sounds obvious, but if we give a programming challenge with an unlimited timeframe, we expect the candidate to deliver a functioning result. It’s amazing how many people send something that simply doesn’t work.
- Actually writes their own code. Additionally, it’s clear that many applicants just copy/paste snippets they found online, without even the decency to hide it by reformatting it into a consistent style. Certainly, the ability to leverage the internet to help you solve problems is a great skill. But learning and applying a lesson is different than copying a code snippet without understanding.
- Reuses code appropriately. It’s not enough to create a functioning result, it also needs to be done in a way that minimizes redundancy. This means pulling out shared code into reusable functions, reusing rather than recalculating outputs, etc. Great people are naturally efficient, making minimal, deliberate actions to achieve their goals.
- Makes sound layering decisions. Knowing where to solve a problem is at least as important as knowing how. The ideal candidate intuitively recognizes those areas that are tightly coupled and should be kept together as a single unit, from those that are loosely coupled and should be isolated through formal interfaces.
And for me, that’s really about it. I think a fantastic application is one that has a solid, obvious solution, with consistent style, good layering, and appropriate reuse. A great application doesn’t need to do much. But what it does, should be done right. This shows the candidate has a solid, clean foundation on which new skills can be quickly learned. Because in the fast-changing world of computer programming, how much you can learn is far more important than how much you know.
Knows Little that Needs Unlearning
This last item is at least as important as the first two, and since those were deemed “necessary but insufficient” I’d say the same about this. Even more important than what you need to learn, is what you need to unlearn. It’s at least twice as expensive (but probably more like 4x) to unlearn a bad skill than to learn a new skill from scratch. This is because the cost to learn the skill is the same, but then you need to add the “unlearning cost” — which is probably more expensive than what it took to learn in the first place. Here are a few hard habits to break for which we’re always on the lookout:
- Overengineering. Defensive programming to anticipate and handle a wide range of scenarios can be great. But it’s easy to go too far, creating custom logic for scenarios that are extremely unlikely in practice, complicating the solution (and often introducing bugs) for the much more common cases.
- Excessive encapsulation. Abstraction is a powerful tool for reuse, as well as to create layered decoupling. But done poorly, “unused reusability” can add such bloated and complicated overhead so as to leave the codebase larger, more fragile and less understandable than before it was “simplified” with abstraction.
- Premature optimization. It’s good to be efficient, and to naturally take advantage of low-hanging-fruit performance opportunities where convenient and clear. But the most important thing to optimize for is code clarity, and that should only be compromised when genuinely necessary.
- Unnecessary cleverness. Modern languages have a huge range of esoteric features that enable for enormously powerful and succinct solutions. But great candidates amaze with simplicity rather than sophistication.
Admittedly, all of those bullets are really just different flavors of the same thing: solving problems that don’t actually exist, while creating new problems in the process. It’s insidious, as good programmers can imagine a wide range of potential future problems — ranging from performance, to maintainability, to extensibility, and more — and the temptation to solve them all right now can at times be overwhelming. But we need people with strong impulse control who can remain focused on solving the problems of today, without adding to them unnecessarily.
Anyway, I think these are three high level themes we use when evaluating a candidate. (And if it wasn’t already apparent, note that where you went to school, what degree you have, where you’ve worked, who has referred you — none of that matters, at all, and I think that’s the one thing we all agree on.) But getting more detailed than that is extremely contentious, and despite how helpful it would be, there really is no such thing as a clear “minimum viable employee” so far as we can tell.
So if you want to impress me, keep the above criteria in mind. But in all honesty, I’m not the only one (or even the most important one) you want to impress – so view my suggestions above as “necessary but not sufficient” to get the job.