Search

347: Jason Miller and PreactJS

Download MP3

Jason Miller stops by to chat about the world of performant JavaScript, the future of PreactJS, and lettings tools do the code golfing for you.

Tags:

Guests

Jason Miller

Web · Social

Web Developer Relations at
Google. Creator of PreactJS.

Time Jump Links

  • 01:09 Guest introductions
  • 02:57 What is PreactJS?
  • 07:07 React and classes
  • 10:29 Does Preact have hooks?
  • 19:56 Sponsor: .Design TLD
  • 20:52 How and why are you setting such an agressive bar for Preact perf?
  • 25:26 Jason's 3 reasons for the growth of any library
  • 32:20 Is writing for a compiler a skill we should have?
  • 36:38 Sponsor: Digital Ocean
  • 37:46 Is 3kb as a target the modern weight?
  • 42:33 Serving Module and NoModule Bundles
  • 45:49 Could Webpack help?
  • 49:16 Is Module NoModule happening in 2019?
  • 51:34 CSS @supports
  • 58:12 Advice for people wrangling javascript apps

Transcript

[Banjo music]

MANTRA: Just Build Websites!

Dave Rupert: Hey there, Shop-o-maniacs. You're listening to another episode of the ShopTalk Show, a podcast all about frontend Web design and development. I'm Dave--over three kilobytes--Rupert. [Laughter] With me is Chris Coyier. Chris, I don't know how many kilobytes you are.

Chris Coyier: Oh, I thought you were going to call yourself Dave--never slow--Rupert.

Dave: Oh, no. Chris -- Chris--fast hand--Coyier, Instapaint. That's all I've got. Hey, Chris, why are we talking about kilobytes?

Chris: Well, we're in the middle of our modern JavaScript ecosystem miniseries here, which has been exciting to talk about. We've had great guests on. Then last week, we got to just talk with each other about crap. I think we spent two-thirds of the episode talking about CSS and JS. That was fun. That was fun.

Dave: Yeah.

Chris: But this week, we're going to continue on with our guests. We have a perfect guest to talk to us about all things JavaScript and speediness, Mr. Jason Miller. Hey, Jason. How are you?

Jason Miller: Hello. I am well. How are you guys?

Chris: Fantastic. Jason is in Boston at the Google, to set the stage. Yeah, is that right?

Jason: Yes, I am El Goog.

Chris: [Laughter] El Goog, in developer relations and whatever that means these days, yeah?

Jason: Yep, it changes daily.

Chris: [Laughter]

Jason: It's the only thing. Since I can't focus on anything, it's actually quite a good job for me.

[Laughter]

Dave: New job title, great. I'll just--[Laughter]

Chris: Was it a dream job for you, because you could probably do anything? You're known for a bunch of libraries and stuff that we'll get into. I imagine you have -- I don't know. I feel like a guy like you, you could bring that knowledge just about anywhere. Was it Google reached out to you or were you like, "Oh, that'd be awesome to have an impact at the Web by going there"?

Jason: A bit of both. We had done some stuff for events in the past, kind of just lining things up. Eventually, I realized that most of the people who I knew worked at Google, so I might as well go and work also at Google.

[Laughter]

Chris: Yeah, that's why I worked at this ice cream stand in college--

[Laughter]

Chris: --because all my buddies already worked there.

Jason: Oh, yeah!

Dave: Same dif.

Jason: Just loved the product, you know.

[Laughter]

Dave: Yeah.

Chris: I just loved the product.

[Laughter]

Chris: Okay. Cool. Google clearly cares about Web speed stuff, right?

Jason: Yeah, a little bit.

Chris: There are all kinds of products from Google that are Web performance based, and you care about that too. Probably most notably, perhaps if you haven't heard of Jason, which would be crazy, but just in case you haven't, you probably have heard of Preact, a huge library that's kind of -- I don't know. You tell us. I'm not going to tell the world what Preact is when Jason is sitting right here. What is it?

Jason: Oh, let me see if I can remember the elevator pitch. [Laughter] Nobody has asked me that in a while. Preact is a tiny, like three kilobyte--

What's the best way to describe this? It's a tiny, three kilobyte library that kind of feels a lot like React. Over the years, that has evolved to mean multiple things.

Chris: Sure.

Jason: In its present state, it means that if you were a person who knows React or who wants to know React but you want to build an app where you may not be able to use React or you are embedding something in someone else's website, there are constraints there.

Chris: Yeah.

Jason: Preact is like, you can use your React skills but using only the tiniest little substrate possible. It's almost like if you were going to use vanilla JS, but you wanted to use a bunch of React patterns, this is the library you might go for.

Chris: Yeah. It quite literally is super, super tiny.

Jason: Yeah.

Chris: It's got to be 10x smaller or more. Yeah?

Jason: Yeah. Yeah, it's 14. I can never remember the exact number. React has been doing crazy good stuff reducing their build size, especially since Dowding joined the team. It used to be a lot higher. It's not. [Laughter]

Chris: That's nice. They've been slashing their size; that's cool. But anyway, it's not anywhere near as small as Preact. What do you get, though?

I don't know. Even I'm a little foggy exactly. I feel like when I spin up a React project, which of course I have, it's always like React and friends, you know. I'm sure those friends can come along to Preact land as well. For example, you need some kind of more elaborate state management kind of thing.

React deals with state, but only at a component level. If you want state management beyond that, you've kind of got to bring in the friends or whatever. React doesn't concern itself with that. It doesn't concern itself with a lot of things.

Jason: In the same way as the value prop for Preact is, bring your skills and we'll help you to go to more places; kind of like how React native lets you bring your skills to native dev. The other half of that is, because those APIs are so similar, in some cases identical--definitely it used to be more identical--a lot of the libraries that you'd use with React just also work in Preact.

For the weird cases, there are a bunch of legacy APIs that Preact doesn't bother implementing or some stuff that just doesn't make sense given its approach to children diffing. For those, there is a separate add-on library called compat that kind of bridges those gaps. It basically just like polyfills a bunch of the more esoteric React functionality into Preact itself.

Chris: Interesting. What do you get, though? You get a component library, right? You get a thing that makes little components.

Jason: Yep. You get your JSX. You get your component base class or whatever we're calling it these days, a render method, and a virtual DOM diffing - all the stuff that's familiar.

Nowadays, there is more surface area to React than there was in the 0.15 era or 15. Obviously, now, as of [laughter], I guess, yesterday, React has a thing called hooks that is a thing.

Dave: It's a big deal.

Jason: And so, everybody is -- yeah, a big deal. Everybody is going around making a hook for everything that didn't have a hook.

Dave: [Laughter] I see a lot of people, now, they're just looking at their 10,000 line React app like, "It's all garbage." [Laughter]

Chris: Well, what's confusing to me is that it seems like React itself is like, "Oh, sorry about classes. That was a bad idea," or whatever. I'm like, "Really?! I like that. At least I get that."

I don't know. I don't mean to sound totally ignorant. I try to keep up okay with it, but it seems like React itself wants you to not use them.

Jason: Honestly, it kind of felt -- I totally don't have inside information here, and I started going Preact when classes were already sort of accepted as like, "Oh, this is going to be the way forward for everything," because I was one of those people that had been using Babel transpile classes since way before they ever had a hope of making it into browsers.

[Laughter]

Chris: You like classes.

Jason: I'm weird that way.

Dave: Classes: they're super.

[Laughter]

Jason: I would say I like classes more than I like inheritance.

Dave: Okay.

Jason: I know that's really weird. I like classes as just like instantiable objects.

Dave: I'm writing a 10,000 word Medium post on why you're wrong. [Laughter]

Jason: Yeah, exactly. Yeah. You'll have good company.

Dave: Yeah. [Laughter]

[Laughter]

Jason: It's funny because React obviously used to have create class, which confusingly was specifically to avoid having a class. It was more like a stamp factory object. Now, we're kind of in this weird world where we've taken the -isms of create class, like being able to have static stuff or being able to have things that, like, functions are an object that have their own lifecycle that has nothing to do with class instantiation or disruption.

It's like instantiate on first use or these interesting semantics. Hooks are basically a functional implementation of create class. I know this is super heresy for me to say that, but I kind of view this as the natural evolution of how create class goes away and we get something that replaces it that fulfills exactly the same need.

Dave: Hmm.

Jason: Yeah, it's weird.

When hooks first came out, my initial impression was, "This is crazy! How on earth is the ecosystem going to move to anything new? We already know how painful it was to go to classes."

Since then, I've done five or ten prototypes of what that would look like in Preact and other libraries. Now, I kind of get it in the worst possible way. I get it in that hooks has injected itself into the back of my brain so any time I'm thinking about writing a demo or whatever and I've got this, like, pure function that returns DOM, my logical back of the brain is going, "Hey, if you want to add state, you should implement hooks and then use hooks."

[Laughter]

Jason: And so, everything I do now has this awful subtext of, if I….

Chris: It's funny how that happens. I feel like, even in CSS land, you learn. Let's say you learn CSS Grid and use it a lot. Now everything that you come across that has any kind of layout, you're like, "Use Grid! Use the Grid!"

Dave: [Laughter]

Jason: Yeah.

Chris: It's Grid.

Jason: That or you don't use Grid. You use a float or whatever, and then there's this little demon on, like, your shoulder.

[Laughter]

Jason: "You know this would have been better with Grid."

Chris: [Laughter]

Dave: I guess Preact does not have hooks yet, or--?

Jason: Okay, so this is where I get to tell you that we spent the last year rewriting Preact from scratch.

Chris: Oh, my gosh!

Jason: I mean no lines of code copied over. Generally, not even looking at the existing implementation.

Before I joined Google, I was trying to figure out how the heck we were going to do fragments in Preact. Fragments is like a node in the virtual DOM tree that can have children and whatever. But then when you go and render it out to the DOM, it's gone.

The way that Preact's diff has always worked, that is ridiculously [laughter] hard to do because it diffs the virtual DOM against the DOM, so you can't get rid of something and then compare to it later.

Chris: Oh, that's wild.

Jason: Yeah.

Chris: It's funny because I think of it as just sugar, kind of. I know it matters because, in the end, it's a div that's not there or some kind of wrapper element in the DOM. It's kind of nice to have that level of HTML control, but it doesn't seem like that big of a deal, you know. [Laughter]

Jason: Yeah, and it's weird. My first pass at this was, why don't we create a persistent fragment in the DOM? You can't do document fragment because, when you append a document fragment to something, it kind of dissolves and it's still going to get moved instead of copied.

But what if you had an element that has a shadow root and a slot? Yeah, and so my brain started going into the weeds there and trying to figure out how can I make…?

Chris: That's a little weedy, yeah. It also seems like you could go div style equals display contents and then that div, the parent div kind of disappears. Yeah.

Jason: Yeah, and that kind of works, so our work was this giant, giant mega thread on the Preact compat repo being like, "Hey, how come there's no .fragment?" [Laughter] My solution was just fragment=div, style=displaycontents, and there are people using that. There is something both scary and amazing about the fact that that is the case.

Chris: Yeah, because it still affects selectors, right?

Jason: Yeah, it affects selectors. I talked to … a lot about the React ecosystem and kind of parlay things between those two groups. One of the things that always comes up is the difference between the DOM and the virtual DOM is the virtual DOM has a cost that asymptotically approaches zero.

Yes, you could build a virtual DOM tree that would take a while to traverse, but you are going to run into some other performance issue before you ever scale up to that point. Right? The only people who are legitimately facing this issue are the Facebook of the world, and they have the engineers on staff to figure out how to face that. [Laughter]

But that's the big difference. You can't necessarily persist all this stuff in the actual DOM because those things aren't fully free and you might run into a case where the size of your virtual DOM tree, even if the size of your real DOM tree should be small, affects the style recalculation time for your whole app because these are things that selectors could match. It gets messy.

Chris: Yeah. I derailed you on your rewriting Preact story.

Jason: Yeah, so fragments was this big problem and it had been a problem because fragments was available as a beta or unannounced feature in React for ages. There was just no way it was ever going to happen in Preact and I knew that. The people who were working on Preact with me at the time knew that.

At some point, I think it was like December or January, I had some time off in between leaving my old job and getting ready to come to Google because there was a whole bunch of visa stuff to do, and I basically [laughter] decided that I would see what it would be like to rewrite Preact from scratch.

I started just prototyping. A lot of time spent on my couch just thinking about things. Eventually, a month or two after, I showed it to come of the core people. There was this agreement that it might be interesting to investigate what would it look like to cut over to a code base based on that.

There are five or ten people who are actively committing to that code base. It is a private repo, which is terrible and I'm a bad person. It's something I said I would never do.

Dave: How dare you!

Jason: Yeah, I know. I know. It's closed open source. The thing is, anybody who has ever asked us, "Hey, can I get access to the source code?" we pretty much give you access to it.

It's not private because we have something to hide. It's private because, for months on end, master would be broken or hydration didn't work. [Laughter] I definitely did not want to have anyone looking at that and being like, "This is the state-of-the-art for Preact. This is what's coming."

Dave: Well, and that's what's frightening, too. Humans are undependable [laughter], I guess, is the best way to put it. People start using things based on weird branches in your repo.

Chris: Mm-hmm. Just look at vendor prefixes.

Dave: Yeah.

Dave: Yeah.

Chris: It didn't work out.

Jason: Yes, exactly. [Laughter] Yeah, webkit-user-select or whatever.

Chris: [Laughter]

Jason: I don't even know if that one is still useful. I have no idea. I never bothered to look it up. I just put it in everything.

Dave: This rewrite has been a year in progress, kind of thing?

Jason: Yeah, a year in progress. It got really intense over the summer. We had some people just comb through. They know the code base to an unfathomable degree better than I do. We've brought new people in. We brought in somebody who said he had an interest in writing hooks because I showed around a demo of hooks working in Preact 8.

Chris: Nice.

Jason: He just wrote an implementation for this thing. I don't know if I'm supposed to say this, but the code name for the rewrite is called Ceviche. I have no idea where that name came from.

Chris: [Laughter]

Jason: I do like ceviche.

Dave: Mmm.

Chris: Yeah, well, that's nice. What's cool is that this isn't just -- you're not just doing this as a thought exercise. If this ships or -- I don't know -- I mean I guess you'll get to that point.

Jason: Oh, it's a "when." It's a "when." It was supposed to be end of February.

Chris: Nice.

Jason: We're still shooting for that.

Chris: Well, that's great. It really will have good stuff and it's still small, but now it has fragments and hooks. That seems like a big deal.

Jason: Yeah, it's actually smaller, so our original target for the Ceviche release was two kilobytes.

Dave: Wow. Wow.

Jason: Yeah, and if we didn't have to account for hooks and a bunch -- like we ended up building a bunch of optimizations in the diffing algorithm that don't exist in Preact today, so it will be faster, more memory efficient, and whatever. We didn't really account for the fact that we were also going to do that while doing the from scratch rewrite.

For a long time, it was two kilobytes. Then it was going to be 2.4 kilobytes. We sat at that limit for ages and ages and ages.

Then, as part of getting ready for an actual release, in a bunch of cases we were trying to figure out, do we add stuff to Preact compat and to the compatibility shim thing and have everybody end up using Preact compat because they want to use their party libraries, or do we take this opportunity to say, "Hey, what if the size didn't change so much? What if it only went down from 3.5 to 3 kilobytes, but what you got was a lot of React stuff would just work out of the box with Preact core?"

Dave: Mm-hmm. So, more compat would go back into core.

Chris: That's so wild to me. It's cool how much you care, but that seems like a no-brainer to me. 0.5K, who cares?

Jason: Yeah. I don't know. One of the other things that we did is, Preact has always been somewhat tree shakable, although, at 3K, the degree to which that matters varies. Marvin Hagemeister, one of the main core people, as part of moving Preact compat into our new mono repo that we're going to release, also decided to rewrite it. In doing so, the amount of compatibility shim that you end up with is a function of how much compatibility you needed. So, like, if you only needed the React.children API because something is using React.children.only, you're basically getting something that looks like an implementation of a ray.prototype.fullreach and an object with three properties. That's it.

The same deal with hooks. We actually decided to ship hooks as, like, a secondary module, so if you input hooks from preact/hooks. The way it's structured, you only pay for the hook that you import. There's no native programming in behind them. Each hook is a standalone implementation of that hook and all of its infrastructure. It's kind of cool. If you don't use layout hooks, you're not going to get a polyfill for after frame callback.

[Banjo music]

Chris Enns: This episode of ShopTalk Show was brought to you by something really cool. It's an alternative to .com. It's the .design domain name. If you're a designer and you've thought of a sweet name for your website and it isn't available under .com, check out .design. Chances are, the domain name you want is waiting for you. You can head to Porkbun.com and use the coupon code SHOPTALK on the checkout page to get your free .design domain name for your website.

.design is super widely used, too. It's not like it's a super weird one anymore. There is airbnb.design, facebook.design, uber.design, adobe.design. There are so many of them. It's exactly the same. It's just a domain name and Google doesn't really care these days.

It functions the same way as .com or .org. It's just more interesting. It looks great on resumes or business cards, on email addresses, and it's free. Did I mention that? You can go to Porkbun.com and use coupon code SHOPTALK at checkout. You get a free year of email hosting, Whois privacy, and SSL certs and all that stuff, which is pretty fantastic. You should go get one right now. Thanks to Porkbun.com for sponsoring ShopTalk Show.

[Banjo music]

Dave: Well, that's really cool. I cornered you at a conference. [Laughter] I was just like, "Three kilobytes? How?" I feel like you're constantly hammering and shaving off every single thing you can.

I guess my next question is, why are you setting such an aggressive bar for JavaScript? Let me tell you. That doesn't seem to be the trend. [Laughter]

Chris: The question is, how and why.

Dave: Yeah, how and why? Then I think the third part was, what's missing from the React API? You can't just have React, but smaller. That doesn't seem possible to me. How, why, and what's missing?

Jason: That is the goal. The how and the why, to me, this is a weird one that probably requires a little bit of backstory. I used to maintain -- I built and then maintained, first as a personal project, then as my startup, then after the acquisition as my full-time job at my previous company. I maintained what I want to call an implementation of an operating system in a browser.

Take this with a massive grain of salt. I don't even have an engineering degree, so it was missing a bunch of crap.

It was this idea of being able to have multiple apps that live inside of a browser tab, and they'll come with you across all your devices and do sync and all this fancy stuff. It was this gigantic, somewhere between two and five megabytes of compressed first party JavaScript to implement all these things. It had an implement of most of node's core modules and it had an implementation of the DOM, Web components, Shadow DOM, and all this insane stuff.

I was always working in this environment that started from being unfathomably large as a Web app, like in the era of Android between 1.6 and 4. These were the kinds of things I was trying to make load in a mobile browser. Eventually, that project ended or whatever, and I left.

There was this kind of void for me because I had set up so much infrastructure and substrate for me to build on that I had this way to be super productive in a browser that wasn't the browser way of being productive. When that ended and I was forced to kind of move on to something that wasn't this monolithic, giant thing that I was productive on, I bounced around between a bunch of frameworks, tried a bunch of stuff out, and I felt like I was never finding a good balance between stuff that worked out of the box and cobbling everything together myself from random stuff pulled off of NPM.

I just started writing modules when I would find a case where the platform wasn't fitting my need because I had already been using Web components and Shadow DOM in this crazy, custom implementation thing, so I kind of chose those as my way forward. I would run into, like, "Oh, the routing is crappy, so I'm going to build a little router."

At the time, because I was starting from vanilla, everything that had a cost seemed like a huge cost. If your app is only 30K for a completed application, a router being 10K seems absurd. It's like, no, that's a third of the entire code base now.

I started writing them. I started releasing them. Nobody cared at all. If you go into the far reaches of my GitHub, there are hundreds of modules there that are just garbage lost…

Chris: Hundreds? Wow.

Dave: Yeah. I deleted a bunch of them because they're embarrassing and made mistakes, don't have tests, or whatever. My thing was, I was putting these things out there and using them myself, and that was fine.

After Preact took off, though, it got harder for me to just publish something to GitHub and have it be just for me. There was scrutiny. People would be interested in asking why I was doing a certain thing.

I had to a mantra around why it was that I was doing this to make it make sense. It was stuff that was already in my head, like why am I releasing small modules? Because I need small modules. But I needed to have some sort of a story that explains why.

What I came up with was, there are three factors that control the growth of a library, a module, or whatever -- the size footprint, the size on disk, size over the wire, size downloaded by NPM.

Then the runtime performance impact, so when you import the module, how long are you waiting for it to finish its crazy native programming steps and actually return you a value? Then how much is that going to affect every render of your application? How many times is it going to interrupt scheduling, whatever?

Assuming performance improvement is a constant goal, because I feel like at least that's the one thing everybody is kind of able to degree on, it's like, yes, performance matters to some extent. Then feature creep is a constant. You're always going to be asked for new features, assuming other people are using this thing. Everybody will have a new edge case. Everybody will have a new thing that you didn't think of when you were building it that totally means nothing to you but is the most important thing that you could possibly add to the library, TypeScript, or whatever.

The only way to actually control those other two factors is to just set a soft limit on something. Library size is the easiest one because you can measure it quickly using the gzip size module. It's interesting because what it turns into is not like, "Hey, we need to have this library deliverable in one packet," which is, I think, what people assume that's coming from.

It turns into, instead, every issue and every pull request that gets opened against a project has to answer the other half of that equation when you make one of them constant. If you're asking for a feature and size must be held as a constant, then you have to either give up runtime performance or you have to change the substrate that that equation is being run on. You have to find a way to golf down something that's already in the library to offset the cost of that feature. What I have found is that this is a way for new features coming into a library to be forced to do tech debt fixes in that library.

Dave: Wow. Yeah, that's cool.

Jason: Yeah. You have to go back and rehash these things because you need to find a way to squeeze eight bytes out of this other code that's super old that works just fine because you need those eight bytes for this new feature.

Chris: Hmm. And they do it. It works.

Jason: Mm-hmm.

Chris: Wow.

Jason: Yeah, that was one of the weirdest things I'd noticed with this whole Ceviche Preact X rewrite thing is that now there's a whole team of people working on it. Through no involvement of me, every pull request has: Here's the performance characteristics. All the tests pass. This is plus 86 bytes with like a crying frowny face or whatever. What are we going to do, guy or, you know, everyone?

Chris: Yeah.

Jason: Part of the PR review process just becomes a crowd sourcing code golf of, like, how can we make this? In addition to improving this so we can merge it, how can we make this free…?

Dave: Oh, yeah, so you don't have plus only commits. You have, like, everything, ideally, is a plus and minus.

Jason: We shoot for negatives.

Dave: Yeah, negatives. Wow.

Chris: Yeah. You use the word "golf" there. Golf, I feel like, is that terminology that means, how can I use trickery to make something really small? How can I write a router in a tweet or something crazy?

The end result is usually just incomprehensible -- you know, it wouldn't mean anything to anybody looking at it, but it works, magically, because every function name has a single letter and it uses weird language features and stuff. Is there that in the code base, or can you golf without sacrificing?

Jason: This is where I think we actually are in a place with the JavaScript ecosystem that is kind of unique in that the codes that people write when they're contributing to, like, Preact, MIT, or any of these modules that I have to have these goals set on touch, you're writing a syntax that will be compiled by Terser or Uglify. That's just a known thing.

Chris: Yep.

Jason: It will be Uglified. Even in the case where we're shipping a not minified source, we Uglify it and then re-beautify it. The reason for that is--

Chris: You can write a function that says, "Get properties from DOM element."

Jason: Yes.

Chris: And it doesn't matter because it will be X in the output.

Jason: Yeah, and you start to learn these things about Terser. I like to use Terser as an example here because Terser is not at the same level as something like Clojure Compiler's advance mode where it will literally just, like, decompose all the parts of reapplication and figure out a different representation of that that happens to have the same characteristics. Terser is not that. It's just all of the weird code golf-y syntax optimizations as a service.

You start to learn how to work that in. You could totally use helper functions and that's okay. Just write them in the way that lets Terser inline them. Now they're free.

Chris: Okay.

Jason: We have a convention. This microbundle, I think, has this by default. It's used in most of these libraries now. It's like, if a property starts with an underscore, it's assumed to be private and it will be minified to a one property name prefix and underscore in the build.

Chris: Oh, but if it's not, it's public. That means other code might touch it, and I can't mess with it because I can't know if anybody else needs it.

Jason: Exactly.

Chris: Yeah.

Jason: Yeah, and so, like, there are weird implications here. We can probably get into this later, but I really want the ecosystem to shift towards not transpiling everything down level to ES5. I think, even in doing that, I still want to have this step of, like, you don't need to necessarily think about crazy, like inverting boolean logic, to save one byte optimizations. The compiler should be thinking about those things for you.

We only get into the weeds with those sorts of things as a final step in Preact before a release. Every once in a while, we'll do a version rev and then some iterations. Then one of the releases will be like, no functional change. We just found a way to shave 16 bytes off the library by rearranging some if statements or whatever. It's like you're writing a compiler specific variant of JavaScript.

Chris: Yeah, and almost like you think in hooks kind of thing. You think in golf or whatever. [Laughter] You think of how--

Jason: This is it. You're writing Uglify JS.

Dave: You don't actually -- yeah, you're writing for a compiler. Is that a skill everyone should have? Whether or not we write very popular, three kilobyte libraries, we all ship to a compiler at some point now.

Jason: I think, scope wise, as a community, app development is so distant from being in a place where those levels of optimizations matter at all that there is -- I don't even want to use the term "lower hanging fruit" because I feel like that's not grandiose enough. Shaving 16 kilobytes off of your 250 kilobyte gzip JavaScript bundle isn't going to help anyone.

Dave: Are you looking at my JavaScript…? [Laughter] How dare you?!

Jason: I would say 250, you're not in the … bucket at all there.

[Laughter]

Chris: It's interesting to hear you say that, though. That's not worth it? What is worth it, cutting it in half or rewriting it?

Jason: My philosophy here is that when you scale up an application that you have multiple people contributing to it and you're iterating on a feature set and that value proposition for your application is, like, "What does it do? What goals does it accomplish?" the types of optimizations that are impactful for you are going to be whole program optimizations. Those would be things like being able to apply a compiler optimization that strips out unreachable type errors.

One concrete example of that is, you could totally use Babel's strict mode class transform in development if you want to know that you've forgotten to call super. But in production, I would argue that no one should ever be relying on a failure to call super error in their logs. Nothing should ever get to that point in production, so you should compile that out. You should find those things that are run time in variants or unnecessarily detailed error messages that are trying to be helpful and just don't ship them in production. That's not where they're most valuable.

Dave: View does a good job of this, I feel like. Their production and their development branches are very, very different.

Jason: Yeah, exactly. React even just ships. They're just separate files. There's not even -- they compile flags out from one and the other. I think their production or the development build is four times as high as the production one because it's got all these error messages in it.

The thing is, we don't do this in application land. We use stuff like node end, but we don't really structure our code bases in a way that can benefit from it. We use syntax constructs that don't collapse well, like classes, decorators, object properties, and whatever. Then we actively transpile them to the largest possible representation.

For years, if you were profiling a website, you'd look for how many copies of regenerator were in-lined into the source. Regenerator was like 30K at that time.

Dave: Hmm. Yeah.

Jason: That's not a size that you can offset by saying, "Oh, we got this functionality out of it," because, in reality, it's just a syntax choice. You just didn't use promises. You used async await.

One of the optimizations that I would characterize as being this low effort, high value thing for apps would be moving or identifying that the way that you wanted to use async await was this 90% subset of the possible functionality of async await, like maybe you're not using await star or whatever, or that await for syntax, and being able to say, like, "I, in production, am willing to accept that I'm using a slightly nonstandard transpiled version of async await," and I can switch to Babel plugin async to promises or whatever and just apply that for the entire program. Those are things where it's going to take your bundle size down by a percentage, like a real 10+%, 20+% change.

Chris: That's a big deal. If we're talking about the JavaScript ecosystem, that's kind of what we're trying to dig into here. I love stuff like that, that type of thinking.

[Banjo music]

Chris Enns: This episode of ShopTalk Show is also brought to you by DigitalOcean. You can get started today with DigitalOcean with a free $100 credit by going to do.co/shoptalk.

DigitalOcean offers the simplest, most developer friendly cloud platform. It's optimized to make managing and scaling apps easy with an intuitive API, multiple storage points, integrated firewalls, load balancers, and more. From predictable pricing to flexible configurations to world-class support, you'll get access to all the infrastructure services you need to grow. Plus, DigitalOcean's community provides over 2,000 cloud agnostic tutorials to help you stay up-to-date.

Like I said in a previous episode, I fired up a discourse forum, old-school Web forum on DigitalOcean. They had tutorials that walked me through configuring it all. I had no idea what I was doing, but it works fine and flawlessly. There are tons of folks chatting in there, and it just works. I still get to feel like a smart Web nerd who ran some server software. But, most importantly, the folks who are using the forum have a stable, secure environment to carry on their conversations without having to worry about any server stuff.

If you've been thinking about starting up some sort of Web app, a site, or a service online, take advantage of DigitalOcean's free $100 credit by visiting do.co/shoptalk and get started today with DigitalOcean.

Our thanks to them for sponsoring this episode of ShopTalk Show.

[Banjo music]

Dave: I was going to say there's kind of another dimension here too, right? You sort of were getting at it. The modern JavaScript async await and then what's it look like after it goes through Babel? Most browsers support it, but let's say you support IE11. When you say three kilobytes as a target, is that the modern, ultra-modern weight, or is that the, oh, it's been through a little bit of reset ENV weight?

Jason: For a long time, I would have to constantly remind people that the only bytes that matter are the ones that get sent over the wire because the bytes on disk, when you're developing the library, only matter to you. That's a one versus ten kind of a problem.

In its current state, I would say, for the ecosystem, the size that you should track and the size that matters is gzipped compressed bytes because gzip compression and basic Uglify Clojure in simple mode Terser style whole program optimizations, those are the closest thing we have to free transforms for a whole code base. You should be able to--

You should assume that if somebody cares about performance, they have applied these optimizations and we can rely on that. Those bytes that you ship over the wire, you can kind of factor in the fact that that is our lowest common denominator level of optimization.

I still think that the size of the code before being transpiled, optimized, or minified matters because that's where you get into things like not even just syntax constructs, but one of the things that I always tell people about Preact is, Preact is a collection of 25 functions in a file and that is the entire architecture of Preact. There's nothing else there. There are not patterns. There are no gorilla banana problem or any of these things.

It's not because I'm a functional program … or anything. That's just the type of code that optimizes most readily. You don't have to infer anything about where properties live or what could be collapsed. There are no classes that you could potentially dissolve out as an optimization step. You've armed the compiler with everything that it needs to be able to reduce your code base in the highest possible -- in the most intense possible way.

Dave: Which probably makes sense at the framework level. With frameworks, you're usually not hopping in and you don't need big DX in there, you know. [Laughter] You just include it and forget it, kind of, right?

Jason: Yes. That's the other half of it. We all have this kind of in-built nature of wanting to ship things to NPM that just work out of the box. Because NPM is a repository for JavaScript and doesn't get more specific than that, it's not a repository for Browser JavaScript or Node JavaScript. We are all in this weird situation where, if you build a module and you want it to have any hope of running across Browser, Node, and maybe multiple different browsers, you have to go lowest common denominator.

I think if there's any one thing that's holding the entire ecosystem back more than anything else right now, it's the fact that we still have this expectation that NPM modules are ES5. It just breaks everything because everybody is afraid to transpile their node modules because it might take extra time, even though there are really, really easy deterministic caching that you can set up since NPM modules are immutable. We're just kind of afraid to take that leap as a community. I think, until we cross that bridge, we're going to be in this position where even if you do a really good job of doing differential serving, like using module, no module, or whatever to only ship modern JavaScript to modern browser … transpiled fallback for IE11 and prior.

Chris: Let's talk about that a little bit because if you're saying that's one of the biggest things that would matter these days is that there's this problem with shipping too much ES5 when it's unnecessary to do so and you're saying there's an answer to that in this differential serving thing, can we just put a point on that? That would be nice. Isn't it by default? There are probably a lot of people out there that maybe have never heard of it and are just ES5-ing just because it seems like what you do.

Jason: Yeah.

Chris: And that you're going to get the best compatibility that way.

Jason: Honestly, it's not the most intuitive solution, right? When I say module, no [module], differential serving is a confusing term because it could mean lots of things. It just means serving different things to different clients.

Module, no module, it sounds like what I'm saying is, "You should use ES modules when they're available and then not ES modules when they're not available." In reality, it actually has nothing to do with that and most differential, like, module, no module setups, don't use ES modules in either case. [Laughter]

What it is, is a cutting the mustard test. I try to remember what library used to do this. You'd stick an inline script in the head of your document that would check for something. It would check for, like, oh, do I have--?

Chris: Query selector all.

Jason: Yeah. Yeah, exactly, and it's like, "Oh, you have no query selector all. I'm going to bring an HTML5 shiv," or whatever it is. I forget what the semantics was. Normalizer is a similar thing where it's just like, "Here. Here's the APIs you might care about. If everything meets this decided factor, then you should go with modern mode."

Module, no module is that. If you're in a browser that supports JavaScript modules, you can assume that that browser supports classes because, as it turns out, all of them do. Because module, no module kind of gives you that point in time where all browsers suddenly were able to handle these types of things, like these basic syntax constructs like class, const, and whatever else.

Chris: Yeah. Right, right, right, right. You can make this. You can say, "Hey, Babel. Could you please produce for me a version of my JavaScript that doesn't have any of that stuff in it? There are no classes in there. There are no var const, whatever. And also, please also make me another version of my JavaScript that does have those things in there because, when they do, the size is just inherently smaller and probably runs faster."

Jason: Yeah, and the value here is the percentage of browsers that fall into the, "Yes, I have modules," camp is like 90.

Chris: A ton.

Jason: It's super high. It's almost to the point where people would question needing that fallback bundle. I think, while you could probably make an argument for like, "Oh, you should just use modern things, we'll just ship modern things, and people can upgrade their browsers," if you're in a position where you're forced to be pragmatic about this, at least what you've been able to do here is take a problem where you had to have one solution before where you had to go lowest common denominator and you've given it two denominators. You split the problem completely into two and now you have modern and everything else. You can load that everything else bundle up with all the transpiling.

Chris: Yeah. This seems like a really good pattern, like really good. JavaScript community, I feel like, has had a lot of success stories at sharing what a best practice is and having that just dig into the mindshare of the world and everybody just does it that way, but this one, it doesn't seem to be winning to me. It doesn't seem to be like, this is de facto. Is there some way that we can get it there? Could a tool like Webpack or something just start helping with this by default or something?

Jason: Webpack is in an interesting position here because their substrate for the 90% use case, most of -- this is totally a grandiose generalization. Most Webpack usage is either through some sort of a CLI wrapper like React app, UCLI Preact, CLI, NextJS, whatever, or it's something that somebody cloned because they were learning Webpack or found a config that was really good.

Dave: Copy and pasted Wes Boss's.

Jason: Yeah!

[Laughter]

Jason: Yeah, or one of the starter packs that have more stars than GitHub is capable of rendering.

Dave: Right.

Jason: It sort of takes them out of a position where they could do something like this by default because they don't set the defaults. These tools set the defaults. And so, one of the things that -- like, I've even been very, very arm's length. I don't want to take any credit for any of this because I'm totally uninvolved.

People like Chris Baxter who is now at Google, Luke Edwards, Pratik, a bunch of have been kind of in this space poking the various command line tool authors being like, "Hey, there is this huge, usually on the order of 30% size savings you can get." When you go and tell people, "Hey, you know, to do NBC in vCLI is X number of bytes over the wire," you can tell them the module version and that's all you have to say. It's totally okay because, for the 90% use case, that's what you're shipping.

They've been kind of doing that work to get everybody onboard. Now, vCLI has shipped differential serving. Pre-XCLI has differential serving it as xpering. I think NextJS might even be looking into it, which is super cool. Create React app….

Chris: That is cool. That's cool. Some people know this. It's coming.

Jason: Yeah.

Chris: It's happening maybe slower than we'd like, but the tools know this and are fixing it.

Jason: The tools know it. Everyone knows that this is an opportunity. There is some discussion of whether this is enough or whether that module versus doesn't have modules cut off is going to turn into the next, you know, one bundle to rule them all. So, you know, people have been pitching, oh, have a script tag with an async function in it and then check to see whether it got declared successfully and load depending on that. Your litmus test is instead, do I support async await?

Chris: Okay.

Jason: Yeah, there's….

Dave: You can always change that, the test. Hopefully, the test becomes easy to change. I don't know.

Jason: Yeah. The beauty of module, no module that nothing else can ever come close to touching is that it does not require script execution in order to make this decision.

Chris: It's like a native browser feature?

Jason: Yeah. Well, with the exception of Safari 10.1 point something. They broke the no module attribute, but there's a workaround for that that involves some script execution. [Laughter]

Dave: Wow.

Jason: But, in a lot of cases, because it was a patch update and it's been fixed, people will just ignore that, and that's fine.

Dave: When does this module, no module future -- I mean is it happening here in 2019?

Jason: Yeah, I think so. I think, once you get the starter packs of the world, like once Angular CLI, vCLI, NextJS, Create React app, and Preact CLI have all shipped this, really the only thing left is boilerplates. That gauntlet has now been laid. We know what the standard is as a result of people actually deploying these things to production and seeing the size benefits.

By the way, not only size benefits. The killer feature here is, when you load something as a module, you're in a different parser. You are literally -- so, there are like two modes for JavaScript parsers: classic and module. Classic does crazy stuff. You can use HTML comments in JavaScript in the classic parser.

Chris: Listen to this! This is wild!

Jason: Yes. Yeah.

Chris: Wow.

Jason: There are so many things that everybody, if you thought about it, would recognize like, yep, that doesn't belong in the scripting language that I want to be using in 2019, and so many of them are turned off in modules that this is just the hugest single way to make that giant leap forward.

Dave: Wow, because quite literally we've been calling this new era kind of modern JavaScript. Under the hood, in the module, there's a new, modern JavaScript.

Jason: Oh, yeah. Everything is strict mode by default. While that might not feel like it means a lot, when you're talking about a JavaScript engine that needs to compile stuff, we're not really in interpreted engines anymore, with the exception of the first tiny fraction of time your app is running in V8 now. Any time where you can not have to account for some crazy edge case, especially ones that are very unlikely to ever be used or to happen, like mutating the arguments object in place, that's a cost savings at the engine level, but we have to be in a mode where we can enable it. And so, modules is just like one -- it's like a very neatly packaged up with a bow version of this that you can opt into super easily.

Chris: You know what this reminds me of, and this is such a small thing comparatively to what we're talking about here, but there's this feature in CSS called @Supports, a block. It has a JavaScript API as well, but you can write. For three or four years now, I think it's been kind of considered a bad practice to write an @Supports block and a not supports block, even though it would be really nice to do that because then it's totally two separate things and you don't have to worry about what's cascading into each other and rewriting stuff.

We're starting to get to that point where it is okay. Once we're past the point where browsers do consistently support @Supports, then you can write those mutually exclusive blocks and it's pretty cool.

Jason: Yeah, exactly. If you had some of your code base that was in classic mode and then this new stuff that was in module mode and, when it didn't load, it wouldn't override stuff from class mode, that's a really hard, weird inheritance model to think of in your brain versus, if you just split up the two things as just … groups, especially if it's "to" and not "in," things get really easy. You're in a position where let's say you're using @Supports for some feature like viewport units or whatever, your browser support crests 95%, and you decide, "Oh, I don't need the other one," you just delete the other block. That's all you do. There's no, "Oh, how is this going to work with a cascade of rules prior to it…."

Chris: Right. That reminds me of polyfills.

Jason: Yes.

Chris: Oh, I'm polyfilling this feature and I don't want it anymore. Ideally, everything has been written in such a way where you just stop loading the polyfill then.

Jason: Yeah. That's the other half of this equation, too, for JavaScript at least. We need better solutions for automatic polyfilling, and we have better solutions for automatic polyfilling. We need to use them, stuff like polyfill.io and even just basic stuff.

Chris: Yeah, which is notable in that it doesn't polyfill unless it needs to, which is….

Jason: Exactly.

Chris: Yeah.

Jason: And it's highly cachable. I would really like to see someone write a clever little client side library that pulls from an iFrame with a service worker so that more than just rely on the HTTP cache, you can pull your polyfills through your current browser from memory in a frame--

Chris: [Laughter]

Jason: --almost synchronously, like with one microtask, tick, or whatever, and just have that be how you load polyfill, so there's no hit.

Dave: Mm-hmm.

Jason: Nobody has done this yet, but almost like a client-side CDN kind of thing. I feel like that would a cool situation where you just work on the Web platform, whatever the current year is. That's your spec year, and you just don't ever think about the compatibility implications of using these things.

Chris: We heard, when we talked with Laurie Voss, it was not at Google--it was at NPM--but seemed to indicate that they've talked or he's talked with people at Google in such a way that maybe -- you know … far from dumb. They're like the most invested in browser tech of any company, really. They can see that this world of React and Preact or whatever, like frontend JavaScript, is what everybody is doing already. That ship has already sailed, in a way.

The future might be optimizing browsers for that case. I know I've shifted the conversation here. I did it kind of on purpose as we wrap up here. Do you have any insider knowledge on that? If you can't share anything, does that sound reasonable to you that browsers themselves might be like, "Oh, okay, well, we're not shipping HTML anymore, so we'll optimize for JavaScript," in a way?

Jason: That's very reasonable and I would say there is a huge interest on the Chrome team and other teams, even, at Google in changing the way we build -- changing the way we think about browser usage by developers from just assuming that everyone is using the primitives to understanding how those primitives are being used by the intermediary layer of libraries and frameworks and understanding it at more than just a superficial level.

Chris: A primitive being HTML, CSS, and JavaScript, right?

Jason: Yeah. HTML, CSS, and JavaScript, direct DOM manipulation or whatever. There are definitely people still doing that, but those are the cases we've been optimizing for in the browser for years.

Chris: We assume that you're building websites this way, so we're going to make everything really fast, assuming that you're doing it that way, and maybe that assumption is not so right anymore.

Jason: Yeah, and I feel like there is a risk. There's a risk internally and externally always of making it seem like this is some huge internal change or whatever. I don't think that's the goal at all.

It's really just an empathy thing. We've realized that you can't just only build the substrate and expect people to just work out for themselves what the best practices are on top of that. It's amazing at this point that people have done such a good job of doing that, but why can't you have the knowledge of engineers who work on a browser engine and incorporate that into the feedback process of building a library?

We've been talking to the React team a lot directly at conferences. Honestly, it's been fantastic. It's really interesting to hear all the feedback from them about, like, "Oh, we're pushing the limits of this," and it totally, totally falls over or whatever. [Laughter] Sometimes it'll be the first time we've heard of it. Or if we're working on a new spec, it's not just like, "Does this make sense for Web components in vanilla JavaScript?" You factor in, does this make sense for where that huge percentage of the ecosystem is?

Chris: That's good. [Laughter] I'm glad the talking happens. I think a lot of people kind of assume that the talking happens, but it turns out it's in a hallway at the Hilton Garden Inn, you know.

Jason: [Laughter] Yeah.

Chris: At least it happened.

Jason: Now it's much more intentional.

[Laughter]

Chris: Yeah.

Dave: Well, I think we should probably wrap it up here, Jason. Thank you so much for coming on. I was surprised to learn your last name is not Developit.

Jason: Hmm.

Dave: What a great time. Do you have any parting advice for people wrangling kind of JavaScript apps? Is there a quick tip? I feel like you're on the other end of the spectrum running three kilobyte frameworks. How does average Joe or Jane developer start embedding these processes in their workflow?

Jason: I mean the easy thing in modern JavaScript land is, do a real build or buy analysis, like a comparison between the tools that you have available when you're starting out. Your build tool, your transpiler or, preferably, something that wraps all these things up. If you aren't in a position where you feel like you have this huge working set of knowledge that you can bring to a tool chain for building your app, you should find one that can deliver on your needs. Go and look at the performance characteristics and goals of each project, each of these CLIs I'd mentioned before.

To me, that's almost a bigger factor in the decision of what stuff you might use as a JavaScript developer to build an app than your library or your framework is, like, what is the tooling around that that can support you as you move from prototype all the way up to production? Then every time you build an app, just sort of do a check to see. When I look at my bundle, does this look like code I would write or is this something that is just so far removed from what I've typed into my editor that I don't understand it?

Dave: All right. Awesome. I think my dog is barking at the mailman, so that's time to wrap it up. [Laughter]

Thank you. Again, thank you so much, Jason, for coming on. For people who aren't following you and giving you money, how can they do that?

Jason: Oh, I don't need money, but they can follow me on the Twitter. There's an underscore before my developit handle there because I've not been able to rectify that or follow me on the GitHub.

Dave: All right, well, thank you so much. Yeah, thank you, dear listener, for downloading this in your podcatcher of choice. Be sure to star, heart, favorite it up. That's how people find out about the show. Follow us on Twitter at @ShopTalkShow for tons of tweets a month.

If you hate your job, head over to ShopTalkShow.com/jobs and get a brand new one because people want to hire people like you.

A little bit of housekeeping. Our website fell over. We're working on it. Hopefully, it's done this week. [Laughter] Anyway, we'll figure that out.

Chris, do you got anything else you'd like to say?

Chris: Yeah, you know I looked on iTunes. Hopefully, that is sorted out. We haven't had a review in months. If you love the show, five stars.

Dave: Yeah, five stars. Use the word "tractorbeam." That's it.

[Laughter]

Chris: ShopTalkShow.com.