Slide 1

Slide 1 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 IDIOMATIC EMBER Finding the Sweet Spot of Performance & Productivity h D O C K YA R D h • hi everyone! • today i want to share some ideas with you about how we can write idiomatic ember apps that are both performant and productive IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 bit.ly/idiomatic-ember • if you can't see the slides clearly, you can look them up on • bitly idiomatic dash ember (repeat it)

Slide 2

Slide 2 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • but first with a heavy heart, let's address the elephant in the room – • what's an idiomatic talk without idioms? IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • they say time is money • so i'm gonna just cut to the chase

Slide 3

Slide 3 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 LAUREN TAN SUGARPIRATE_ POTETO • my name is lauren, and i was born in the year of the rabbit • you can find me on the interwebs as sugarpirate or poteto on github IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • when i'm not working on client work or open source, i sometimes play final fantasy 14 • it's a game that can be quite addictive, but thankfully i have

Slide 4

Slide 4 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ZELDA • a little puppy Zelda who keeps me grounded in reality • unfortunately she couldn't be here today to give this talk, so you'll have to listen to me instead IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I WORK AT DOCKYARD • a year ago i was still living in australia • but i moved to Boston last year to work at DockYard • we work on a ton of Ember and Elixir apps as well very many open source libraries including ember addons • in 2 days it will be the end of my 1st year at dockyard, and i have to say that it's been one of the best jobs i've ever had

Slide 5

Slide 5 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • the best part is, we're hiring senior ember engineers • so please talk to me, brian, marten or estelle later if you're interested in finding out more IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • and if you come speak to me after my talk, i'll give you a very limited edition sugarpirate sticker!

Slide 6

Slide 6 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 THE STATE OF JAVASCRIPT IN 2016 • now before we start talking about ember, let's look at what it's like to build a modern javascript app in the year 2016 IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • react has been steadily gaining ground and mindshare • and for good reasons • some of its ideas have even made their way into ember and other frameworks

Slide 7

Slide 7 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • but despite how far we've come from our early days, modern web dev is getting really hard • i mean, it used to be so simple, right? IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • in the Good Old Days™, all you had to do was write some html, maybe some css, slap some jQuery on a page • and even use a plugin or 5

Slide 8

Slide 8 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • but these days we spend so much time configuring things • and we spend valuable time writing glue code instead of working on what matters IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • you have to make a ton of choices for your front end stack • which can be quite paralyzing

Slide 9

Slide 9 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 leftpad • & its easy to build a tangled web of dependencies • which can lead to all sorts of problems… • IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 “I hand-rolled my own front-end stack using the most organic, gluten-free and artisanal micro- libraries.” • it's tempting to fall victim to javascript hype • and try to build your own makeshift framework • that might end up slowing you down bc you have to make sure everything plays nicely together • why go through that pain?

Slide 10

Slide 10 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • but that's why we use an opinionated framework like ember, right? • with ember, we opt into convention over configuration • problems are shared across the community • bc chances are, your app isn't a special snowflake IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Performance Productivity • these strong standards have given the ember community hands down the best tooling experience available • we have things like ember-cli, the ember inspector, more than 2,000 addons, which is an incredible number • and all of this would not have been possible without the community

Slide 11

Slide 11 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ❤ ❤ • and i think it's fair to say that ember has one of the most welcoming and friendly communities out there • our slack channel is always active, and getting help is easy • there are a few people in there that don't seem to sleep • so if you have a question, there will almost always be someone available to help you out IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 THE EMBER WAY? • because ember is opinionated, it also means there is an "ember" way of doing things • instead of rebuilding the wheel and inventing your own abstractions • we leverage the community's exp in building web apps • with ember 2.0 being released last year in august, quite a few things have changed since then • changes are main motivation for talk

Slide 12

Slide 12 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Data Component Owner Action • for example, there has been lots of discussion around data down, actions up • this is a pattern for one way data flow that we adapted • and is meant to make your apps easier to reason about & maintain • i'll cover this more in detail later IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • we've also established conventions that singleton state should live in services instead of controllers • services are excellent for handling the long lived state of things such as shopping carts, activity feeds and notification messages

Slide 13

Slide 13 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 this.attrs.fooAction(val); this.get('fooAction')(val); VS • actions have been getting a little confusing lately • with the future introduction of glimmer components • these action calls will be subtly different, although they may appear to be the same right now IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • we also know that controllers are going away in the future • i'll talk a little more about what that means and share some techniques you can use to ease the transition to routable components

Slide 14

Slide 14 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • in a nutshell, things have changed • and dealing with change can be hard • these changes can be diff to deal with bc • things that used to be best practices may no longer be true • and lack of updated info about new way of doing things IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 MORE JAVASCRIPT • going forward, we can expect to see Ember become more closely aligned with JavaScript, and for things to become more explicit and less magical • ES2017 features and beyond are going to play a pivotal role in Ember's future

Slide 15

Slide 15 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 DECORATORS Stage 1 https://github.com/wycats/javascript-decorators • for example, you can already use decorators today using rwjblue's excellent addon IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 https://github.com/rwjblue/ember-computed-decorators @computed('first', 'last') name(first, last) { return `${first} ${last}`; } • this is currently a stage 1 proposal, but is incredibly useful for DRYing up your code • the most obvious example is with CPs, as you can see this is much nicer than what we have to do today

Slide 16

Slide 16 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ASYNC/AWAIT Stage 3 https://github.com/tc39/ecmascript-asyncawait • async/await functions are also on the horizon, and are 1 stage away from becoming included into the spec • these let us write asynchronous operations as if they were synchronous IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 await fillIn('.display-title', 'Hello!'); await click('.update-display-title'); assert.equal(find('.display-title').text(), 'Hello!'); https://github.com/emberjs/rfcs/pull/119 • if you ever been trolled by Promises and async programming, you'll love this new feature • for example, in rwjblue's RFC for making tests great again, he describes how we might make use of async/ await to clean up tests • as a result, code & tests are much easier to read and follow

Slide 17

Slide 17 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 GENERATORS ES2015 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* • generator functions are already a part of es2015 • and are really cool • they allow you to write co-operative code – you can pause, resume and restart them as well as end them early IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 countingTask: task(function* () { this.set('count', 0); while (this.get('count') < 5) { this.incrementProperty('count'); yield timeout(300); } this.set('count', 'DONE!'); }).restartable() https://github.com/machty/ember-concurrency • these are really powerful, and you should definitely checkout Machty's addon called ember-concurrency • it allows you to write Tasks to do things you would have a hard time replicating without generator functions

Slide 18

Slide 18 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ❤ • as you can see, when you use Ember, you're betting on JavaScript • we're going to become even more closely aligned with the latest JS features, meaning that you'll be able to write more JavaScript and less Ember IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 – Yehuda Katz “All good ideas will eventually end up in Ember.” • i think it's fair to say that ember is the solution to javascript hype fatigue • and that is why i'm excited to give this talk, bc with ember, we don't have to sacrifice performance for productivity

Slide 19

Slide 19 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 THE EMBER WAY • so in this talk, i want to cast away any doubt you might have about what the ember way really is • and hopefully you'll take away some useful ideas you can use straight away in your apps IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • very broadly, i'll be speaking about patterns and anti- patterns in modern ember apps • first, i want to talk about an important concept

Slide 20

Slide 20 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • data down actions up is one of the core principles driving modern ember apps • by architecting our applications in this manner, we can build apps that are easier to maintain and reason about IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • data down, actions up describes flow of data thru app • eg this is what app might look like once routable components land • fetch data from API, which gets normalized and pushed into ED store • records then get passed in as attrs to routable component

Slide 21

Slide 21 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • you might have noticed controllers were missing from that diagram • and that's because they're going to be deprecated and removed in the future IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Is MVC Dead? ☠ • but why remove controllers if they already work? • well, core team found that a component could easily take the place of a controller + view, as components are superior implementations

Slide 22

Slide 22 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Is MVC Dead? ☠ NOPE • does this mean the death of MVC? • no, but there has been a lot of confusion on this topic, so i'll address it in more detail later IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 2 WAY BINDINGS • what React taught us is that the biggest problem of the frameworks that predate it is the very thing that made them popular • and that is two way bindings • implicit changes are hard to reason about, and • you can very easily cause an infinite cascade of changes if you're not careful

Slide 23

Slide 23 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • when glimmer or angle bracket components land, data bindings will become one way by default • and instead of mutating data, we will have to send actions to update it • this becomes a nice, functional way of building applications that are still reactive • and easier to reason about IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • by ignoring 2 way bindings and instead re-rendering multiple times efficiently, we can have changes propagate immediately without introducing all the cascading semantics of a 2 way bind • and this is made possible with one way data flow and making renders pure and idempotent

Slide 24

Slide 24 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Who owns the data? • with DDAU, a guiding principle is to ask who owns the data • only the owner should be allowed to modify it • this is particularly important when that data is application state, which we should strive to keep as single sources of truth IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Component A Component A.1 Component A.2 Component A.3 Route Owns the shared state of A1 - A3 • for example, here's a diagram of what a simple ember app might look like on a given route • lets say Component A only allowed to show 1 child component at a time • perhaps after doing some config in a child component, we need to programmatically show the next one • where would the best place be to do that?

Slide 25

Slide 25 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Component A Component A.1 Component A.2 Component A.3 Route Owns the shared state of A1 - A3 {{component configComponent user=user changeStep=(action "changeStep")}} • if we follow the DDAU principle, we need to ask ourselves – "Who owns the data?" • In this case, which config component to render is the concern of Component A, it owns the shared state of all its children • so, in order to mutate the current step property, we should send an action up to Component A IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{component configComponent user=user currentStep=currentStep}} // don't do this in the child component! export default Component.extend({ actions: { next() { set(this, 'currentStep', 'configure-foo'); } } }); This is bad ☹ • what we must avoid doing is mutating the "currentStep" in the child component • it's a subtle difference, but it means an app that is easier to reason about and maintain • you won't need to spend hours figuring out who is changing what • because you know there is only one place it is being changed

Slide 26

Slide 26 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 …or someone else's state! • but the action is changing the property anyway, why not just do it directly? • when you change the state that someone else owns, you're stealing • effectively it means that the owner no longer controls the data and you have bypassed the owner's interface • however, in the future these problems will likely go away IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ... • Glimmer Components will be 1 way by default • so even if you tried mutating `currentStep` in the child component, it would not flow up to mutate the currentStep prop of the parent component • unless you explicitly opt-in to doing so with the `mut` helper

Slide 27

Slide 27 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Service.extend({ init() { this._super(...arguments); this.data = []; } }); • another thing to note is that services can also own their own data, • and this is an important part of the Ember programming model • this will be especially useful to know when routable components land, • as singleton state must be moved into a service bc components are stateless IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • with ddau and routable components coming, what do we do with controllers?

Slide 28

Slide 28 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • so, as i mentioned earlier • controllers aren't completely dead yet… IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ROUTABLE CONTROLLER • one important thing to note about controllers is that the concept itself isn't going away • however, the implementation is changing • essentially, the concept of a controller decorating a model still exists, but it is implemented as a routable component instead

Slide 29

Slide 29 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Controller.extend({ queryParams: ['category'], category: null }); • you might still need to use a controller right now if you want to use query params and in certain cases of bubbling actions • but these will be moved to the route eventually when routable components land • that said, don't get too creative in trying to avoid controllers, only remove when it makes sense IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ROUTABLE CONTROLLER • Ember 1.x was a straightforward implementation of MVC, we had a controller and a view • but we've deprecated views in favor of components • and it turns out that what the controller + view was doing could be better handled by a component and service

Slide 30

Slide 30 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ROUTABLE COMPONENT • so the routable component was introduced • the major difference you have to keep in mind is that a routable component is not a singleton like its controller counterpart • but conceptually, they should do the same things, which is decorate a model IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Component = Controller + View • a simpler concept is to think of the routable component as the unification of what we call a controller and a view

Slide 31

Slide 31 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016

Alpaca Route Template

{{#each alpacas as |alpaca|}} I'm a cute little {{alpaca}}! {{/each}}
{{foo-alpaca alpacas=alpacas}} move to • there are things you can do today to make this transition easier • the first key thing you can do is to move your route-level template into a top-level component • when routable components land, it basically just moves that invocation into the route implicitly IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 // shopping-cart top-level component export default Component.extend({ shoppingCart: inject.service(), actions: { remove(product) { // ... } } }); {{#each shoppingCart.products as |product|}}

{{product.title}}

{{product.description}}

Remove {{/each}} • the reason it's a good idea to use a top level component now is that it forces you to separate your stateful, singleton logic out and into a service • a transition to a service for the stateful bits and a component for the stateless bits is most likely to be future proof • and will make the transition an easier one

Slide 32

Slide 32 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Controller Action Action Action Route Action Action Action move to • another thing you can do is to move actions that deal with data to the route • these actions are likely better located in the route as that is where they'll eventually live when routable components land • however, actions that deal with UI or presentational logic should remain on the routable component IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ACTIONS • there are some gotchas with moving controller actions to your route, so i'll share a way to do so a little later • but first, let's take a look at actions

Slide 33

Slide 33 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{foo-bar submit="submit"}} {{foo-bar click=(action "submit")}} • currently, actions can be a little confusing • for example, you could define a classic action on a component by giving it a string property of the action name • or you could use a new style action IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 New-style Actions aka Closure Actions • as a best practice, you should always prefer to use a new-style or closure action

Slide 34

Slide 34 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{foo-bar click=(action "submit")}} • a new-style action is essentially just a regular javascript function • which means you can do things like make use of return values, partially apply arguments and more IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Classic Actions aka String Actions • while classic or string actions are very implicit in nature

Slide 35

Slide 35 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{foo-bar submit="submit"}} • and make things hard to debug • for example, it would be easy to mistake submit for a string property • and it would be difficult to trace where the action actually lives IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 this.sendAction('someMysteriousAction', args); • another thing is when you use classic actions you have to use `sendAction` in your components • which is kinda like sending your action to space

Slide 36

Slide 36 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Your classic action • because you send it up somewhere and hope something is listening to it • who knows where it's going • and the worst part is it never comes back with a return value IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 get(this, 'submit')(...args); • on the other hand, new-style actions are awesome • they're one of my favorite additions to ember and they've been available since 1.13

Slide 37

Slide 37 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • don't let the word new-style or closure action scare you • it's really just a regular javascript function IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 actions: { saveAndDoStuff(item) { get(this, 'save')(item) .then((savedItem) => // do stuff); } } • as i said earlier, you have return values • these are especially useful when dealing with async operations like saving a record • you can return the save promise and then handle it from inside the component • e.g. handle success and failure state

Slide 38

Slide 38 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ember install ember-route-action-helper https://github.com/DockYard/ember-route-action-helper • so new-style actions are awesome, but out of the box you can only use them if they're defined on the controller as they do not bubble like classic actions • but if you want to move your actions that deal with data to the route this seems rather lame • so rwjblue and i built a little addon you can use today • it's called the ember-route-action-helper IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{foo-bar value=value updateFoo=(route-action "updateFoo") }} • by using `route-action` in place of `action` • this lets you have new-style actions that work with routes, and when the time comes, you can search and replace route-action back to action

Slide 39

Slide 39 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 SERVICES • if you recall, controllers without routes can be created, and it was a common practice to manage singleton state with them • a service is a long lived singleton we can use to manage long lived state IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • a great example for a service is a shopping cart or activity feed • for things that have singleton state, you can extract that logic into a service and then inject it into the objects you need to

Slide 40

Slide 40 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Service.extend({ init() { this._super(...arguments); this.cart = []; }, addToCart(item) { // ... }, removeFromCart(item) { // ... } }); • a shopping cart is an excellent example of a service, because there can only be one singleton cart in the application, and it needs to have long lived state across the app • you can implement a cart with as little as setting a property on a service to be an array • or you could do more IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • what we must be careful about is not to abuse services • if your main reason for creating a service is to use it as a global bucket to avoid passing things around, that's a bad sign • instead, adhere to data down actions up and don't use services for something that doesn't need long lived singleton state

Slide 41

Slide 41 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • now i want to talk about templating, something i'm sure is close to all of our hearts IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{if (eq fullName "Jim Bob") "You're the chosen one"}} https://github.com/jmurphyau/ember-truth-helpers • another of my fav new things in Ember is the new Helper impl • introduced in Ember 1.13 and great for expressing presentation logic • for example, this `eq` helper can be found in ember- truth-helpers addon, which we pretty much use for all our apps

Slide 42

Slide 42 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{concat "configure-" configName}} http://emberjs.com/api/classes/Ember.Templates.helpers.html • Ember itself also ships with a bunch of useful Helpers like `concat`, `hash` and the`get` helper • the best thing about Helpers is that it basically lets you power up your templates IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 import Ember from 'ember'; export function add([a, b]) { return a + b; } export default Ember.Helper.helper(add); • again, because Ember is converging towards just being JavaScript • a helper is really just a regular JavaScript function • one of the reasons i like helpers is that it forces you to keep your functions small and free of side effects • you can basically create 2 kinds of helpers, this one is a simple one that will recompute every time the params change

Slide 43

Slide 43 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Helper.extend({ // ... }); • for more complex needs, you can also make a class based helper, which is essentially an ember object IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Helper.extend({ localesService: inject.service('locales'), currentLocale: readOnly('localesService.currentLocale'), compute([key]) { let currentLocale = get(this, 'currentLocale'); return get(this, 'localesService').lookup(currentLocale, key); }, localeDidChange: observer('currentLocale', function() { this.recompute(); }) }); • this means you can do things like define CPs, use services and so on • for example this is a helper that looks up translations on a locales service • specifically you might want to listen for a change in locale • and then all the translations should auto-update, even though the locale itself is not passed into the helper

Slide 44

Slide 44 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • some of you might have noticed i used an observer in my previous example • class based helpers are currently the only place it is ok to use one • bc it is still a lightweight implementation (almost mvp), and its lifecycle hooks are not implemented yet • when those become available, we can use them to recompute instead IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Component.extend({ isDropdownDisplayed: false, actions: { saveUserAndHideDropdown(user) { get(this, 'save')(user) .then((user) => { // do stuff set(this, 'isDropdownDisplayed', false); }); } } }); • another reason i like helpers is because it means a lot of UI logic can be moved back to where it belongs • for example, how many times have you written something like this? • this feels a little dirty because it mixes data and UI logic together

Slide 45

Slide 45 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Component.extend({ isDropdownDisplayed: false, actions: { hideDropdown() { set(this, 'isDropdownDisplayed', false); }, saveUser(user) { return get(this, 'save')(user); } } }); • so one thing you could do is to split them up into 2 actions right? • but now you have a problem, because you can't invoke them both • what i'm about to show you demonstrates how powerful helpers can be IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Save and Close • let's say we had a hypothetical helper called `pipe` • it would let you take the return value from 1 action, and pass it along to the next one • and it would keep passing that value down to each function in the pipe

Slide 46

Slide 46 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 A(B(C(D('E'), 'F'), 'G'), 'H'); • if you've used the programming language elixir before, this pipe helper is essentially the same as the pipe operator • the pipe operator lets you express the above, which isn't very nice to read, as IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 filing = DB.find_customers |> Orders.for_customers |> sales_tax(2016) |> prepare_filing E |> D |> C("F") |> B("G") |> A("H") http://elixir-lang.org/ • a series of data transforms • this is a lot easier to read compared to the 1st example • since we use Elixir a lot at DockYard, it was a language feature we wanted to use in ember

Slide 47

Slide 47 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 1-Click Buy • so the pipe helper was born to let you compose small, pure actions to allow more declarative templating • in this e.g. the item argument at the end gets passed into 1st action, then its return value piped into the rest • as you can see, helpers can be really powerful tools to extend templating in your app IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 ember install ember-composable-helpers https://github.com/DockYard/ember-composable-helpers • together with my colleague Marten Schilstra, we've built an addon called ember-composable-helpers • which contains the pipe helper and other declarative helpers

Slide 48

Slide 48 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Save and Close Save and Quit • the best part about helpers is composability • helpers can do things that might be more complex to express in your component • using the pipe helper example again, you don't need to define 2 actions called `saveAndClose` and `saveAndQuit`, you can just compose them directly IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{#if (eq (not (incr (count user))) (decr (count user))))}} • that said, you shouldn't try to get too ambitious with nesting helpers inside of each other • and when your template looks like this, you're better off creating a CP instead

Slide 49

Slide 49 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • it can be easy to get carried away with using helpers • how much logic you want to have in your templates depends on your comfort level • so use them with caution and exercise best judgement IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • the point is, helpers are better suited for dealing with UI / presentational logic • which quite often is highly abstract and unrelated to the exact data in question • in contrast to CPs which are coupled to data

Slide 50

Slide 50 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • which brings us to our next question • when we do need to perform computation, what is the best way? IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 COMPUTED VS HELPER VS COMPONENT HOOKS • generally speaking, we have 3 ways of doing so • you could express it as a CP, use a helper in the template, or use a component hook to set a value • each approach has its pros and cons, lets look at when we should prefer to use one over the other

Slide 51

Slide 51 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 COMPUTEDS • CPs are one of the first things you learn in Ember • they can range from simple to powerful, and are a nice way of keeping some computation up to date • but these auto updates can be double edged sword • bc observing changes are implicit, so when a CP recomputes, don't always know why it did IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • the best analogy for a CP is an excel spreadsheet • it would be painful to sum a1 and b1 by placing event listeners on the cells and then getting their DOM values • a CP lets you declaratively express logic that changes automatically when the dependents change

Slide 52

Slide 52 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Component.extend({ @computed('payment', 'rate', 'periods') annuity(payment, rate, periods) { let factor = ((1 - Math.pow(1 + rate, -periods)) / rate); return payment * factor; } }); • i would recommend using CPs for expressing business logic • one of the best things about CPs is that they can become reusable macros IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export function customMacro(dependentKey, ...keys) { return computed(dependentKey, ...keys, { get() { // computed property logic } }); } • you can extract a CP into a macro by creating a function that returns a CP • you can then pass in any arbitrary key or value into the macro,

Slide 53

Slide 53 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Component.extend({ myValue: customMacro('myKey', 'foo', 'bar'), someValue: customMacro('someKey', 'baz', 'qux'), otherValue: customMacro('otherKey', 'meow', 'woof') }); • and then you can import and use it like you would any other macro • this is a great way to DRY up your code and test your business logic IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • another awesome thing about CPs is that they are cached • meaning expensive work is only done when it needs to be done

Slide 54

Slide 54 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Business logic Reusable ⚛⚛⚛⚛ React to changes Changes can be implicit Totally arbitrary emojis • so basically CPs are great for business logic and have potential for reuse in other ember objects • they update when dependents change, but you can 'subscribe' to these explicitly by specifying keys • however it can be sometimes difficult to trace why a CP recomputes IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 HELPERS • helpers can be simple functions or class based • they don't have a DOM element and are simple to debug – you know where the data is coming from, and what comes out of it • just like CPs, we should strive to keep these pure and free of side effects

Slide 55

Slide 55 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{#each (repeat 3) as |nil index|}}
{{!some HTML block}}
{{/each}} https://github.com/DockYard/ember-composable-helpers#repeat • helpers are best suited for generic utility functions, and are especially useful for UI logic • for example, this little helper repeats the block inside of it, 3 times • it's non-business logic related but helps us DRY up our templates, so you can use this nice markup instead of copy pasting a block of HTML 3 times IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{capitalize "hello"}} {{capitalize "hello"}} • another thing to note about helpers is that ember doesn't guarantee that multiple uses of the helper will actually run at any given point • for example, this capitalize helper will only run once, even though it's used 2x • this is a good thing as it means ember can optimize rendering • but that means helpers must be carefully written to not

Slide 56

Slide 56 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export function toggle([obj, prop]) { return function() { set(obj, prop, !get(obj, prop)); }; } https://github.com/DockYard/ember-composable-helpers#toggle • you may or may not be surprised to note that helpers can be used as an action if it returns a function • again, this is really useful for expressing UI logic, we might use this toggle helper to toggle a dropdown or a popover IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 {{if isExpanded "I am expanded" "I am not"}} • you can then use your action helper like this, • which is much nicer than defining yet another `toggleIsExpanded` action in your components

Slide 57

Slide 57 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Express UI logic ✍❤ Composable Simple mental model Can be used as action No hooks available (yet) Totally arbitrary emojis • so helpers are great for UI and presentational logic • & inherently composable with sub-expr or nested helpers • they're easy to understand and can also be used as actions • & bc they're still lightweight implementations, we sometimes have to use observers to recompute IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 COMPONENT HOOKS • component hooks are also a nice explicit way of handling computation • for example, when props change from the outside, we can handle it easily • however, they can be quite tricky to use, so you should prefer a CP unless you know why you need to use a lifecycle hook

Slide 58

Slide 58 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Component.extend({ didReceiveAttrs() { this.updateChart(get(this, 'data')); }, updateChart(data) { // update the chart's data } }); • lifecycle hooks are best used when you need side effects, and should be preferred over an observer • for example, you might have a component that wraps a chart library which has some kind of render method • you can use a hook like `didReceiveAttrs` to update the chart whenever new data flows into the component IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Initial Render init didReceiveAttrs willRender didInsertElement didRender External attr changed didUpdateAttrs didReceiveAttrs willUpdate willRender didUpdate didRender Internal value changed willUpdate willRender didUpdate didRender • this is a simple diagram showing the order of hooks that fire when components render or re-render • when using these hooks, its important to think about whether the changes are 'idempotent' • in other words, these hooks should work no matter how many times they are called • as you cannot easily control the order and timing of the hook

Slide 59

Slide 59 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 export default Component.extend({ didRender() { // this is an infinite loop that will crash your browser let isFoo = get(this, 'isFoo'); set(this, 'isFoo', !isFoo); } }); • for example, this might look like fairly innocent • you might want to toggle some property after a component has rendered • but because it has side effects, it will cause a re-render, which fires the `didRender` hook again, which re- renders, and so on until your browser explodes IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Dealing with side effects ➿ Can cause infinite loops "Just re-render it" Not invoked in FastBoot environment Totally arbitrary ratings • in a nutshell, lifecycle hooks are useful for replacing observers and for controlling side effects • however, they can also be potential footguns, as you can easily cause an infinite loop or sync issues if your logic is not idempotent • so use them with caution

Slide 60

Slide 60 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 SHOULD I USE AN OBSERVER? • and finally the age old question, should i use an observer??? • this topic has been covered quite in detail already, IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • but basically, you almost never want to use an observer • they're low level primitives used by ember so you don't have to • as i mentioned earlier, the only place it is ok to use one is in a class based helper bc no lifecycle hooks are implemented yet

Slide 61

Slide 61 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 https://youtu.be/vvZEddrClAQ • you should remember that every time you use an observer, Stefan Penner dies a little inside • so don't do it • and watch his Wicked Good Ember talk on observers IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 THE FUTURE • i want to close off the talk by briefly mentioning what we can learn from game renderers • and what the future might hold for how we build web apps

Slide 62

Slide 62 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • game renderers are surprisingly similar to web applications • this diagram shows the rendering architecture for Doom 3 • there is an idea of a "front end" and a "back end" as well as an Intermediate Representation (IR) that sits between IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Game world state Front End IR Back End DirectX / OpenGL OS Screen • the front end captures the state of the world and determines what contributes to the view • this is then expressed as an IR, which is then passed into the backend where it goes through a hardware abstraction layer like DirectX or OpenGL before reaching your GPU and then your screen

Slide 63

Slide 63 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Application state Front End HTMLBars Glimmer DOM APIs Browser Screen • an ember app is similar • we capture the state of the app and determine what to render • our templates go through HTMLBars and Glimmer, and then to DOM APIs • this is then taken by your browser and rendered onto your screen IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • so if games and web apps are so similar • how can games that are so much more complex and graphically rich sometimes run faster than a web app?

Slide 64

Slide 64 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Hidden blocks rendered, CPU time wasted Line of Sight Camera Without occlusion culling • the answer is that games are heavily optimized to make use of your GPU IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Hidden blocks not rendered, CPU time saved Line of Sight Camera With occlusion culling • & use a bunch of tricks like occlusion culling to optimize rendering performance • i'm really excited to see the trailblazing work Chris Thoburn (@runspired) is doing in this area, you should follow him for updates

Slide 65

Slide 65 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • in fact, Mozilla is actually working on an experimental web renderer for Servo, that aims to draw web content like a modern game engine • early prototypes are able to render at hundreds of FPS IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 https://air.mozilla.org/bay-area-rust-meetup-february-2016/#@1m53s • i won't go into further detail, but you can check out the video for more info • i think web apps have a lot to learn from game rendering, and i'm excited to see us moving in that direction to optimize performance

Slide 66

Slide 66 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 TL;DR • to summarize IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • we looked at the state of javascript and we saw that ember is going to closely align itself to new features from ES2017 and beyond

Slide 67

Slide 67 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • we then looked at what DDAU means and how to implement it by keeping in mind who the owner of the data is • and to avoid mutating data directly IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • then, we cleared the air on controllers • routable components and services are superior impl • & will still play the part of a controller, which is to decorate a model • we also looked at using ember-route-action-helper for using route actions in templates

Slide 68

Slide 68 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 I. Data Down, Actions Up? II. Controllers are Dead III.Declarative & Composable Templates • and finally, we looked at ways to power up your handlebars templates • helpers are a great way of performing UI logic in a composable and easily testable way • you can write your own or use ember-composable- helpers to make your templates more declarative IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • i want to thank dockyard for allowing me to give this talk and being an awesome place to work at

Slide 69

Slide 69 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 • we're organizing the WickedGoodEmber conference this june, so please check out our website for more info IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 LAUREN TAN SUGARPIRATE_ POTETO • once again, please get in touch if you have any questions

Slide 70

Slide 70 text

IDIOMATIC EMBER – FINDING THE SWEET SPOT OF PERFORMANCE & PRODUCTIVITY EMBERCONF 2016 Thanks! LAUREN TAN SUGARPIRATE_ POTETO • remember to speak to me if you're interested in working AT or WITH DockYard • and i'll give you a sticker too • thank you all so much for listening!