×  

Home / Coder

Building things is what I do. For work, for open source and for fun.

I'm a generalist when it comes to technologies and techniques, but I do have a special affection for JavaScript, workflow automation and test-driven development.

You can check out my open source-projects, of various seriousity, on Github. You can use all of the JavaScript modules I've written via npm. Or hear from a few of the companies I've worked for below.

Jakob is one of the best (and fastest) programmers I've ever worked with. He's not the oh-so-common "head-down" type of programmer, he actually enjoys working with the problem at hand and making it better through collaboration and iteration. On top of that he can manage a team with no problem. Creative, ambitious and someone I wouldn't hesitate to work with again. I really hope we do.

Åke Brattberg, Burt

Jakob is a master of his trade. He's skilled in every part of the web development chain, from writing code and architecting software to setting up processes that make it easy to get quality work done. He's not only great himself, he makes everyone around him better.

Erik Frisk, Touch&Tell

Jakob had a leading role in developing the web service Edvirt Training Portal. He's been solely responsible for the back-end development of the product. The greatest thing about working with Jakob is that you can rely on him taking full responsibility of the task and deliver with perfection.

Erik Göransson, Edvirt

As a developer Jakob shows great enthusiasm and passion. A hard-working and impressive problem solver, he's great at exploring how to leverage new as well as established tools, technologies and architectures. He is humble and listens carefully to others, but doesn't hesitate to voice his own opinions.

John Sjölander, Burt

Working together with Jakob has been a breeze. When contacted, Jakob showed great enthusiasm and quickly helped us with expertise in 3D-modeling, something which exceeded our expectations. Without trouble, he understood our problem and delivered great results.

Vilhelm Josander, IVEO

Home / Train

Frameworks, coding practices and idioms; I talk about them, I write about them and I teach them as well.

As lead developer in teams and as consultant I've been guiding coders in picking up new skills time and time again.

We’ve contracted Jakob for training of a number of our employees in JavaScript. He was highly appreciated by the trainees for his pedagogical and interactive way of teaching, as well as his technical competence. We aim at conducting more of these trainings and Jakob will be our first choice.

Fredrik Borg, New Minds

We have hired Jakob several times to help our employees to develop and boost their sales. With an inspirational personality and hands-on tools, his lectures has been among the most appreciated within our consultant-training program.

Carl Törneman, CTK

Home / Recruit

When I think about recruiting, I think about lighting the fire in someones eyes and creating an urge.

I'm not a recruiter, but I've engaged hundreds of people while hiring for startups. I'm fairly good at it and at times I help other companies with their key recruits.

After searching on our own for months, Jakob helped us with a perfect match in no time. We hired the guy and today he is our tech lead. We would definitely ask Jakob again, especially since it went very quickly. There is no reason to try someone else.

Alexandra Bylund, Foap

After a VC investment and prior to an international launch of our virtual law service we needed to recruit an experienced CTO. Jakob helped us with a second opinion as well as a test of the candidate. We are very happy with the help we got from Jakob and his constructive insights about the strengths and weaknesses of the candidate. I would be happy to work with Jakob again.

Magnus Stein, Avtal24

Home / Architect

Distributed systems, continously deployed systems, testable systems, scalable systems. I love designing them all.

I've spent a lot of time working on large scale systems within teams and I enjoy sharing those experiences with other teams.

Jakob was a great support during the development of our web service Lumado. He helped us and our developer to design the architecture behind the technical solution and speaks in a language everyone understands. He showed many times over why he is such an appreciated developer.

Erik Nilsen, Trendie

Jakob was tasked to help identify scaling strategies for our system. He did so by asking us all the right questions, by tapping into his experience and by using his extensive network. We discussed both highly detailed technical challenges and wider strategic perspectives, with Jakob providing valuable insights in both cases.

Jesper Josefsson, Foap

Home / Due Diligence

I've worked for several startups and even started a few, so I speak both code and business. I know what to look for when it comes to technology.

I usually speak with the team and dig through the code to identify risks and suggest improvements for all aspects of the product stack.

Jakob has conducted a handful of technical due diligences in IT related private companies which we have considered investing in. As the output delivered by Jakob is of consistently very high quality, he is one of my preferred parties for conducting this type of work.

Malin Carlström, Industrifonden

After seeing Jakob's work I decided to hire him for one of our own due diligences. I was impressed by the quality delivered and have already put him in touch with other portfolio companies, for further advice. We're not finished with Jakob by a long shot!

Robert Hellman, Almi Invest

Home / Speaker

Oh boy, do I love to talk!

In the words of stand-up comedian Mitch Hedberg, “People either love me or hate me. Or they think I'm OK”. Regardless, I believe in sharing and people appear to have a good time. Many conferences (including SpainJS, JsDay and ScanDev) have invited me repeatedly, so I must be doing something right.

When on stage, I prefer to entertain and give concrete inspiration rather than verbal manuals and documentation that you can simply find online yourself.

Funny, interesting, to the point and relevant.

Everything was well summarized. Good readibility of the slides.

Focused and well structured, with great content.

Very well structured, easy to follow. Nice and simple slides.

Good public speaking skills, I had fun!

Most of my talks are available at Speaker Deck and Lanyrd. A few examples can be found below.

Manage Those Dependencies

A talk about dependency management in the browser, using RequireJS, npm, browserify, bower, jam and all of their friends.

Automated CSS Testing: Not just a myth

Some people say that you can't test HTML/CSS. I'd like to show you the opposite.

Home / My Story

I'm an outgoing introvert, an overly calculated risktaker, an exploration junkie and I'm incurably competitive.

I'm at most alive when fully engaged in struggling with a challenge. Anything from rubics cube to learning an instrument or getting up properly on a surfboard counts. Interesting challenges tend to show up everywhere.

I happen to like software and music in particular. Maybe because of their purity and ubiquity. Maybe because they're endlessly expressive languages. Or maybe for no reason at all.

When not spending time on a new hobby, my own pet projects or as a consultant, I've mainly worked in small, intense and energetic companies as developer in a leading role. Most notably at the companies below.

  • FishBrain Hired as CTO, I'm currently working to build a kick-ass team as well as a kick-ass product out of Stockholm, Sweden.
  • Touch&Tell As one of the co-founders, I worked with everything from coding to sales and eventually hired me out of it.
  • Burt Big data, extreme JavaScript and a highly distributed system - I started by building cool stuff and ended up as VP of Engineering.
  • CTK Consultant in four different projects, consultant manager and eventually CEO; I did it all and moved on when we were 60 employees.

You can probably find me in all kinds of weird places on the Internet. Here are my favorites:

Home / Blog

Hello World in Node.js is broken

I just realized there's a minor fallacy in the typical Node.js Hello World-program. The same program is found in countless tutorials as well on nodejs.org itself. And it's broken.

Reflections from Sthlm.js

I just gave a talk at sthlm.js, about JavaScript promises and my library Z. You can find all of the content in the links.

As usual I ran a quick survey by the end. In the name of openness, self-improvement and hopefully as inspiration to others, I'll write a little about my learnings here.

Semver in node.js and npm

A lot of folks seem to misunderstand semver and/or how to do versioning in npm. In particular ~ and ^, which many believe to be kind of the same thing. This is my take on explaining the difference and why to use one over the other.

Selective npm test

When writing programs using node.js npm test is typically how you run your tests. Although when I'm coding, I typically want to run just the test(s) that I'm working on and not ALL of them. Sure, if the entire suite can run in less than a second I don't mind, but that is usually not the case. There are two ways in which I've typically dealt with this...

The dumbest language construct ever

I answered a question on stackoverflow yesterday that made me realize something almost insane. That’s the good part about having to explain things to other people by the way; you really starting thinking about all the whys...

Building a ship

If you want to build a ship, don’t drum up people to collect wood and don’t assign them tasks and work...

The purpose of business

Business has to give people enriching rewarding lives... or it’s simply not worth doing...

Code is a liability

Code is not an asset, it’s a liability. You want as little code as possible to solve the problem...

JavaScript parseInt considered harmful

Some devs use JavaScript’s parseInt in order to convert floats to ints. It works perfectly fine most of the time, but happens to result in an excellent gotcha in a certain edge-case...

Avoid indexing JS strings like arrays

I’ve been writing a lot of JavaScript the past year, but I never ran into this stupid gotcha before. Can’t understand how I’ve avoided it for so long...

Simplicity is the root of all genius

Dennis Ritchie, creator of the C programming language and co-creator of UNIX, once said that Unix is simple. It just takes a genius to understand its simplicity...

Recipe for trouble

Do you have a JavaScript-program that sets element.innerHTML, has deferred scripts contained in strings and requires IE-support? Then you might have a serious problem...

Home / Blog / Hello World in Node.js is broken

Hello World in Node.js is broken

I just realized there's a minor fallacy in the typical Node.js Hello World-program. The same program is found in countless tutorials as well on nodejs.org itself. And it's broken.

This is the code I'm talking about:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Super-simple! It's a web server in a few lines. Wow.

Now, what's the error?

Node.js is highly asynchronous. Just because you called a function and that function has returned doesn't mean that the function has completed its task. If you've ever written anything in JavaScript or Node.js you've been exposed to this and you know that you should pass callbacks to solve this. The return value of a function is usually less useful than the invokation of the callback. You probably already know why this is powerful (async without threads etc). Oftentimes, that is. Or sometimes. Or so. You also know it's still hard to get it right. Actually, it's so hard that even this tiny program got it wrong.

Here's a slightly enhanced version that got it right:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1', function() {
  console.log('Server is *actually* running now');
});
console.log('Server is not really running yet');
console.log('Syncronous access here would fail.')

As you can see from the log-statements, there's a difference between having called createServer/listen and actually having a server that listen to requests. As per usual, things are not finished until the callback is invoked. You cannot go ahead just because your function call has returned. You are failing at async.

I'm sorry Hello World. You are so young and small and innocent, but you just proved a very important point.

Async is hard.

Disclaimer

  • I love JavaScript and Node.js. This post is just some rough love.
  • I know that you would never synchronously send requests to a web server in Node.js. For all practical purposes, the original code example is fine.
  • I want to point out that async is hard. It's easy to miss things. Easier than you think.
  • I also want to point out that tutorials often over-simplify. I'm sure this tiny adjustment was intentional in the name of simplicity.
  • There are other example in the Node.js HTTP docs where things are done the right way, like this for example.

Bonus

If you like using promises to simplify the async-challenges check out my library for making using them easier: Z

If you haven't really grokked the point of promises yet, or find the cure as bad as the disease, then check out this presentation: How to actually use promises

Home / Blog / Reflections from Sthlm.js

Reflections from Sthlm.js

I just gave a talk at sthlm.js, about JavaScript promises and my library Z. You can find all of the content in the links.

As usual I ran a quick survey by the end. In the name of openness, self-improvement and hopefully as inspiration to others, I'll write a little about my learnings here.

I got 38 responses (out of about 100 I'd say), which is good! A vast majority were positive and rated the talk high. There were some clear opinions on improvements though, which is the really interesting part.

Quickly, on the positive side, words like the following were the most common: "energy, pace, well presented, focused, deliverance, high level, clear goal, dynamism, style, progression, code, humor, positive, honest". I'd summarize those things as (1) well-prepared and (2) well-presented.

On the negative side the gist was something like this: "explicatives, live coding, more error handling, slow down, invert colors, a little cocky, assumed people knew much, better projector, less code." I think of these as three different classes of feedback.

The first class are hard to address. "More error handling" and "Assumed people knew much" are clear trade-offs against values that were also rated as positive. I intentionally left the error handling out (despite comments from my co-workers) due to the already info packed talk. And in the same spirit, it would be hard to give this talk without assumptions about peoples knowledge. It's impossible to cater to everyone and this time I chose to give a talk that was a bit advanced. I still listen to this though and I'll see if some changes could improve it without compromising the good parts.

The second class where color invertion, projector quality and avoiding the live coding (I guess it took too much time). Those are some things that I can easily adjust until next time. No biggies. Thanks for letting me know!

The third class are the ones I really take to my heart and will really spend time on changing. The explicatives, the fast talking and the slight cockyness are what I'm thinking about. I often get very engaged while speaking, which easily leads to speaking too fast. Especially when there's a lot of ground to cover. I'll try some tricks next time and really halt myself.

Regarding the explicatives and the cockyness, I get a bad feeling just reading it. I can imagine how it must have sounded. To quote one comment: "the first 'fuck' may be funny, but the next 10 ones are forced." A wrote a few of these into the actual slides, but it must have affected my language beyond that. Just as the commenter pointed out, one or two scripted are ok, but it's not ok to go on an unprofessional ranting rampage. It came as quite a surprise to me (I guess I couldn't hear it myself), but it also came as a total turnoff and something I will very much avoid next time around.

I very much appreciate all the feedback and will use it to improve this and future talks that I'll give. Thanks sthlm.js, you're great!

Home / Blog / Semver in node.js and npm

Semver in node.js and npm

A lot of folks seem to misunderstand semver and/or how to do versioning in npm. In particular ~ and ^, which many believe to be kind of the same thing. This is my take on explaining the difference and why to use one over the other.

Let's say I depend on underscore. My package.json then contains the following:

"dependencies": {
  "underscore": "*"
}

But the star is bad, because it requires any version of underscore, which is probably not what I want. I want to explicitly require a version where all the things I use are included. So then I do this for example:

"dependencies": {
  "underscore": "1.4.2"
}

Now I get version 1.4.2 and nothing else. I get all the juciy things from this particular version that I'm using. Sweet. NOT. Why? Because if there's a bug in this version, I won't get fixes automatically.

Enter semver. I recommend you click the link and read the whole thing. Carefully.

But you didn't, did you? Ok, so the tl;dr version would be to say that there's a reason why we have three separate numbers in our versions and not just one single number that keeps incrementing. Very simply summarized:

  • The first number (the "major") increases when we introduce breaking changes.
  • The second number (the "minor") increases when we add new features.
  • The third number (the "patch") increases when we fix bugs.

So then I should require underscore like this I guess:

"dependencies": {
  "underscore": "1.4.x"
}

That will get any version of underscore that starts with 1.4 and just get any patch - which will allow new patches to be installed when the package is installed.

But what if we know there's a critical bug in 1.4.0 and 1.4.1? Then we could do something like this:

"dependencies": {
  "underscore": ">= 1.4.2 < 1.5.0"
}

Now we will only get versions that start with 1.4 and that have a patch number equal to or larger than 2. Perfect! Almost...

In practice, people patch the latest version when they find bugs and not all the old ones as well. So if underscore 1.5.0 gets released and then 1.6.0 and then they find a bug, that bug will be fixed in 1.6.1. There won't be a 1.4.3.

So, since 1.6.1 is also supposed to work if 1.4.2 worked (if you don't understand why, read the semver spec more carefully - only increases in the first number, the major, can introduce breaking changes) then we can just as well accept any minor version and not just any patch version:

"dependencies": {
  "underscore": ">= 1.4.2 < 2"
}

Now, this is good! We don't allow the major version to be increased (because that could introduce breaking changes) but the minor and patch (which will never break our code) are allowed to be updated.

Of course all of this assumes that people follow semver. But most people who know what they're doing are. Or are at least attempting to.

Now, what does ^ and ~ have to do with this? Well, the ~ operator works like this:

  • ~1 means >= 1.0.0 and < 2.0.0 (or "Any version starting with 1")
  • ~1.4 means >= 1.4.0 and < 1.5.0 (or "Any version starting with 1.4")

That is clearly not optimal according to semver, as outlined above. The operator ^ on the other hand works like this:

  • ^1 means >= 1.0.0 and < 2.0.0 (or "Any version compatible with 1")
  • ^1.4 means >= 1.4.0 and < 2.0.0 (or "Any version compatible with 1.4")
  • ^1.4.2 means >= 1.4.2 and < 2.0.0 (or "Any version compatible with 1.4.2")

So using ^1.4.2 is the same as >= 1.4.2 < 2, which is the optimal way of specifying a dependencies if semver is being followed. Using ^ is just shorter and sweeter.

These definitions are available right in the node-semver readme, which is used to resolved dependencies in npm.

I use ^ and believe everyone else should too. Just note that you need npm 1.3 or later (introduced in node 0.10.13) to install a module that uses this operator.

Home / Blog / Selective npm test

Selective npm test

When writing programs using node.js npm test is typically how you run your tests. Although when I'm coding, I typically want to run just the test(s) that I'm working on and not ALL of them. Sure, if the entire suite can run in less than a second I don't mind, but that is usually not the case. There are two ways in which I've typically dealt with this.

The first one is indicating which tests I want to run with code - and it sucks! Using mocha for example (a testing framework I use quite a lot) you can do this by adding .only to your test, like this:

describe('someFunction', function() {
  it.only('always returns 4', function() {
    someFunction().should.eql(4);
  });
});

No matter how many other tests you have, only this one will run. Now the problem is that sometimes you forget removing .only and then you commit. Your continous integration (and maybe even deployment) system will run this one test and conclude that the entire project still works. Thanks .only. Now I've deployed a version of my system where my new feature works, but potentiallt nothing else. Note that I'm not blaming mocha in particular here (expect for the fact that the framework implements such a retarded concept). It's the pattern, that's found in lots of other testing tools as well, that is the real problem.

The second way is a lot better. You tell the actual runner of your tests which tests you'd like to run. Once again, using mocha, that would be done by passing it the argument --grep on the command line. So for the example test above, I'd do:

mocha --grep someFunction

Now that's fine, except for the fact that I typically have other parameters that I'm passing to mocha as well. In reality, it'd probably look more like this:

mocha tests --timeout 500 --compilers coffee:coffee-script --recursive --grep someFunction

Needless to say, I don't want to write that entire thing everytime I run a test. One, because I'm lazy, and two, because it's error prone. Maybe I forget one argument. Different projects have different test setups and I work on a lot of small projects. That's exactly the reason why we have npm test! We want to abstract away the configuration of the test and just be able to run the damn thing.

So finally, here's what I usually do; I put the following into my package.json (with any combination of arguments and setup that I need):

"scripts": {
  "test": "mocha tests --timeout 500 --compilers coffee:coffee-script --recursive --grep \"$TESTS\""
}

With the help of this environment variable I can now run selective tests like this:

TESTS=someFunction npm test

If I just run npm test, all tests will run as usual.

The reason I have ended up calling the variable TESTS rather than GREP or something is that I want to keep if framework agnostic. All projects doesn't use mocha and then the argument would no longer be called grep.

I hope this helps some people out there, who have been doing .only too many times or are just tired of typing long error prone commands. And if you have another way of dealing with this, please share it! I'm very curious!

Home / Blog / The dumbest language construct ever

The dumbest language construct ever

I answered a question on stackoverflow yesterday that made me realize something almost insane. That’s the good part about having to explain things to other people by the way; you really starting thinking about all the whys.

Anyway, what I realized was that the void-keyword in JavaScript is so trivial to implement that it’s embarrassing. Language constructs should provide orthogonal building blocks for constructing programs. Abstractions that can be built from those should be put in a standard library or 3rd party ones; not in the language itself. The void-keyword is such a perfect example of the opposite, since it can be implemented in one line. It’s equivalent to the empty function!

Implementation for you:

function myVoid() {}

Now these are the same, except for the quite useless syntactic sugar allowing us to leave out the parentheses:

var res1 = void 1 + 2;
var res2 = void any(expression(goes(here)));

var res1 = myVoid(1 + 2);
var res2 = myVoid(any(expression(goes(here))));

If sugar is really important to you, then you are probably already using CoffeeScript and can skip the parentheses as usual.

Home / Blog / Building a ship

Building a ship

If you want to build a ship, don’t drum up people to collect wood and don’t assign them tasks and work, but rather teach them to long for the endless immensity of the sea.

Antoine de Saint Exupéry

Home / Blog / The purpose of business

The purpose of business

Business has to give people enriching rewarding lives... or it’s simply not worth doing.

Richard Branson

Home / Blog / New business cards

New business cards

Home / Blog / Code is a liability

Code is a liability

Code is not an asset, it’s a liability. You want as little code as possible to solve the problem.

Home / Blog / People that call themselves nerds

People that call themselves nerds

Home / Blog / JavaScript parseInt considered harmful

JavaScript parseInt considered harmful

Some devs use JavaScript’s parseInt in order to convert floats to ints. It works perfectly fine most of the time, but happens to result in an excellent gotcha in a certain edge-case. So shame on you guys!

I wrote a highly upvoted answer at stackoverflow on this. Read the whole thing here or just the gist of it below:

var test = 9.546056389808655e-8;

console.log(test); // prints 9.546056389808655e-8
console.log(parseInt(test)); // prints 9 - oh noes!
console.log(Math.floor(test)) // prints 0 - this is better

Home / Blog / Avoid indexing JS strings like arrays

Avoid indexing JS strings like arrays

I’ve been writing a lot of JavaScript the past year, but I never ran into this stupid gotcha before. Can’t understand how I’ve avoided it for so long. The correct syntax hurts my brain.

var str = "foobar";
var firstChar1 = str[0]; // doesn't work in IE < 9
var firstChar2 = str.chatAt(0); // works everywhere

Home / Blog / Simplicity is the root of all genius

Simplicity is the root of all genius

Unix is simple. It just takes a genius to understand its simplicity.

Dennis Ritchie

This proves to be true over and over again. Not just for UNIX, but for a lot of of things. The concept of a Grand Unified Theory in physics is probably the best example. Now, I don’t work with the fabric and forces of the universe itself, but with the fabric of programs and the forces of people developing and using these programs. The goal is still the same though; as simple as possible, but not simpler.

When it comes to JavaScript, the ubiquitous language of the web, I find that there are two de facto standard functions that perfectly embodies this concept; Function.bind and Object.create (sometimes called Object.beget). They are not part of the standard library (a major mistake of course) but ridiculously simple to implement, yet they require a deep understanding of what’s going on behind the scenes. Actually, my own personal criteria for determining if someone is a JavaScript ninja or not is to ask them to explain the what, why and how of these two functions.

Arguably, Object.create is the most complicated one. Apart from the semantics of JavaScript itself, it teaches quite a bit of philosophy. I could ramble about the details of it all day, but it all boils down to a single great argument: it simplifies the language by encapsulating three related but none the less difficult concepts.

  • Functions as constructors
  • The prototype property
  • The new-operator

Object.create captures these one by one, in exactly one simple line each, and renders them obsolete:

Object.create = function(o) {
  function F() {}
  F.prototype = o;
  return new F();
};

By using this function, none of these three concepts will ever have to be used directly again. I know that there are a lot of people who calls themselves JavaScript-programmers, but still doesn’t understand how/why this is the case. It sure takes a genius to understand the simplicity. Douglas Crockford is one of those. When he is talking about the evil of the new-operator and that it should be avoided, this is what he is referring to. Behind his sometimes strange sounding recommendations there is a philosophy that has to be grasped first; if everything is an object and dynamicity is the goal, then it should be possible to use anything as the base for inheritance (not just “classes”).

I won’t explain how the function works (that has already been done a million times, just google it), but I want you to think about the beauty of it. Three concepts unified in the most generic and straight-forward way imaginable, to create a single abstraction that supersedes the individual ones. It’s like the Maxwell equations of JavaScript.

I’m sure Brendan Eich knew this, but implemented the three-headed hydra instead of something pure and simple due to popular demand. Just like many other features in the language, it was shaped to suit the classical inheritance crowd of C++ and Java. Classical inheritance has its uses, but so does the prototype-model. Someone has to tell JavaScript: “Man, just be yourself. No need to emulate the other languages. You’ve got your own thing going.” Force the mob to learn something new once in a while and maybe they’ll realize just how simple it is.

Home / Blog / Recipe for trouble

Recipe for trouble

Do you have a JavaScript-program that:

  • Sets element.innerHTML (because it's a very performant way to manipulate the DOM)
  • Has deferred scripts contained in strings (because you manipulate 3rd party string out of your control)
  • Requires IE-support (because the world is a sad place)

Then my friend, you might have a serious problem (apart from point 2 and 3, which are quite serious on their own).

You can summon the beast with this simple example, creating a div-tag dynamically and inserting a line break and a defered script into it:

var e = document.createElement('div');
e.innerHTML = '<br /><script defer type="text/javascript">alert("Damnit IE!")</' + 'script>';

Now, the div-tag is not even a part of the DOM, yet IE insist on actually running the JavaScript!

It runs a script that is not even part of the DOM!

It’s like an eval in disguise! I discovered this by breaking a site (imagine what would happen if you replaced the call to alert with document.write for example) and would like to save future generations some tears.

So, remember: a very performant and useful property meets two evils and an even greater evil is produced.

 

Come on!

Your browser is really outdated. Update it to view this site - and improve your experience of the entire web while you're at it.

Go to browsehappy.com to find out more.