Here is a highlighted overview of the major points from some of my favorite EmberConf 2020 talks:
DAY 1
Opening Keynote - Yehuda Katz, Jen Weber, Godfrey Chan
- First ever all-virtual EmberConf 💻 ⚡ 🎉
- There’s more to software than the number of lines of code you write
- Value sustainability
- Innovating on Community 🤝 👪
-
Ember 1.13 had a large number of deprecations
- Technically followed SemVer, but created lots of chaos…😰
- SemVer doesn’t tell the whole story of ecosystem stability
-
RFC Process (Started 2014 📅)
- Driven by the community
- Aid the core team with many perspectives
- Collect constraints
- Build relationships
- Provide structure for implementation
- Record history and rationale
- RFC: Slack → Discord 🌟
-
RFC: Website Redesign ✨
- But where’s the Tomster? - Drives conversation and solutions
- Core teams → Diverse leadership
- Together™
- 2017: Ember framework began to feel like it was falling behind
-
Surviving the Framework Hype Cycle by Brandon Hays 📺
- Settlers and Pioneers
- Shipping features as low-level primitives and let the pioneers finalize the APIs
- Stable 🧱
- Give pioneers the tools and leave the settlers alone
-
visit()
API → Fastboot - Component Manager →
@glimmer/component
- Modifier Manager →
ember-modifier
- But when do settlers adopt?
- 2019: Ember Editions
- Collection of features
- …and the settlers keep on working
-
Ember Octane
- Fresh look for a new user 🎊
-
HTML-First Framework
- Core of Ember is HTML and CSS
- No special Ember-specific tweaks…Just copy paste
- Components can simply be a natural extension of HTML → Driven by HTML and Templates
- Template only components (Improved performance, no
this
in template) 🏎️
- Template only components (Improved performance, no
-
Integrating JavaScript
- Old: Managing DOM 😭 👎 → New: Managing Data 😆 👍
- JS file co-located next to your existing Template
- Native JS syntax and idioms
this.args
-
get shareURL() { //... }
→{{this.shareURL}}
(JustWorks™ in the template)-
Autotracking: Auto-updates the getter if any passed-in
this.args
properties used inshareURL
change-
@tracked
properties - Even works on Native JS classes
-
-
Autotracking: Auto-updates the getter if any passed-in
-
Working with the DOM
<button {{on "click" this.toggleSize}}></button> {{#if this.isLarge}} Large code... {{else}} Small code... {{/if}}
import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; export default class MyComponent extends Component { @tracked isLarge = true; @action toggleSize() { this.isLarge = !this.isLarge; } }
- Powerful, intuitive
<input {{autofocus}} />
export default class AutofocusModifier extends Modifier { didInstall() { this.element.focus(); } }
-
HTML-First Framework
- Syntax and mental model overhaul
- Shipped Dec 2019
- Don’t have to rewrite apps to begin using features
- Linting out of the box
- Updated defaults when generating apps
- Zero config builds
- Refreshed guides
- Create a new app →
ember new
- Paste in HTML and CSS →
application.hbs
- Fast deploy → Netlify
- Incrementalism
- Backwards compatibility
- Interoperability between new and old paradigms
- Codemods
-
Ember 2019-2020 Roadmap RFC
- Invest in Octane
- Modernize build system
- Better accessibility by default
- Share Octane outside our community
- Building Ember Together 🤝
- ember-inspector - Octane certified 🥇
- Ember Twiddle - Octane certified 🥇
- Fresh look for a new user 🎊
FastFlood: The Story of a Massive Memory Leak in FastBoot Land - Sergio Arbeo
- Fastboot: Render Ember apps on the server
- Memory leak: piece of data which should have been garbage collected ♻️
- But Why❓
- A reference is being kept somewhere
- Chrome Developer tools 🛠️
- Object memory graph - All objects in memory 📊
- Distance - Distance from GC root
- Shallow size - Size of the object itself
- Retained size - Size we would free if we freed the object reference
- Heap profile - Capture memory state at a point in time
- Three snapshot technique to find the problem areas
- Warmup the application - Create baseline objects
-
First snapshot
- Now, do the suspected memory leak action
-
Second snapshot
- Now, repeat the action
- Third snapshot
- Three snapshot technique to find the problem areas
- Object memory graph - All objects in memory 📊
- Timeline tool
- Step 0: Reproduce locally on a production-ish build (no uglification)
- Step 1: Find the leak 🔎
- Run the server with debugging enabled
- Make one request
- Start timeline
- Make a few requests
- Inspect
class LeakDetect { constructor() { Object.assign(this, attrs); } }
- Step 2: Find the dominator (retainer we need to remove so the leak is gone) 🔎
-
Container
: used to instantiate and cache objects, associated with aRegistry
- Look for Owner leaking (public API of the
Container
) - Problem:
export default Adapter.extend({ headers: computed(function() { return { Authorization: `Bearer ${something.here}`; } }) })
- Fix:
export default Adapter.extend({ get headers() { return { Authorization: `Bearer ${something.here}`; } } })
Octane: A Paradigm shift in EmberJS - Suchita Doshi
- The Journey of Ember 🏃♀️
- Ember 1.x
- Convention over configuration
- Built-in routing
- Ember Data
- View driven architecture
- Two way bindings 🔁
- Attribute bindings
- Ember 2.x
- Component driven architecture
- Glimmer rendering engine adoption
- Better binding with properties
- Improved template scoping
- “Data Down, Actions Up” approach
- Roadmap for further improvements
- Ember 3.x
- Road to Octane
- Cleanup
- Remove IE9, IE10, PhantomJS and Bower
- Native Classes
- Angle Brackets
- Lots of documentation
- Ember 1.x
- Evolution of Ember
- Edition: Shift in programming model due to new framework features ✨
-
Native Classes
- Increased performance
- Smooth learning curve
- More aligned with JS community
-
Glimmer Components
- Simple, Ergonomic, Declarative 💪
- Fewer lifecycle hooks and properties to learn
- Removed the implicit component wrapper
- Namespaced arguments
-
Templating in Octane
{{!-- Old --}} {{employee-details name=employeeName empId=employeeId addEmployee=(action "addEmployee")}} {{!-- New --}} <EmployeeDetails @name={{this.employeeName}} @empId={{@employeeId}} @addEmployee={{this.addEmployee}} />
- Tracked Properties
- No more
this.set()
- Reduce complexity
- No more
- Modifiers and Decorators
ember-codemods
- Ember Atlas
AST: the Secret Weapon to Transform a Codebase - Sophia Wang
- How do I bulk edit syntax at scale?
- Example: Wrap all requests with a global error handler
- But we have a problem… Hundreds of requests scattered around the codebase
- Update manually? 🤔😢 Tedious and time consuming
- Update with RegEx? 🤔😢 Too inefficient for so many unique changes
- Update with Codemods? 🤔😆
- Understanding the grammar behind a language
- Apple === Noun
-
Tools 🛠️
-
ESLint
- Disallow unreachable code
function foo() { let retVal = 1; return retVale; returnValue = 2; // <-- Unreachable code detected ts(7027) }
- Disallow unreachable code
- Codemod: Bulk edit syntax
-
Babel Transpiler: Write modern JS → Transpile → Run in any browser
// Convert this [1, 2, 3].map((n) => n + 1); // To this [1, 2, 3].map(function(n) { return n + 1; });
-
ESLint
-
What is an Abstract Syntax Tree (AST)? 🌳
- A Tree structure highlights the most important syntax of a language
- Tree: Nodes connected by Edges
- Heap Tree
- Parse Tree
- AST: Best parts of a Parse Tree (Structure) but only highlights the most important information
- Remove extraneous information, Simpler
-
How does syntax transformation work?
- Parsing
- Lexical analysis (Scanning/Tokenization)
- Syntax analysis - AST Explorer (Includes Glimmer parser ✨)
- Transformation
- Code Generation
- Parsing
Taming the Beast: Managing a really ambitious codebase - Luke Deniston
- But this time I will build things the right way…
- The Beast === Complexity
- When we write code, we’re telling a story
- Audience: Developers, Computers
- Strategies for dealing with complexity
- Abstracting
- Subdividing (Systems → Subsystems)
- Codebase v1: Coffeescript, Rails, Ember 😑
- Regressions with each upgrade
- Performance degradation
- Codebase v2: Shared addon and micro front ends 😑
- Solved some issues but full page refreshes when navigating from app to app
- In-repo engines? Repo-mageddon
- Fix bug in shared addon → 5-6 different cascading pull requests
- Codebase v3: Mono repo - yarn workspaces with Ember Engines 🥳
- Workspaces: 1 repo, many packages
- Packages can import each other
{ "private": true, "workspaces": ["packages/*"] }
- Pro tip: Make individual package folder with the same name as the
package.json
name field
- Packages can import each other
- Engines: Mini application, built and booted by the host application
- Optional lazy loading
- Code isolation
- Routable engine
Router.map(function() { this.mount('super-blog'); })
-
Routeless engine
{{mount "super-blog"}}
- Tests: Use the correct resolver
ember-engines/test-support/engine-resolver-for
- Sharing services
- Workspaces: 1 repo, many packages
- Controversial Tips⁉️
-
Replace this with your real tests
→ Treat this as a bug! - Use the last argument in tests
.assert()
to describe the test - Use Prettier and ESLint
- Be careful with Addons (Actively maintained? File size bloat?)
- Use TypeScript
- Use Tailwind
-
Lessons Learned from Changing Careers - Kara Luton
- Your past experiences make you the developer you are today 🥇
- A non-traditional background is a feature, not a bug
- What did I want in a new career?
- Codecademy
- Get a CS degree, self teach or go to a bootcamp?
- The Iron Yard bootcamp
- Overwhelming amount of information about HTML, CSS, and JS
- Job rejections → Judged for not having experience 😩
- Lessons learned along the way (Ballerina → Music Publicist → Developer) 👩🎓
- Practice doesn’t make perfect, it makes progress 📈⬆️
- Pay attention to the small details
- Accepting feedback and not taking it personally
- My writing skills → Code commenting! 💪
- How to work well with others
- How to stand out amongst the crowd
- Interviewing skills
- Community is important
- 37.6% of developer who have a college degree did NOT major in Computer Science
- Look back on your own past experiences
Ember as Song - James C. Davis
- How best to compare building an app with composing a song?
- Programming and Songwriting are very similar 🎶 ⌨️
- Song: composed of sections
- Section: composed of instruments, notes, parts, measures, phrases
- Section → Route
- Instrument → Service
- Part → Component
- Notes → Contextual Components
- Other globals: tempo, master volume, playing state, where are we in the timeline
- Web Audio API: Powerful but low level. Provides primitives for making sounds
- Tone.js: Built on top of the Web Audio API. Provides primitives for making music
- Scale: string of notes, one after another
Democratizing Frameworks Through Developer Experience - Sarah Yu
- Reach: Engineering apprenticeship program at LinkedIn
-
ember-m3
: alternative model implementation toDS.model
- What is developer experience (DX)?
- Things that make developers’ lives easier
- DX is often not prioritized 😞
- Involves people 👩🦰👨🦰
- Metrics can be hard to measure 📝
- Much easier to just say you reduced the build size by
n
Bytes
- It’s a bug it that happens -Yehuda Katz
- When should you contribute to DX?
- Beginner: Getting started guides
- Intermediate: Advanced APIs
- Advanced: Self-documenting source code
- Where can you contribute?
README.md
- Ember Learning
- Debugging tools
- Source code comments
Why JS is Coming to Ember Templates - Matthew Beale
- “No more unification RFCs” 😨
-
<Welcome />
could live anywhere since resolvers are a runtime concern - Local maximum → Global maximum
- Matt’s resolving Ember Microlib
const template = [ [0, 'To you I say:'], [1, 'welcome'] ] setup(); render(template);
- rollup.js
- terser
- Dynamic resolution vs. static linking
import { welcome } from './components' const template = [ [0, 'To you I say:'], [1, welcome] ] render(template);
- How can we bring the benefits of a statically linked system into Ember ❓
- Handlebars Strict Mode 💪
- What are the constraints in strict mode?
- No implicit
this
fallback - No resolution.
{{foo-bar}}
is only ever a variable - No dynamic resolution.
{{component foo}}
- No partials
- No implicit
- Need a static solution for getting other components into template scope.
createTemplateFactory({ "scope": () => [OtherComponent] })
- Template imports
- You can import a default of named export into your template
- The right side of an import, the path, works just like it does in any system using Node.js resolution.
-
Adopt ES Modules syntax module syntax into templates
--- import Quote from './quote'; import { titleize} from '@ember/template-helpers'; import { animatedEach } from 'ember-animated/helpers'; --- {{#animatedEach @greetings as |myGreeting|}} To {{titleize this.subjectName}} I say: <Quote>{{myGreeting}}</Quote> {{/animatedEach}}
- Next steps:
- Get Handlebars Strict Mode into Final RFC and land the primitives
- Build an addon for template imports so we can experiment
- Start a design for what the ES module API is for built-ins like
<LinkTo>
or<Input>
- Performance + Payload size benefits
- Explicit templates
- Strict mode for templates RFC
Solving Problems for African Soil - Ridhwana Khan
- Kasi Maths
- Why should you care?
- Outline of challenges
- Power/Electricity: Loadshedding and power outages (Rolling blackouts) 🔌
- Plant breakdowns 🏭
- Poor cellphone reception
- Basic digital literacy 💻
- Reduced access to computers in public school and at home
- High data costs 💰
- Low end smartphones
- Bandwidth speed
- Power/Electricity: Loadshedding and power outages (Rolling blackouts) 🔌
- First Paint (Is it happening?) → First Contentful Paint → First Meaningful Paint (Is it useful?) → Time to Interactive (Is it usable?)
- How can I help?
-
Reduce Bundle Size
- Minifying & concatenating JS
- Code splitting
- Tree shaking
- Measure
-
Server/Static Rendering
- Fastboot and Prember
-
Service Workers
- Caching and offline
- ember-service-worker
- ember-pouch
-
Other Performance Tips
- Optimizing images
- Optimizing the JSON
- Adaptive loading
-
Reduce Bundle Size
Shift Left - Melanie Sumner
- Where did you start: Hobbyist? Computer Scientist degree? Coding Bootcamp? Self-taught?
- Change is the constant we can rely on 👈
- Continuous learning is a must
- Assistive technology is not included is most curriculums
- Find your niche and grow
- Think beyond
- Enjoyable UX
- No vision
- Keyboard-only
- Low vision
- Accessibility settings
- High contrast mode
- What does a typical pattern look like? 🧩
- Basic examples
- Variations
- Rendered code
- Copy & paste
- Too many DOM nodes can crash screen readers
- Patterns should also include anti-patterns
- ember-template-lint
- ember-a11y-testing
- It’s ok to not know everything. Lean on tooling
- Shift Left mindset: Intent matters
- Accessible interfaces can used the way they were intended to be used
- Don’t wait for the audit, move accessibility into the design workflow
- Elevate the quality of your code
- Technical Accessibility Issues for New Ember Apps RFC
DAY 2
Autotracking: Reactivity and State in Modern Ember - Chris Garrett
- Reactivity: How apps update when things change
- Autotracking is a form of reactivity
- TodoMVC
- How do we let the template know to when to update?
-
this.rerender()
😭 -
this.rerenderItem()
😭
-
- Annotation overhead: All the extra thought that has to go into doing things the way the framework wants you to do them
- Grows combinatorially with the size of our application 😨😨
- jQuery
- Reactivity can solve this by linearly increasing rate instead of exponentially increasing rate
- Declarative programming model for updating based on changes to state
- Declarative: “What, not how” ❗
- HTML (Derived State)
- State: All the things that can change in your app
- Variables
- Properties
- User inputs
- Root State
firstName = 'Liz';
- Derived State
get fullName() { return `${this.firstName} ${this.lastName}`; }
- Observable - RxJs
- Observable → Events → Transform → Subscriber (Push-based)
- Similar to classic Ember -
set()
,pushObject()
allTodos = Observable.of([ { title: 'Code', completed: false }, { title: 'Plan EmberConf talk', completed: true } ]);
- Performance++ / Ergonomics–
- Virtual DOM - React
- “Where did the state change occur?” (Pull-based)
- Tree: Dirty → Rerender
- Class based components
state = { allTodos: [ { title: 'Code', completed: false }, { title: 'Plan EmberConf talk', completed: true } ] } this.setState({ allTodos }); // Specifically communicates "What" to the framework
- Hooks
let [allTodos, setTodos] = useState({ allTodos: [ { title: 'Code', completed: false }, { title: 'Plan EmberConf talk', completed: true } ] }); this.setState({ allTodos }); // Specifically communicates "What" to the framework
- Performance–
-
useMemo
/shouldComponentUpdate
(manual optimization) → Scalability ⬇️ → Complexity ⬆️
- Can we bend the curve? Performance++ and Annotation overhead–
- Autotracking: Use the JS state model (Pull-based)
- Focus on root state: Push to array, Add to object
allTodos = new TrackedArray([ { title: 'Code', completed: false }, { title: 'Plan EmberConf talk', completed: true } ]); @tracked displaying = 'all';
- Performance++ / Ergonomics++
- Root State → Output
- Memoization: Return a previously computed value if nothing changed
memoizedRender(...args) { if (deepEqual(lastArgs, args)) { return lastResult; } lastResult = render(...args); lastArgs = args; return lastResult; }
- Clock: state v0 → state v1 … → … state v15156;
- Did the clock increment? Update
- Tag:
myClass.trackedProp
↔ state v15156 (last known clock when this was changed) - Simple / Performant
- Dirty: Increment a number
- Validate: Array.map + Math.max
- Lazy (only checked on demand)
- Only need to annotate root state
- This is what bending the curve looks like
- What’s next? 🔮
- Libraries, patterns, common abstractions - tracked-built-ins
- Tooling - Ember Inspector state timeline
- Extract to be used anywhere
- Focus on root state: Push to array, Add to object
An Octane-Powered JAM Stack - Chris Manson
- JAM? JavaScript, APIs, Markup
- Fast sites delivered by prerendering files and serving from CDN (Serverless)
- Created by Netlify
- Evolution of some of the things we were already doing
- JAM Spectrum 🍓
- Server-side Rendering 🏎️
- Pre-rendering: Prember
- JAM Lifecycle: Serve HTML → Load JS
- Gatsby - $15M Funding 💰
- VuePress
-
Empress
- Stable 🧱
- empress-blog - Shallow fork of Casper theme from Ghost
- Guidemaker - Powers EmberJS guides
- Field Guide - Ember website redesign
- Beta
- Alpha
- Stable 🧱
-
empress-blog
Architecture- Host App (Ember) ↔ empress-blog (Broccoli) + empress-blog-template (Handlebars/CSS)
npm uninstall empress-blog-casper-template npm install empress-blog-attila-template
- Host App (Ember) ↔ empress-blog (Broccoli) + empress-blog-template (Handlebars/CSS)
A11y First and Everyone Wins - Ava Wroten
- Adding Functionality with Composable Components
- Equitable & Discoverable UX
- Automation Testing
- 15% of the world lives with some form on disability 🌍
- Degrees of disabilities 🐕🦺
- Limited mobility, Muscle slowness, Tremors, Low vision, Color blindness, Partial hearing loss
- W3C Pattern Rearrangable Listbox
-
ember-sortable
-
<SortableGroup>
→<SortableGroupAccessible>
(wrapper) -
<SortableItem>
→<SortableItemAccessible>
(wrapper)
-
-
Splattributes
<SortableGroupAccessible tabIndex="0" class="border focus:border-teal-400" />
<div ...attributes> {{yield}} </div>
-
Modifiers
<div {{key-up this.handleArrowUp key="ArrowUp"}}></div>
-
@ember/test-helpers
-
triggerEvent
andtriggerKeyEvent
- Test reordering and key events 🙌
test('it can listen for specific key up events', async function() { this.keyUp = ({ key }) => { assert.equal(key, 'Enter'); assert.step('key up'); } await render(hbs` <div {{key-up this.keyUp data-test-id="keyup"}}></div> `) let selector = '[data-test-id=keyup]'; await triggerKeyEvent(selector, 'keyup', 'Enter'); assert.verifySteps(['key up']); });
assert.dom(findAll('[data-test-id=item]')[0]).hasText('Item 1'); assert.dom(findAll('[data-test-id=item]')[1]).hasText('Item 2'); // Reorder await triggerEvent('[data-test-id=group]', 'focus'); await click('[data-test-id=sort-down]'); assert.dom(findAll('[data-test-id=item]')[0]).hasText('Item 2'); assert.dom(findAll('[data-test-id=item]')[1]).hasText('Item 1');
-
- Quick feedback loop
EmberQuest: Building an Octane Role Playing Game - Dan Monroe
-
ember new emberquest
🎮 - Hex tiles
- Pathfinding
- WebGL
- Canvas
- Web Audio
-
ember-concurrency
@task *reloadHealth() { // ... }
- Phaser 🕹️
-
ember-phaser
ember install ember-phaser
ember generate phaser-scene tomster
ember generate service game
ember generate component gameboard
An Ember Dev’s Guide to CSS Grid - James Steinbach
- Grid: Responsive CSS-only layout method
- Column / Row / Gap control 🤝
- Grid properties & values:
grid-template-columns
,grid-template-rows
,fr
,max-content
,grid
😱😨display: grid; grid-template-columns: 1fr 40em 20em 1fr; grid-template-areas: "header header header header" ". body sidebar . " "footer footer footer footer"; gap: 12px 16px;
-
grid-template-columns
: Set the proportions for tracks along the inline axis of the grid container- 30% 150px 30%;
-
grid-template-rows
: Set the proportions for tracks along the block axis of the grid container- 50px repeat(10, 100px);
- 🚨 Warning 🚨 Fixed heights cause overflow
- Better:
auto
,min-content
,max-content
- Better:
- Logical properties
-
inline
: start to end of a line of text in the block -
block
: start to end of the block of text
-
-
dir=rtl
andwriting-mode: vertical-lr
support for free -
grid-template-areas
: assign names to grid areas- One rectangle, adjacent cells, strings (not integers)
-
grid-template
: assign rows, columns, and areas as a shorthand- Every row ends with its height
- Every column ends with its width
grid-template: "header header header header" auto ". body sidebar . " auto "footer footer footer footer" auto / 1fr 40em 20em 1fr;
-
gap
: Creates gaps between columns and rows - Grid container values
-
fr
: 1 fraction of the grids free space- Calculated after non-
fr
space is used - Based on the total number of
fr
in the row or column
- Calculated after non-
repeat(<count>, <length>)
-
repeat(12, 1fr)
=== 12 column Bootstrap
-
-
grid-area
: Agrid-template-areas
name from the parent - Center an item
display: grid; justify-content: center; align-items: center;
- Gotchas
- Images sizes:
img
obeys a the grid-cell size →object-fit: cover
- Animating grids: Some browsers animate, can’t animate between areas
- IE 11 😨
- Images sizes:
Stronger App Architecture Using Maps - Matt Gardner
- GIS (Geographic Information System) Mapping technology
- NYC Planning 📍
- Changing expectation about how governments deliver digital services
- We use maps every day 🗺️
-
ZoLa
- Making data easy to consume
- Where can we build Housing?
- A brief history of maps
- John Snow: London Cholera outbreak map
- Florence Kelley: Income map by residential block
- Basemap: Static data, always present
- Data Layers: Polygons, Lines, Points
- Enter…The Tile: Avoid triggering a fullpage reload when navigating
- Dynamic interactions 🖱️
- How are tiles made?
- Cylindrical map projection
- Assume that the world is a square 🟦
-
GeoJSON: Opinionated JSON format for storing spatial data
- Best for most use cases
- Leaflet
- ember-leaflet
- mapbox-gl-accessibility
- ember-mapbox-gl
- Test Stubs → Dependency Injection
- How to lie with maps 🤥
- “Map users seldom, if ever, question these authorities, and they often fail to appreciate the map’s power as a tool of deliberate falsification or subtle propaganda.”
- Spatial correlation !== Meaningful data relationship
The Power of Debugging - Samanta de Barros
- Challenges
-
Dev vs Prod
- Sources tab
- Breakpoints
- Pretty print formatting for minified JS
-
External Libraries
- Stack trace ⛔
- Don’t be afraid to step in 🚶♀️
- Addon vs NPM library code location
-
Not My Code
- Use the Ember Inspector
- Find the function location
-
Dev vs Prod
Talking to Your Dog with Ember - Robert Wagner
- wuf.plus 🐶
- Web Audio API
-
fftSize
(Fast Fourier Transform) andfrequencyBinCount
getByteFrequencyData()
getByteTimeDomainData()
-
- Determining dog bark types 🐕
- ~1000-2000 Hz range and between ~80-90 dB (decibels)
- Finding db spikes
- Types: Alert, Greeting/Playful, Distress
- Deciphering audio data 🔊
@action async startRecording() { const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); const options = { mimeType: 'audio/webm' }; this.mediaRecorder = new MediaRecorder(stream, options); this.mediaRecorder.addEventListener('dataavailable', e => { this.args.uploadAudioVideo({ blob: e.data }); }); this.mediaRecorder.start(); }
- Integration with Ember 🐹
- Future work
- Add more bark types
- Refine frequency ranges
- Add “Talk Back” feature
The Moderate Programmer - Derek Gavey
- Moderate: Avoiding extremes of behavior
- The moderate programmer names things appropriately
- 3 words or less
- Avoid jargon and metaphors
factory
,object
,class
- Use plurals where appropriate
- For booleans, prefix with
is
,has
,can
- For functions, user a verb then noun
- The moderate programmer writes small functions
- Function does one thing
- Beware of flags in arguments
- Beware of too many arguments
- Components are functions too
- The moderate programmer writes tests
- Write your tests immediately (or you won’t write them)
- Integration tests are your best bet
- You have diminishing returns after
n%
code coverage
- The moderate programmer refactors
- RailsConf 2014 - All the Little Things -Sandy Metz
- “Duplication is far cheaper than the wrong abstraction”
- Write ugly code!
- Rule of 3 (abstract on your third duplication)
- Will future me/teammates understand this better?
- Weight the costs of the refactor
- The moderate programmer gives up
- Give up and ask for help
- Give up and take a break 🧘
- Give up and throw out your code…it’s ok
Why Contributing Seems Scary - Anne-Greeth van Herwijnen
- Contributing 🙋♂️
- Everyone sees this differently
- Add / Remove code
- Setting up a meetup
- Code review
- Ideas
- Motivation → Social Norms → Community
- Motivation: Intrinsic / Extrinsic
- Social Norms: Ask ⁉️ ↔ Encouragement 😆
- Community: Psychological sense of community → Commitment
- What can we learn?
- Thank every contributor 🤗🍻
- Ask people to contribute where their strengths are
Octane Octopus: The Multi-Faceted Migration - Jordan Hawker
- Why is the migration experience important?
- Past migration pains
- Stability
- Productivity
- Can continue building new Octane feature while you’re migrating
- Unlock new features quickly
- Reduce manual refactoring costs
- Minimize overhead of learning new patterns
- Many features are polyfilled and stable back to 3.8
- Recommendations
- Phase 1: Upgrade to Ember 3.16 LTS
- Enable Octane features
- ember-cli-update
- Phase 2: Codemods
- Phase 3: Prepare for Glimmer
- ember-classic-decorator
- Remove classic Ember patterns
- Template-only components
- Phase 4: Adopt Glimmer
- Preliminary component refactors
- Empty
tagName
- Migrate
classNames
,classNameBindings
,attributeBindings
-
{{on "click" (fn this.foo 'bar')}}
event handlers - Modifiers
- Empty
- Finish Glimmer conversion
-
computed() / this.set()
→@tracked
- Convert lifecycle hooks
-
- Preliminary component refactors
- Phase 1: Upgrade to Ember 3.16 LTS
- Classic vs Octane Cheat Sheet
- Ember Atlas Recommended Migration Order