On JavaScript, Frameworks, and Hiring
Recently, my coworker & friend Kevin Ennis wrote a compelling article entitled: You’re Thinking About Frameworks the Wrong Way. There is an excellent quote found therein I'd like to discuss:
"My point is that by exaggerating the importance of frameworks, we’re ending up with an army of technicians and no engineers. By pretending that Ember or Angular can solve all of our problems, we give the false impression that simply learning the “right” framework is all you need in order to be effective. But it’s not."
-- Kevin C. Ennis, Esq.
Anyone that knows how I feel about JavaScript and web development writ large will not be surprised at all that I entirely agree with his sentiment here. But I'd like to expound a bit on a more subtle inference here.
If Kevin's note suggests that frameworks often make better technicians than engineers, this begs the question...
How can I be a more effective JavaScript engineer?
Note: The suggestions made here are meant to be for candidates interviewing for mid-to-senior level JavaScript-heavy roles. They are just a small set of the major holes that I've observed in hiring.
Answer: Learn JavaScript.
OK, that may sound a bit sardonic (and it is), but from my experience interviewing JavaScript developers over the past few years, I have seen some disturbing patterns that could be easily remedied with a modest amount of reading and experimentation.
1) Understand hoisting.
Does this snippet confuse you?
var foo = 'bar';
(function() {
console.log( foo ); // logs `undefined`
var foo = 'baz';
})();
Declaration hoisting can be the source of lots of debugging pains for junior engineers, understanding how it happens can save you and your team lots of cycles down the road.
At Project Decibel, we actually enforce a JS style guide that requires all variable declarations to be put at the top of a given function to remove any ambiguity.
The above snippet would fail our automated CI tests and would have to be rewritten as the following in order to pass:
var foo = 'bar';
(function() {
var foo;
console.log( foo ); // logs `undefined`
foo = 'baz';
})();
Notwithstanding the fact that this is a very contrived example, this new
snippet is much less confusing, and presumably the dev never meant to log
undefined
originally — so we got ahead of bug in this case.
2) Understand prototypes.
Prototypal inheritance, in my view, is so fundamental to solid app design, I'm routinely surprised by most engineers lack of exposure (even experienced devs).
I often ask the following in technical interviews:
// How would we set up the inheritance chain, such that
// Bar inherits from Foo?
function Foo() {
console.log('Initializing...');
this.init();
}
Foo.prototype.init = function() {
console.log('Initialized!');
};
function Bar() {
}
var bar = new Bar();
// should log 'Initializing...' and 'Initialized!'
To some, this may seem trivial — and I think it is — but realistically, most engineers that have been taught a higher-order abstraction for building applications (a la Angular, Ember, et al.) may never have run into a situation where inheritance presented itself as an obvious (or even useful) pattern.
I'm empathetic to those folks, as I've worked in framework-laden environments in the past, but would I be bullish on hiring them for a critical role? Not likely.
Some out there may claim that inheritance is non-essential to effective application design, particularly for single page web apps. And to those I would say: I strongly disagree.
3) Understand closures.
Imagine you received a spec or user story for a score module in a game your company is building. In the acceptance tests is a requirement:
1) The score module should have a property named
score
. Whenever that integer changes by any means, the template MUST also update.
There are 101 ways to pull this off. But an understanding of closures offers a really terse, clean implementation:
function ScoreModule() {
this.tmplString = 'Score: {{ score }}';
this.elem = document.querySelector('#my-score-elem');
(function() {
var score = 0;
Object.defineProperty( this, 'score', {
get: function() {
return score;
},
set: function( val ) {
score = val;
this.elem.innerHTML = template(
tmplString, { score: score }
);
}
});
}).call( this );
}
var scoreModule = new ScoreModule();
scoreModule.score = 5; // auto templating abound!
If a candidate could show me code like that — even with zero framework experience — I'd be much more likely to hire them over an otherwise equivalent Angular dev that couldn't grok that pattern.
But everyone uses [Framework X], shouldn't I dive in to be marketable?!
Absolutely! I'm a huge Backbone/Marionette fan and encourage people to choose the right tool for the job and also for the culture you want to build in your organization. Whether it's React/Flux, Angular, Ember, or Backbone; they all have something novel to offer. Read the docs/source for yourself and find what excites you.
But that gusto for tools shouldn't eclipse your desire and ability to develop an expert-level command of the web platform, particularly that of the JavaScript language.
If any of this resonates with you — I'm hiring JavaScript engineers, feel free to reach out: stephen@projectdecibel.com