ponyfoo.com

Polyfills or Ponyfills?

Fix

These are short-form “thoughts”, in addition to the usual longer-form articles in the blog. The goal is to publish one of these every weekday. I’d love to know what you think. You may send your questions to thoughts@ponyfoo.com. I’ll try to answer them over email and I may publish them here, with your approval. I also write thoughts about the current state of front-end development, and opinions on other people’s articles. You can use the form to the right (near the bottom in mobile) to subscribe via email.

These days we all know what polyfills are. It’s been over five years since Remy Sharp coined the term. A polyfill is usually a snippet of code that patches a piece of functionality that’s missing in some browsers. With ES5, polyfills became all the rage because you could instantly get access to functional Array.prototype methods like .map and .reduce just by dropping in a file. There’s also entire bundles that patch most of ES5 for you to use in older browsers, such as es5-shim. However, not all is peaches and cream.

When it comes to ES6, a flurry of problems turn polyfills into ineffective vaccines. For one, you simply can’t polyfill language features, such as arrow functions, generators, async/await (ES7), rest and spread parameters, classes, modules, etc. There are other features you *could* actually polyfill, such as Array.of, Number.isNaN or Object.assign, because those don’t introduce syntax changes to the language – except that you shouldn’t.

Christian Heilmann argues that, sometimes, developers probe feature support by testing for some other feature that’s implemented most of the time alongside the feature we actually want to use. Under those circumstances, and considering you already have tools like Babel if you’d like to play around with ES6, I suggest you strongly avoid polyfills for any ES6 features, out of the ones that could be polyfilled.

As an alternative, you could use ponyfills instead.

Ponyfills

A ponyfill is almost the same as a polyfill, but not quite. Instead of patching functionality for older browsers, a ponyfill provides that functionality as a standalone module you can use. Let’s go to an example.

Here’s how your typical polyfill looks like – it was taken from MDN.

if (!String.prototype.trim) {
  String.prototype.trim = function () {
    return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
  };
}

The equivalent ponyfill would be a module that exports the method below.

function trim (text) {
  return text.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}

They’re very similar, except a ponyfill doesn’t patch missing functionality others may be relying on for feature detection.

Polyfills or Ponyfills?

I think both have their merits. Polyfills are great for methods like String.prototype.trim because they allow you to use the methods on String instances. That gets even better with methods like .map, and .reduce which return this, so chaining is great. Sure, you could always do reduce(map([1, 2, 3], twice), sum, 0) instead of [1, 2, 3].map(twice).reduce(sum, 0), but there’s also the fact that many of your dependencies probably are assuming ES5 is on the table, so it’s useful to polyfill for the stuff that’s missing in older browsers.

When it comes to ES6 (or more complicated ES5 polyfills) however, the situation changes a little bit. Sometimes we can’t implement a solution that’s fully spec-compliant, and in those cases using a polyfill might be the wrong answer. A polyfill would translate into telling the rest of the codebase that it’s okay to use the feature, that it’ll work just like in modern browsers, but it might not in edge cases.

In that situation it’s better to use a ponyfill, because that way you won’t be polluting expectations about what. Only you will be leveraging the new functionality, and you are well aware of the limitations of that solution, so all is good with the world. Meanwhile, other pieces of code that are feature-detecting on the piece of functionality you’ve half-patched will continue to work by ignoring it as usual. Here, we can fall back to a great thing I’ve heard that I don’t hear often enough: “users browsing the web with shitty browsers are used to shitty experiences”.

In so many words, strive not to break expectations.

Have any questions or thoughts you’d like me to write about? Send an email to thoughts@ponyfoo.com. Remember to subscribe if you got this far!

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (6)

SylvainPV wrote

developers probe feature support by testing for some other feature that’s implemented most of the time alongside the feature we actually want to use

This is a terrible idea and we should not encourage it. If we want to test if a langage feature is supported, we can still try/catch around an eval :

try { eval("async function(){}") } catch(e){ /* async functions not supported */ }
Michael Rosata wrote

Somehow I posted my comment on the wrong page a few hours ago, (so take that point into account when coming up with a voice for me inside your head).

I think that if your entire program is written to heavily rely on polyfills in all areas then you may be asking for trouble. However, I think that extending objects through their prototypes is a valid way to implement functionality/features (as it’s a defining principle of the language), and I don’t think I would tell someone not extend the base String object if they had invented some new great desired feature for Strings. So isn’t that what a polyfill does? Regardless if the polywhopped feature is implemented in 50% of browsers or 0% of them, there will always be failing edge cases, we all know that.

Polyfills apply a pressure to the browser industry [I’ve heard] that breathes movement into the language and gets fellow developers aware of the what can/should/is coming in the future. If you need a polyfill to accomplish your goals then I say use it. However, if there is a safer more reliable way to create the functionality then that’s the way to go, especially if the functionality feels like a “helper” [qoute @Tyler Nieman], then maybe people should go with the ponyfill separation.

I only reason I might probably prefer poly over pony is because when a feature does become supported, now all of a sudden I’d have a pile of code relying on heavy refactoring to remove the ponys where polyfills I the transition is seamless, the developer doesn’t even have to touch the code to ease the transition.

Interesting article, awesome and thanks!

Jeffrey Barke wrote

However, I think that extending objects through their prototypes is a valid way to implement functionality/features (as it’s a defining principle of the language), and I don’t think I would tell someone not extend the base String object if they had invented some new great desired feature for Strings.

I would.

Don’t modify objects you don’t own.” Nicholas C. Zakas

What’s wrong with extending the DOM.” Juriy ‘kangax’ Zaytsev