 Hello everybody and welcome to EmberConf 2022. Last year I gave a long and winding description of the history of Ember and how we evolved our approach to evolving the framework. This year I want to talk about the next big phase in Ember's evolution, the Polaris Edition. Before we get into Polaris, I want to talk a little bit about what Ember is. People often ask me, if Ember is evolving to adapt to the ecosystem, then what is Ember? What makes Ember 1.0 the same framework as Ember Polaris? At a basic level, the answer is Ember apps. Ember apps built a decade ago, including Skylight, the app I work on, smoothly evolved from a world where the dominant frameworks were a backbone and knockout, a world before ES2015, and a world where Bower was the dominant way of getting assets into your front-end app, to a world with bundlers, MPM, and TypeScript. Ember and many other Ember apps managed to maintain momentum over a decade, while smoothly adapting to the ecosystem around it. In a nutshell, Ember is a framework that prioritizes making that possible. Ember is for you if you want to build long-term products together with other people. So that means that an Ember app is a long-term product built together with others. When I say others, I don't just mean other developers. I mean developers, of course, but also designers, DevOps folks, marketing and sales folks, and all the other people who make a product tick. Now what is a product? Well, for one thing, a product is more than just a user interface. Of course, a product includes a user interface, but a product is an end-to-end experience built for customers and their needs over time. Ember is a product framework because the source code you write in an Ember app describes the way it works across many different facets of a product's lifecycle. A product is developed, tested, continuously integrated, and released to production. So Ember's authoring format and its public APIs support all of those modes. Your route, component, and service definitions run in a development mode server with assertions and other niceties that are too slow to run in production and are then automatically stripped out if you ask Ember to build a production build. The thing that makes Ember a product framework is not that it's possible to create a development or production build. Basically, all UI frameworks do that. It's that we design our app and add on APIs to support those modes without you needing to understand it. Similarly, just using Ember's testing APIs as documented will give you out of the box local in-browser testing as well as testing in CI. You don't need to learn about how to wire up or configure your tests. You just write them, run them locally in development, and the same source code runs in CI. And because these details are part of Ember's public API, we can adapt to ecosystem changes over time, like the migration to Babble, changes to Babble over time, the rise of GitHub actions, and many other changes without requiring you to learn about those things and update your app's setup and configuration to take advantage of them. Which brings us to the second thing that makes Ember tick. Ember is for people who want to build long-term apps. When I say long-term apps, I mean products that outlive the ecosystem trends that were popular when they were created, and frankly, many more trends after that. Long-term products need to adapt to changes. A lot of changes represent important innovations, and they need to do that without long feature pauses every few years to significantly retool the application. A good long-term product maintains its momentum. Customers have no idea about big technical changes, and it's rarely acceptable to tell a customer that a feature that they want is on long-term pause while you migrate your entire code base to a new paradigm. Long-term products take advantage of innovations on behalf of their customers without grinding to a halt. When we say that Ember is a long-term framework, we mean that Ember is designed to help you with this problem. When you start a new Ember app, you're of course targeting today's tools, web browsers, packages, and techniques. But a few years later, a long-term product targets a new set of tools, web browsers, packages, and techniques. It's how progress works. As a long-term framework, Ember takes on the responsibility to keep up with those changes and help applications smoothly migrate over time without the need for huge feature pauses. This is a feature of Ember that doesn't appear in any feature comparison checklist. Each version of Ember incrementally migrates Ember applications along with the ecosystem. You probably wouldn't notice that except when you look back a few years later and try to imagine where your app would be if you had hand rolled something at the beginning and updated only when you really needed to. What I mean when I say that Ember is a long-term framework is that when we change our recommendations over time, we invest in a detailed, worked out migration strategy. This applies just as much to new idioms as it applies to breaking changes. One example, our policy is that before we turn on a deprecation in stable Ember, we first land the feature that you're supposed to migrate to and write a migration guide how to go from the deprecated feature to the new feature. We don't do that before we remove the deprecated feature. We do that before we tell Ember users that they should move away from a pattern. I bring up Skylight a lot, but it's because it's honestly astonishing to me that a code base that started 10 years ago is somehow aligned with today's ecosystem, even though we started in a world that predated even Bower. I know that we don't always think about things this way in the Ember ecosystem, but Ember has gone through a ton of these sort of changes. Early on, we added Ember CLI, moved all apps to ES modules, actually before ES 2015 even landed. We adopted promises for Async, we landed Glimmer, a whole new rendering engine, we've migrated to JavaScript classes, Glimmer components, the whole reactivity model and PM packages and the list goes on and on. And along the way, we've always kept the old world running alongside the new world, providing a path for apps to keep building features while incrementally moving to the new patterns. And we adopted all those changes in Skylight, even as our team gained new engineers across the experience spectrum. At this point, all the developers except me who work on Skylight weren't even there when we created it, which brings me to the final piece of Ember's formula. Ember is for products that are built together with others. Products built together have little time for Rockstar Ninja developers throwing together their pet theory and foisting it on to the rest of the team. When product teams build their apps together, we think about what future team members will think about decisions we make today. Since future team members will, by definition, come from outside of our team, we prioritize solutions that we can share with the ecosystem rather than solutions that we build in-house. One thing I've seen a lot in teams that valorize Ninja development is this, someone will put together a bespoke in-house solution and put it together in the code base. A few years later, that team member has moved on to a new company, and everyone else is stuck with the consequences of the original developer's hubris. If we're really thinking about our future team and not just our current team, we focus on using shared solutions whenever we can. So then what does it mean for Ember to be a together framework? It means that we focus on making sure that people can pull out slices of their apps into add-ons very easily. This means that add-ons in Ember are slices of products. They support running in development and release builds without any configuration. Because they are built using the same authoring format as applications, they support testing both locally in a browser and in CI, again without any special configuration in the app. When written idiomatically, they support server-side rendering, Clyde-side rendering, and rehydration. And add-ons are also slices of long-term products. By using Ember's authoring format, they take advantage of Ember's strategy for migrating apps along with the ecosystem. And when changes are necessary, changes to add-ons automatically propagate to all applications that use them. So add-ons are an essential part of our strategy for making big changes in additions while allowing apps to maintain feature momentum. So is Ember for you? If you want to build long-term products together with others, then Ember's design and process for evolution is for you. On the other hand, if you're building a short-term product like a temporary microsite and you're doing it on your own, you'll probably find that these aspects of Ember's design get in the way. So today I want to talk about Ember's next addition, Polaris. I'm so excited about how awesome Polaris is going to be. We're going to get template tag, tree shaking, code splitting, track built-ins, resources, type script, accessibility. Now you're probably thinking, is that even possible? Is it going to take us five years? If it takes five years, will Ember still be around in five years? Well, the answer is yes, we can do it. To prove it, I wanted to take a closer look about what we were able to accomplish and octane together. One thing worth noting, the work of Ember additions happens over a few years, but the exciting progress, the part where wide swaths of the community can start using the features is concentrated at the end. So I'd like to take a look at the octane timeframe as a way of calibrating how far along we are with Polaris. Most years, when I sit down to work on the keynote, it feels kind of boring. Yep, we made some more progress, some more progress here. Honestly, that's kind of true this year too, but I thought maybe I could try to unpack what some progress and pretty close means when it comes to octane and Polaris. In some sense, almost all the features of octane are centered around a shift from classic classes to native classes, and generally de-emphasizing the role of the big component superclass in favor of smaller pieces that work well together. So instead of object.ext, we get class extends. Instead of properties pretending to be fields, we get fields. Instead of a custom init function with a special implementation of super, we get a class constructor and built-in super. Like always, native classes landed alongside classic classes. You can subclass a classic class, and you can use classic features like computer properties and native classes. That's a lot of the word class. For simple cases, we wrote a code mod that does the transition for you. So Ember apps could keep shipping while incrementally moving to native classes and use native classes in new code. Building on the migration to native classes, octane also introduced a new reactivity model. Prior to octane, Ember had moved to a unidirectional data flow model. The process started with Glimmer 1 in 2015, and we finished the job when we made backtracking, re-render, and error in Glimmer 2. But a lot of the inner workings of the reactivity system were still based on the legacy of Sproutcore 2 and the cocoa data binding system that inspired Sproutcore. In octane, we took the reactivity system that we used inside of Glimmer to model unidirectional data flow and made it the reactivity system for your entire app. Basically, here's how it works. You start by defining your mutable inputs. These are the pieces of mutable storage that will cause the DOM to update when they change. You then use those inputs either directly or through functions and getters in your templates. Ember automatically keeps track of which inputs are used in each output node and keeps the outputs up to date as the inputs change. Here's how I like to describe it. Your data and computations live in a data universe, and you can understand the data universe completely without knowing anything about the output DOM. And the output DOM reads from the data universe to keep itself up to date, and that backtracking re-render thing, that's just another way of saying the same thing. The render process reads data from the data universe. It shouldn't be writing to it. Now, you write to the data universe inside of an event handler or other browser callback. In this example, we're incrementing a counter inside of a click handler. Internally, we remember that the counter was updated, but we don't do anything else yet. You can immediately read from the counter or call functions and getters that use the counter and they will immediately get the new value of the counter. You don't need to wait until another render. The data universe is always internally coherent. Now, since something changed in the data universe, Ember schedules an output re-validation, which will happen after your event handler is all done and you've made all the changes you want to the data universe. When that happens, Ember will discover that this node used the count field before and that it has changed since the last time we checked. So the output node then reads from the data universe to get the most up to date answer. After that, all the nodes in the output DOM are up to date with their inputs and Ember waits for more changes to the data universe. This whole system boils down to three very simple rules. One, you mark your mutable state as tracked. Two, you use normal functions and getters to compute state and you don't need to annotate those at all. And three, finally, you access that data in your template and Ember will keep it up to date. In case it wasn't already obvious, we call this system autotracking. What I really like about this model is that it's basically the Excel model that says it's pretty understandable to a lot of people. And it's fast. Ember only computes the value of DOM nodes if they might have changed and it can quickly zoom in on the specific nodes whose inputs have changed. But getting back to our main theme, what's truly mind-blowing about this is that we landed the activity model alongside the old computer property system and we made sure that it would be possible to incrementally migrate from computer properties to autotracking. And not only did we make sure that classic computer properties could be used in classes that use autotracking, we made sure that classic classes could use track properties and even getters. Internally, there aren't two systems. There is a single autotracking system that was designed to be expressive enough to support classic features. Doing all of that work might seem counterintuitive. Surely we can go faster if we just built a new system from scratch and kept the old system working and not spend all this time on interoperability. That's what React did with hooks. You can still use classes in React, but you can't use hooks in them. It's a great idea when you don't consider the implications of long-term apps. Long-term apps need to adopt changes like this or risk getting stuck in the past. But in order for apps to maintain momentum through these changes, they need the ability to migrate incrementally with good interoperability between the past and the future. Now, the work on Glimmer 2 didn't just give us the design for a new reactivity system. It also gave us a much better design for components. Instead of a DSL in which you describe the root element in JavaScript completely differently in the app, Octane components just use HTML for the root element. And instead of putting your JavaScript and template files in two really separate locations, you can put them right next to each other. Similarly, instead of adding behavior to elements through mixins, you can define behavior for elements in standalone modifiers. One nice thing about this is you no longer need a component class just to give behavior to an element. In Octane, component classes exist purely to add local state to your component. Everything else that was done with lifecycle hooks components can be done with modifiers in Octane. And once again, Octane components are not a separate system that lives alongside the classic system. You can use angle bracket invocation to invoke classic classes, and you can use modifiers in the templates of classic components. And you can invoke classic components from inside of Octane components. Under the hood, this works because Glimmer only has one kind of component, and components define their precise behavior by giving Glimmer a component manager. In order to allow classic components to be seamlessly with Octane components, we made sure that the component manager API was expressive enough to describe all the interesting features in classic components. By investing in interoperability between classic components and Octane components in the core architecture, we made it possible for apps to begin using all of the new Octane features in new code without worrying about what that would mean for existing code. And over time, apps could incrementally move components to Octane, either by using code mods to help automate the process, or whenever they happen to touch a file containing a classic component. We also added built-in support for NPM packages in precisely the same way. In Octane, you can just import from NPM packages, but that system lives side-by-side with the classic app.import system, allowing apps to start using NPM packages directly moving forward without breaking existing code written before everyone had standardized an NPM. In order to live in the future, we need to migrate from the past, and it doesn't slow us down. In our view, we get to the future fastest when we move smoothly, not by stopping the world on a big adopt-the-future migration project. So now that we took a look at Octane, I want to talk about Polaris. Just like Octane, Polaris brings a whole bunch of new awesome features to Ember apps and helps Ember apps take advantage of innovation in the JavaScript ecosystem. And also like Octane, Polaris builds the future alongside the present with an eye towards a smooth migration process that lets apps use those exciting new features in their own code. So Polaris is Ember's next addition. It builds on Octane's reactivity model and closer alignment with JavaScript standards. Polaris will align Ember with the modern NPM package ecosystem, bringing zero config code splitting per route, automatic tree shaking of unused Ember features, excellent built-in TypeScript support, and templates that can directly import their dependencies. Let's dig in. I want to start with my personal favorite feature of Polaris, and that feature will have the biggest impact on your day-to-day work, the first thing that the new template tag feature lets us do is import components using JavaScript import syntax. This gets us away from Ember specific resolution rules that you have to learn and lets you use the same import statements that you use everywhere else in your app. The primitives necessary for template tag have already landed in stable Ember, and you can use it today in your app with the template imports add-on. But that's not all we can do. Now that our templates are in JavaScript files, we can use normal JavaScript functions in the same file and invoke them directly from the template. The signature of a function called from a template is exactly what you'd expect, no extra ceremony. This feature is built on Helper manager, which has already landed in stable Ember. You can use it today with the Ember functions as helper polyfill add-on. Yes, that's a very long name. Same deal with modifiers. You'll be able to write modifiers in the same file and invoke them using natural syntax. This feature is based on Modifier Manager, which actually landed before Octane, and the design for using plain functions for modifiers is in progress. Now, I'm personally working on a library that explores unifying helpers, class-based helpers, modifiers, and resources, and I'm pretty happy with our progress there. If you want to learn more, check out StarBeam on my GitHub. Incidentally, Polaris will include an API for creating modifiers, so you won't have to learn about and install a package for that anymore. All that's great, and it's nice how much you can do without needing state in your component. But if you do need local state, you make a class just like in today's Ember. Instead of putting the template in a separate file, you put the template right inside the class. One thing I really like about this is how natural the upgrade is from a template without any internal state to a template with state. And I also love the fact that you can use functions instead of methods in more cases. To get a flavor for how this would work out in practice, I slightly changed the example so that we're counting inches instead of just numbers. To format the inches, I used the Intel.NumberFormat API. As an aside, I think the Intel API is genuinely epic, and people should use it more. End of aside. The key here is that the describe function is just a normal function, and you can break it up however you would normally break up functions in JavaScript. I can easily pass a specific locale to the describe function. And if I want to take named arguments, I just take an argument as the final parameter. In other words, if you want to function in your templates in Polaris, you write a function. And if your locale was stored in a service and was reactive, that would work just fine. In general, you can mix and match these features as you'd expect. So that's the status of template tag. If you're feeling adventurous, you can try it out today in stable Ember apps by installing the Ember template imports add-on. The second major pillar of Polaris is the reactivity system. This is the area I personally dedicate the most time to, so I'm going to have to restrain my enthusiasm and avoid spending all of my time on this topic, but it is pretty awesome. First of all, I'd like to define reactivity because it can be the subject of confusion and controversy. My goal with this definition is to cover all of the major UI frameworks. In my opinion, people correctly have an intuition that front-end frameworks are doing something reactive and I think a useful definition of reactivity should go with that intuition. In user interfaces, a system is reactive if it has reactive inputs, a mechanism for computation, and reactive outputs described in terms of those computations. And the system is responsible for keeping the outputs up to date as the inputs change. The programmer described the connection between the inputs, computations and outputs, and does not write code that updates the outputs manually. Now, you might be thinking that I came up with that definition so it only applies to Ember, but the opposite is true. Sometimes people say that react is not reactive and I got to say I'm not the biggest react fanboy to put it mildly, but I think that's pretty silly. So now that we've defined reactivity, let's take a look at octane reactivity with that in mind. As you can see the person class is a normal JavaScript class with normal JavaScript fields. I use TypeScript syntax here for documentation basically and we added some annotations to the fields to say they're reactive. What does it mean to say that the fields are reactive? So far, not much. The person API is exactly the same with or without the tracked annotation. You don't have to wait some amount of time after writing to name before you can use the description getter. When we say that the fields are reactive we're actually talking about the output. When a field is reactive and a computation based on that field is used in a template Ember will automatically keep that output up to date. We can summarize the philosophy of Ember reactivity like this. You mark your mutable state as tracked you use normal functions and getters to compute the right values. You use the computations in the template and Ember keeps the output up to date. If we want to get a little more specific we have a hard and fast rule. You should read and write track state in exactly the same way as you read and write normal JavaScript. If you remove the annotations the behavior of the class itself is exactly the same. You can write a suite of unit tests against the class remove all the tracked annotations and the tests will still pass. Now let's look closely at track fields and see how they satisfy these rules. First of all the reactive name and location are stored in JavaScript class fields. We annotate those class fields with decorators which allows Ember to keep track of them when they are written to and when they are read from. The final thing we need to check is whether the code has the same behavior without the tracked annotation. In practice this means we need a way to intercept all reads and writes of the underlying storage but then do the original read or write. In this case we use JavaScript accessors behind the scenes to intercept the reads and writes so we can record what happens but then we always do a normal read or write afterwards. So if we create a new instance of person and check its description we get the value that the getter computed. If we then remove the tracked annotation we get the same behavior. So we got the job done. Track fields follow our rules and we ship them an octane. But let's take a step back and ask why do we care about these rules. Basically because they give us a reactivity system that allows us to think about our data in normal JavaScript terms and then layer on reactivity by saying mutations to this field should be tracked because I want the output to update when they change. You use normal JavaScript functions to concatenate strings, filter or map arrays or build intermediate data structures. And you can see the result of those computations immediately because they're normal functions. And perhaps more importantly all that works in a file containing a component or in any other file in your application which means that you can refactor reactive code from your components into separate files freely. This might all sound very boring and obvious but believe it or not Ember is the only framework that uses normal JavaScript access patterns to read or write data and allows you to use those access patterns in any file. This gives us a general recipe for creating more reactive data structures in Ember. We build Ember's reactive data structures on top of JavaScript syntax or APIs. We provide a way to annotate the JavaScript features so we can intercept, read and write to the data structure and keep track of them. And we confirm that the reactive data structure behaves the same with and without the annotation. Now, tracked fields are all well and good for single pieces of data but sometimes you want to use other data structures like maps and sets. And you'd like to be able to do that without needing to learn convoluted patterns for manipulating maps and sets by creating new maps and sets. Also, if you change a single entry in a map you would like to use any other part of the map. Okay, so Polaris will add new reactive data structures that follow these rules. Let's see how we can build a reactive map by following the recipe. I made a simple example of a shopping cart class that uses a map internally. It lets you add and remove shopping cart entries and get all the items as a cart. Pretty vanilla stuff. Now we'd like to make it reactive so I can loop over the cart in a template and have it automatically update when I add or remove items. Step one is pretty straightforward. We're turning JavaScript map into a reactive map. Now we need some way to annotate the map as tracked. Because map is an object and not special syntax like fields we can use a function to mark it as tracked. Since map is a JavaScript class we can implement an equivalent API that uses map under the hood but allows us to intercept the reads and writes. And removing the call to track brings us full circle. The behavior of track map is exactly the same as the behavior of regular map. Okay, so Polaris will include track maps. The primitives necessary to build track map have already landed and you can use it today via an add-on. I have and it's awesome. But we can do way better than just adding track maps. Polaris will include tracked versions of object, array, map, set, week map and week set. For example, here's the same principle applied to arrays. Instead of needing to learn obscure patterns for manipulating arrays by creating new arrays you can just manipulate arrays directly. All of these track built-ins follow the same fundamental rule. If you remove the tracked annotation the behavior of the code that uses the built-in will remain the same. And they're all available today in an ember add-on that works with stable ember built on top of stable primitives that have already landed. Next up I want to talk about embroider. Talking about the details of embroider can get pretty weedsy so I want to try to ground it in a basic goal. Ember apps should be able to add features and screens to their apps without affecting the bite size of other pages especially the critical path. Now this is not a minor detail. If adding features fundamentally slows down your critical path you're forced to choose between keeping your critical path snappy or adding features that your customers want. Our work on embroider has been grounded in this goal to end this terrible choice for every ember app. So every ember app can add new settings pages, admin sections and lesser used features and never have to worry about slowing down the critical path. Goffrey is going to cover the details in a bit but the big picture point is that Polaris brings a big change to how ember apps interact with external packages. In short, Polaris apps and add-ons will use node package resolution to import dependencies. This means that Polaris apps will be built using ecosystem bundlers which means that idiomatic Polaris apps will get features like tree-shaking and code-splitting without having to know anything about tree-shaking or code-splitting. Let me show you what I mean. These days ember apps can import from external npm packages which is great but there's still a problem. We still produce a single vendor.js in a bundle containing all of ember and a single app bundle that contains all of the code in your entire app. This is what makes adding new features anywhere in your app slow down your critical path. What we'd like to be able to do is build a bundle for a specific critical path. Ember apps are already organized by route so our app is already organizing the entry points that we can split apart. In this app, the home page is a critical path. It has a handful of modules in it and we'd like to build a bundle that only contains those modules We're not using these other routes so we don't need to include them. We're also not using these framework files so we don't need to include them either. We can then take this much smaller collection of JavaScript and turn it into a bundle that can be shipped to a browser. The good news is the JavaScript ecosystem has developed a bunch of different tools that know how to solve these problems. Instead of transitioning from our current ember bundler to a new ember bundler, embroiderer is going to use knowledge of ember routes to define our entry points We didn't mention it yet, but embroiderer is the codename of the project that makes all this work. Embroiderer has already shipped an ember but it's not on by default. If you're adventurous, it's pretty easy to try it out. And don't worry, you're not going to have to decide which bundler to use, learn how to configure it, learn how to keep it up to date. After you upgrade to Polaris, Ember S will still work. Ember build that trash release will still work. You'll be able to run your tests locally in a browser and in CI release builds without any new configuration. Polaris will probably use Webpack under the hood but neither you nor your app will need to know that. If you want to use Webpack features in your app, you can. If you want to use Rollup instead, you can. Safe in the knowledge that the add-on ecosystem doesn't depend on any specific bundler. To get the full benefit of Embroiderer and get code splitting at route boundaries in your app, you're going to need to do some additional steps. Mostly, you'll need to ensure that you're not using any deprecated dynamic features like the dynamic component helper. You will not need to migrate away from any idiomatic octane behavior. Since the add-on ecosystem is still working on Embroiderer support, adopting Embroiderer today sometimes means helping add-ons that you use migrate to Embroiderer. So this is what it's like to adopt Embroiderer today. But when Embroiderer is on by default and included in Polaris, the process will be streamlined. For one thing, we will feel confident enough in the compatibility mode to turn it on by default so there is no step one. For another, assertions about incompatible add-ons will be incorporated into the framework. They'll say something like, this add-on is not compatible with route-based code splitting, and they'll be quite rare because finalizing Polaris means that we've achieved broad support in the add-on ecosystem. Which means that this whole list will boil down to eliminate uses of deprecated dynamic features using code mods and Ember standard deprecation workflow. In other words, the standard way of upgrading to a new addition. So in summary, here's the story. The primitive and high-level features have already landed including code splitting and tree shaking. Many add-ons are already compatible, but there are still a number of add-ons that aren't. There's also a long tail of obscure compatibility issues that we will need to address before we can turn embroider on by default. Now, all the weird jargon and finniqueness of the details make it easy to forget what this is all about, but this is a big deal. Soon, every Ember app will be able to add features without slowing down their critical pages, and that won't require a huge rewrite to a different framework or even a stop-the-world incompatible upgrade. You'll upgrade to Polaris, incrementally work through deprecated dynamic features, and boom. Ember apps written before code splitting even existed will have route-based code splitting by default. Ember is amazing. The next big Polaris feature is TypeScript. But let's take a step back for a second. Ember CLI TypeScript is a thing. It gets over 500,000 downloads a week, already supports Ember API really well, has good support for Blueprints, templates, a good versioning story, and most importantly, it exists today. So what am I talking about when I say that Polaris is going to ship TypeScript in Ember? Can we already do that? In fact, it has very little to do with implementing types for Ember. The typed Ember team already did most of the work there. Instead, shipping TypeScript in Polaris means incorporating TypeScript types into our design, code base, and documentation, as well as our semver commitments. To make sure that TypeScript types are a first-class consideration in our design effort, the typed Ember project has become an official Ember core team, with the authority to write and review RFCs, and instead of reacting to Ember designs, we have a proactive role in the design of new features. Because so much of TypeScript in Ember is a matter of policy, I thought we would take a look at how the TypeScript RFCs describe what we're doing. After a truly massive amount of conversation, back and forth in iteration, these RFCs are now approved, and they reflect the consensus position of the project with regard to TypeScript. So I pulled out a few snippets here. First of all, the most important consequence of first-party TypeScript support in Ember is that the types are published and this has a benefit that may not be obvious. You get up-to-date types in beta and canary builds in temporary forks of Ember and in every pull request. This means that Ember's APIs evolve together with its types. Second of all, adding support for TypeScript should not mean that we violate Ember's semver commitments, more on this in a minute. Third, adding support for TypeScript does not mean that we think everyone should use TypeScript. We want JavaScript users to be able to use Ember, and we don't want any part of Ember's API to make sense only to TypeScript users. And fourth, not only do we not want to make TypeScript mandatory, we don't want our adoption of TypeScript to degrade the experience of JavaScript users. This last one is based into how TypeScript itself is designed. Unlike most Type languages, TypeScript will compile and run your code even when there are Type errors. TypeScript types drive the JavaScript language supported in VS Code, even in pure JS files that don't have a TS config, JS config, or any reference to TypeScript anywhere. This means that stuff like document.createElementForm, that AdventListener submit gets to right behavior because the TypeScript team has published types for the DOM and also Node. They have also invested in genuinely groundbreaking features to make all this work. Similarly, when Ember invests in high-quality types for Ember, that improves the experience for JavaScript users. And that will even apply to things like block params and template tags in JavaScript files. If the component you're invoking has types, JavaScript users will get tab completion, hover documentation, and other editor support for that component. Of course, the built-in components in Ember will have types. Our investment in TypeScript is intended to be a win-win. TypeScript users get more integrated support for types in Ember and JavaScript users get the benefits of more integrated types in their tools. Like I said, the TypeScript team built TypeScript so you can build an idiomatic JavaScript API first and layer types on top. That's how things like the DOM work. And that's our plan. But there's a problem lurking here. Every version of TypeScript comes with a list of breaking changes and some of them are pretty significant. We said we didn't want TypeScript to compromise our Sember commitments, so what are we to do? First of all, we need a definition of Sember for TypeScript. We landed on this one. If your code base had no type errors before an upgrade to a new version of TypeScript, it should have no errors after the upgrade. And this should be true for any supported version of TypeScript, even versions that came out after the version of Ember you're upgrading from. This is not an easy problem to say the least. But the typed Ember team has years of experience iterating on a workable policy and they put together an RFC describing the approach we're going to use. One thing that makes me pretty proud of the work we did here is the participation of the TypeScript team in the RFC thread. We'll probably iterate on the solution some more as we get even more experience with it, but I think it's a pretty good place to land. Because we're not the only library-shipping TypeScript types that use a Sember for versioning, Chris Kreitschow adapted the RFC for general-purpose use and put it up on Sember.ts.org. It's still in draft form, but I hope that the wider ecosystem sees the value in this and that we can work together on idioms and tools to make it easier for people to commit to stability around their types. Lastly, I talked about template tag earlier and it goes without saying that Polaris will support types in template. The project to make that happen is codenamed glint and you can try it out today. We've been using it at Tilda for a while now and the Ember TypeScript team has done a great job with it. Definitely check it out if you use TypeScript with Ember. If you want to get the full scoop on TypeScript in Ember, check out the road to TypeScript later today. And finally, there's one last pillar of Polaris. This one is a new area of focus for the whole Ember team and it has to do with the way that Ember is a product framework. Ember apps are products built to run on the web and there are web browsers on desktops and laptops, of course, and web browsers in phones and tablets. But there are also unusual ways of interacting with the web, like on televisions. Edge for the Xbox is a full-fledged browser on a television that you can really use, but it doesn't have a traditional keyboard or mouse. There are web browsers in VR headsets, on watches, and in kiosks. And of course, there are many people interacting with the web through assistive technologies. The reason we build products on the web is because of the amazing reach of the web. When you do a good job building something on the web, it works not only on devices you've thought of and not only in devices you didn't think of, but also on devices that don't exist yet, or versions of operating systems that didn't come out yet. The original Space Jam website, which was discussed in the pre-conference talk Space Jamming Accessibility, was created in 1995. It works well in mobile devices, televisions, and assistive technologies that didn't even exist back then. The same cannot be said of the new Space Jam website. Definitely check out that talk for a deep dive into what the difference is and why the original website is so versatile today. One way to build products on the web is to design first for the computer in front of you, your operating system, and your faculties, and then get a bunch of other devices you might also want to support and then start designing experiences for each of them. I call this case-by-case web design. It's very common, and it's baked into how many people think about responsive design, forms, and accessibility. But a better way to build products on the web is to find universal ways to build things. For example, if you want to respond to click events, you could create a div and add click handlers. To make it work with a keyboard, you could add a tab index and handle focus. To make it work on mobile, you could learn about under what conditions mobile browsers send emulated mouse events. On televisions, you could research how different televisions communicate the action button and handle that, and you could investigate how to communicate that the div is clickable to assistive technologies, or you could use a button. You see, every web browser has a fundamental behavior called the activation behavior, and it happens when someone activates a button according to their operating system. So on a desktop browser, that means clicking a button, of course. While on a mobile browser, it means tapping the button. On a television, that means pressing the center button on your remote. On Xbox for Edge, it's the A button. And for users using assistive technologies, buttons just work. And on all platforms, if there is a gesture to go to the next interactive element, the gesture will bring you to the button, ready to be activated. So you can think of buttons as general purpose labeled actions that you can navigate to, and the click event as the activate event. Oh, and you can style buttons however you want. This may not have been true in the past, but today you can style a button just as effectively as you style the div. This all sounds very abstract, and I'm sure that for many of you, this discussion has made your eyes glaze over. But that's the point. If you have some content in your web app, and you want the user to be able to take an action by activating it, you have a button. Don't add a click handler to your div or image. Turn it into a button, and it will work everywhere. This general principle applies to more than just buttons. If you want a place for the user to enter some text, use an input field and the input event. And it will work on desktops and laptops, of course, but it will also work on mobile devices with virtual keyboards, VR headsets with pointer style keyboards, televisions with keyboards on an attached phone, users using assistive technologies, and who knows, maybe a neural interface someday. If you use a key down or key press event instead, you end up with the thing that happens when banks try to get clever with their events, and you can't use a password manager to enter your password or credit card. I hate that. Even on desktops, there are ways to get text into input fields other than pressing those specific keys. You can hit control or command V. If you want to get text, you can use a password manager or auto-filling extension. There are even more ways to get text into an input field than mobile devices, and of course, there are also assistive technologies. If you want the universal answer to the question, use the input event, and it will work with all those cases, and you can forget all the weird stuff I just told you. All you need to remember is this. Key press is pretty hard coded to specific situations involving a hardware keyboard, so don't use it. One more example. We tend to think we know what colors look and feel like but there's a lot we tend to forget. First of all, almost nobody calibrates their monitors and the differences can be huge. This is why web developers don't calibrate their monitors and print designers do. There's no point in getting super precise colors if your users have wildly different monitors. Then there's Night Mode, which is increasingly popular on mobile phones and desktops. Night Mode radically changes the ratios of red, green, and blue on purpose. And Android now ships standard with a feature that will actually put your phone into black and white mode at night. One in 12 men and one in 200 women has some form of color blindness. And on top of that, different cultures interpret colors differently. I'm not saying don't use color in your applications, but if you want to design your web app for a universal audience, you should make sure it looks good in black and white. There are two awesome talks about color, contrast, and night mode, and I really recommend you check them out to get more information. In summary, when building web applications, it's important for us to try to get outside and disconnect from our own faculties. It's sort of a spiritual thing, I guess, but it's important to remember that the vast majority of our users look nothing like us. It's not that accessibility, televisions, or mobile devices are a special case. It's that each of us is an extremely special case. And building for a more universal sense of our users creates better products for them. Over the past few years, Melanie Sumner has been working to make products built with Ember more universal by design. The accessibility community has done incredible work to define what universal design means, so a lot of the work has been focused on helping Ember apps meet the guidelines established by the accessibility community. The first goal was, make sure new Ember apps satisfy these guidelines out of the box. There was some work to do here, and this effort led directly to a feature that everyone can benefit from, Ember new dash dash interactive. But what happens after you generate the app? How can Ember apps remain as universal as possible? App developers already have a lot to do, and it wouldn't work for us to ask every Ember developer to keep all these rules in their head at all times on top of everything else they already need to do. Instead, we started to provide automated guidance through linting and testing facilities. And I don't just mean the classic image-to-have-and-alt tag that everyone demos, I mean stuff like linting against adding a click handler to a div for the reasons we discussed earlier. It lints against duplicate landmarks, empty headings, headings inside of buttons, and way more. By the way, Chris Manson has a great talk on how to use the lint to the future add-on to adopt linting rules like this in an existing code base incrementally. Making it easy for Ember applications to use universal design principles is a key area of focus of Polaris, and the accessibility working group deserves a ton of credit for their hard work and determination. Some day, I expect to stand here and talk about how idiomatic Ember apps naturally work on every device that has a web browser. If you're excited by that vision, you can help make that happen. If you're interested, please find Melanie. There's lots to do. Whew, that was quite the whirlwind tour. Two recap. Embers is a framework for building long-term products together with others. That means that when we design the future we want to live in, we don't just design the future, we make sure that the present can live side by side with the future and we build a bridge for everyone to cross. Octane was an incredible example of how far we can push that vision. We went from classic classes, classic components, and a classic reactivity system to native classes, glimmer components, and the auto-tracking reactivity system. Not to mention going from app.import to NPM packages. And we made sure that those new features interoperated with the classic versions of those features. In practice, people were able to migrate a little at a time and build new features with Octane idioms right away. And we're following that same path with Polaris. Polaris will include more reactive data structures, template tag, support for ecosystem bundlers, automatic route-based code splitting, built-in TypeScript support, and the foundation of a new core pillar of the Ember framework universal design. That sounds like a lot, but the truth is we've already built a great deal of that future. You can even use much of it today. But to finish the job and ship Polaris, we need to build a bridge from Octane to Polaris. We need to ensure that all of these new world features interoperate so well with the apps you're writing today that you can use them right away in new code and migrate existing code over time. If there's one thing the Ember community knows how to do, it's building the future that we want and making sure that we all end up there. It's not the most glamorous thing in the world, but we sure make some amazing apps and our users love us for it. And with that, I'm going to hand it off to Godfrey who's going to give a more technical update on many of the same themes. Take it away.