Skip to content

Instantly share code, notes, and snippets.

@willscripted
Last active December 21, 2015 03:59
Show Gist options
  • Save willscripted/6246090 to your computer and use it in GitHub Desktop.
Save willscripted/6246090 to your computer and use it in GitHub Desktop.

Jeremy Askenas

Keynote

Business Logic vs Views

  • business logic

    • live much longer than views
    • does not belong in handlers
    • does not belong in views
  • views

    • stateless
    • able to change rapidly
    • antipattern: tracking the state of the world
    • designed for course grain rendering (unlike ember.js)
    • tests should not rely on faking clicks, too much coupling to the dom
    • should not be computing things that belong in a different abstraction
  • models

    • under utilized for business logic
    • wrap built-in methods to make methods more semantically meaningful

Garbage Collection & Views

  • objects consume resources

  • gc uses reference counting

  • models and collections usually outlive views

  • if an instance of a class is around forever, it shouldnt reference other things. those things need to be allowed to die.

Events

  • are references
  • usually emited by the dom, models, and collections
  • views
    • usually listen
    • die quickly
    • should be easy to unbind
on
  # callback held by keg
  aKeg.on "empty" griffin.notify
  bKeg.on "empty" griffin.notify
  cKeg.on "empty" griffin.notify

  # later - unbinding griffin requires a reference to all kegs
  aKeg.off "empty"
  bKeg.off "empty"
  cKeg.off "empty"
listenTo
  # callback held by griffin
  griffin.listenTo(aKeg, "empty", griffin.notify)
  griffin.listenTo(bKeg, "empty", griffin.notify)
  griffin.listenTo(cKeg, "empty", griffin.notify)

  # later - unbinding griffin only requires a reference to griffin
  griffin.stopListening()

Models

  • under-appreaciated place for logic

  • can add class methods too. eg User.lookup(id) that returns a promise

    class User
        @lookup = (id) ->
          deferred = Q.defer()
          xhr = new User({id: id}).fetch()
          xhr.then( (model) -> deferred.resolve(model))
          return deferred.promise
    
    User.lookup(id).then (user) -> 
         user.doStuff()

Other notes

  • routes and views are your interface

Tricks

Tracking collection
# `this` is collection that is tracking a top level collection
@listenTo("all", target, (ev, model) ->
  if ev == 'add' or ev == 'remove'
    this[ev](model)
)
Avoid rapid re-rendering
# wont get called until you stop calling render
render: _.debounce ->
  # called after method hasnt been called for timeout period

  #  do rendery stuff

, 0 

# under the hood, browsers set a minimum timeout of 4ms
# essentially just waiting for the call stack to resolve
# without rendering while the models are still updating
Unblock lengthy UI renders
  render: (items = @collection.models) ->
    toRender = _(items).first(50)
    toDefer  = _(items).rest(50)
    _(toRender).each((model) ->
      (new ItemView(item)).render()
    )
    _.defer(@render, toDefer)

Extending models
  Account = Backbone.Model.extend({...})
  Publisher = Account.Model.extend({...})
  Reader    = Account.Model.extend({...})
  GuestReader = Reader.extend({...})

Global Events provided by Backbone object
  • avoid overuse

  • 'mediator pattern' is not an excuse to publish all events to all things

    Backbone.trigger Backbone.on

Undo / Redo
  @on("change", ->
    changes.push(this.previousAttributes())
  )

  undo: ->
    undone.push @toJSON()
    @set(changes.pop())

  redo: ->
    @set(undone.pop())

Brian Mann

of Rdio
"Little Opinions, Big Possibilities: The tools and patterns for building lorge scale Backbone Apps"

Kristofer Joseph (@dam)

of Adobe
"Building Reflow" slides

  • Prototype all the things, fail hard, fail fast
  • Prototype the things you think are impossible
  • Backbone was a big win because of the onboarding process alone, open source reduces ramp-up time
  • Don't acquire "pink wheel syndrome" - "No, no, I'm not re-inventing the wheel, I'm just making a pink one."
  • Open source libraries beat out custom libraries every time because they have been "used in anger". Battle tested, thoroughly beatten upon.
  • Shout-out to the conversion to require.js. Painful, but very useful
  • Code coverage - sonar has some really erotic graphs
  • Approaches prototyping by making a HTML mockup and then TDD added functionality with JS

Ben Vinegar (@bentlegen)

of Disqus
"Backbone at Disqus: a Postmortem" slides

Third-party JS - Ben's book on third party applications

Useful JS Technologies for 3rd Party apps

Tips

#####Use a session object to proxy calls to different user models

window.session = new AnonymousUser()
window.session = new User({id: "someId"})
window.session = new SingleSignOnUser()

class UserMenu extends Backbone.View

  initialize: () ->
    @session = @options.session
    @listenTo(session, 'change', @render)

Dave Wassner

of Kinvey "Enterprise and the Mobile Web"

  • Mixins are your friend, but document & test relentlessly
 ResourceRoutingMixin = {
   fn: ->
   thing: ->
 }

 _.extend(Blogpost.prototype, ResourceRoutingMixin)
  • Cacheing resources

IndexedDB to cache data, use a success handler and call it twice. once when data is successfully taken out of cache, again when model syncs. Note websql is no longer a supported initiative. IndexedDB is the way to go. Space is critical

# Note that backbone provides extension points via options hash
fetch: (options) -> 
    if options.useCache
save: if(!navigator.onLine) db.transaction(this.name, 'readwrite')

Api security by user tokens - AnonymousUser token - can only create accounts - User token - user level access - Admin token - ignore all permissions, never included in client side app

Protip: 'innovation project' translates to 'we dont know what we are doing'

Trevor Landau

"Test-Driven Backbone Development"

Libs of Choice:

Notes:

  • Jasmine fails hard on async

  • Karma / Testacular + Selinium (multi-device browser testing)

  • jsDom - fake dom that makes dom tests superfast?

  • Test complexity - complexity grows when stringing together modules. Want to be able to black box the modules. Dont want to have to test what they claim to be implementing

resistance to change is not very salient in a code base

Sam Breed (@sambreed)

"Dependency Injection for Fun and Profit"

Dependo - visualize CommonJS and AMD dependencies Dependo Dependency Graph

Dependency injection

A way to remove hard-coded dependencies. Helps reduce the amount of code you need to hold in your head while developing. Dependencies become much easier to test or stub.

Tips
  • Enforce DI - dont allow a developer to instantiate a class unless all requisite refrences have been created

  • Use this Dunbarism

    # trick: pick out of {options}
    _.extend Klass, _pick(deps, options)

Software Engineering

Often, web developers either forget or dont think to engineer client side javascript with all the OOP basics they would apply to other software applications.

SOLID design principals (OOP 101)

  • Single Responsibily Principle - do exactly one thing well
  • Open / Closed Principle - "software entities should be open for extension, but closed for modification"
  • Libshivtz Substitution Principle - replacing a class with a subclass should not create unexpected states or behavior
  • Interface Segregation - no client should be forced to depend on methods it does not use
  • Dependency Injection - depend on abstracions not implementations
Single Responsibility Principle

A class should only ever have one reason to change.

require 'spec_helper'

# One responsibility
# Changes when endpoint changes
describe SomeController
  let(:user){ create(:user) }

  # Controller specs ...

end

# Two responsibilities
# Changes when endpoint changes
# Changes when user model changes
describe SomeController
  let(:user) do
    User.new :id => "5734", :required_field1 => "value"
  end

  # Controller specs ...

end
Open / Closed Principle

Eg in Backbone, you never override the Backbone.Model constructor method, instead, Backbone provided you with an extension point initialize.

// Model constructor function
var Model = Backbone.Model = function(attributes, options){

  // cid assigned
  // url root set
  // etc ...

  this.initialize.apply(this, arguments)

}
Liskov Substitution Principle

A subclass should be able to replace an instance of its parent type without sacrificing correctness or any of the desireable properties of the parent class.

  • TODO method arguments
  • TODO method return types
  • TODO preconditions / postconditions
  • TODO invariants
  • TODO history constraint - objs modifyable only through their methods (encapsulation)
Interface Segregation Principle

No client should be forced to depend on methods it does not use. Rather than requiring an instance of a dependency (as seen above in the SRP example), stub out the methods that are depended on.

require 'spec_helper'

# One responsibility
# Changes when endpoint changes
describe SomeController
  context "when authorized" do
    let(:user){ create(:user) }

    # Controller specs ...
  end

end
Dependency Inversion

Depend on abstractions - not implementations

Eg. View a collection of members.

  # Decouple objects from their dependencies by passing them in
  # at initialization
  class MembersView extends Backbone.View

    initialize: (opts) ->
      @members = opts.membersCollection
      @memberView = opts.memberView

  # Less flexible way to wire dependencies
  # Not as easy to reuse view elsewhere, esp. with
  # different member view classes
  class MembersView extends Backbone.View

    # Cannot change the way individual members are displayed
    memberView: Some.Hardwired.ViewClass

    # Collection restricted to this particular group of users
    members: Members.Of.OneParticular.App
  • Stubbing out hardwired dependencies is much more difficult

Notes

  • An application has two dependency graphs

    • instance graphs - set up mainly through dependency injection
    • constructor graph - loaded by module patterns
  • Recommends exposing a development variable to give you access to running application - for debugging

  • Tests enforce the contract of dependencies

Addy Osmani - aura.js Dont rely on service, rely on events generated by the service

  • Components are the future
  • Modules - dominic denecola `-> "Fight for this"

Mark Wench

"The Geologic Time Scale of Gilt.com's View Layer"

git log --reverse play framework status code in json -> bad, mmmkay? scandlebars can render on server side or client side - sites should be robot friendly - what are the trade-offs?

flexibility and maitainability are trade-offs that need to be made Prediction that we will see more chaplainjs and marrionette type things More backplugs

is the DOM good enough? where is jQuery's place? "Why mobile web apps are slow" `-> READ it

TypeScipt?

window.history.pushstate

Ryan @linked in (side conversation)

AMD +++ ViewModels

Bob Holt

"Domain-Driven Web Applications Using Backbone"

Domain model is conceptual, in the head of an expert Need to establish a ubiqutous language - well understood longuage accross teams - business, engineering, designers, etc. Should be priority number 1 for designing a system

Strictly separate lagers

Downwards relations may have references Up stack must use events

    | --------------- | 
    |     interface   | 
    | --------------- | 
    |     app logic   | 
    | --------------- | 
    |      domain     | 
    | --------------- | 
    |  infrastructure | 
    | --------------- | 

Real world domain is infinitely complex Entities need to have ids Value objects - commodity objects (do not need ids) Factory Aggregate route - single point of entry into a bundle of logic reduces complexity

John Paul

"I Like my jQuery Plugins warm and Toasty"

_.defaults {options}, {}
$.data?

Law of Demeter

Introduces some new code smells that identify change-sensitive code.

Aka "Don't talk to strangers" or "One dot rule". A prescription for loosly coupled OOD code. Reduces an objects required knowledge about the world, making it more resistant to change.

Spicifically, any given method should only talk to:

  • the object that owns it (this, @)
  • the parameters it was passed
  • any object the method creates
  • the component objects its parent object
  • a global variable (global access is not recommended)
  class Courtyard

    cook: (food) ->
      # ### Yes ###

      # method -> parent object
      @unlock()

      # method -> param
      food.prep()

      # method -> global variable
      beer = new Beer

      # method -> component of parent
      @grill.start()

      # method -> object created in method
      beer.open()


      # ### No ###

      @grill.propane.check()

      gas = @grill.getPropane()
      gas.check()

Only expose the functionality you need

jQuery patterns list

Brad Dunbar

  • Recommends reading through old issues
  • Also 'rays in the sun' - peach street in atlanta

Pamala Fox coursera

"Beyond jQuery Widgets: JS UI Library Design"

tip: when google searching for recent code bugs, use 'last year' or 'last month' as search restriction

"Gist is a great way to open source" "(When reading code) if you puke a little in your mouth, probably don't use that code" "I found a method on your class and i called it" - if private methods at all accissible from other classes, they will be treated by devs as public her solution:

 _private.method1 = function(){}
 # module method

You're not really paying attention to everything in life Keep people from making stupid mistakes How to document all events fired by a module DOM/Data function Hand it a dollar

LuccidJS - light weight event jsBin data attributes Nyan cat reporter "How to unit test private functions"

Documentation -- please include 'why does this exist' -- please also include 'how can i use this'

Asa Kusuma

of LinkedIn "Becoming a Control Freak with Controllers and Promises"

Use promises for security
  # authenticate will call success, not permitted will call error condition
  Authintication
    onRouteToPlace: ->
      userCanGoToPlace(Deferred)

    goToPlace: (deferred) ->
      deferred.then -> @openPrivateView()
      deferred.fail -> openUnauthorizedNoticeView()

LinkedIn Development

  • single branch, merging suck
  • feature flags ftw
  • train release mentality - jump on if you are ready
  • ViewModel` - presenterish representation of a model
  • there is space between the objects backbone provides - some additional legwork required for well structured code
  |---------------------------|
  |                           |
  |           Model           | resolve
  |                           |
  |-------.-------------------|
         / \
          | then(process NewRecord)
          |
  |---------------------------|
  |                           |
  |         Controller        | setCurrentRecord(record)
  |                           |
  |-------.-------------------|
         / \
          |
          |  onSaveClicked
  |---------------------------|
  |                           |
  |            View           |
  |                           |   *click event*
  |---------------------------|
  Presentation layer

  • context is everything in communication "'Short asian guy' doesnt help" or at least not until you start describing his desk location or team

  • code is communication

  • be concise and explicit

  • pass in the parent object

class Child extends Backbone.View

  initialize: (opts) ->
    @parent = opts.parent

    @listenTo(@parent, 'render', @delegateEvents)
  • promises

A promise represents a value that may not be available yet. The primary method for interacting with a promise is its then method. - A+ spec

  • neat function all - given an array of promises, return a new promise that resolve after all members resolve
# resolves after all syncs complete
Q.all([
  a.sync()
  b.sync()
  c.sync()
]) # returns a deferred
  • everything should belong somewhere, and if it is not obviaus, then your work is establishing that place and making it obvious

  • controllers are finite

    • can be paused
    • monogomous - should only operate on 1 application layer (model layer)
  • unified codebase - jsHint

  • control by making extension easy

  • use helpers, use mixins

  • create and document hooks esp pre-render and post render

  • linkedin uses many analytic tools in there application, many tied to these sorts of hooks. they are a part of the framework they've built

  • linked in devs highlight the importanco of seeing backbone as a tool suite for constructing an application architecture - it is not an application architecture.

  • big things are hard to control

  • think slowly, it takes time to move large application code bases

    • linked in converted to AMD step by step - cant imagine not converting
    • shim as needed
  • page controllers help a lot - especially with metrics

  • fire (analytic) events for all the things

  • encourage you to check out the dependency graphs

Libraries

  • Fiber - exteding components

  • Q - promises library

    • provides long stack traces for debugging
    • a number of helpful promise fns
    • jQuery compatible
  • Fastclick - touch events - dont wait to see if it was a dbl click

  • Venusjs - runs your entire js testing system

  • "Inject"

John Hann

"Advanced Architectural Patterns with Backbone and cujoJS"

CommonJS ++ Dependency injection - pass options into initialize Backbone - "Infrastructure / essential" Dependencies AOP - cause mixins are from hell

Pushing state into css ... ?

what is X-UA-Compatable?

OOCSS

Tim Branyen

"Growing Up with Backbone"

CommonJS syntax, compile to require AMD is weighty

Universal module definition is U.G.L.Y. ES6 modules are a thing Decouple apprunning from config loading import template form "root!comp/thing/place.html" see transpile task

careful with insertRejuire[thing] to start app compress file source maps

web components Templates, Decorators, Custom elements, imports, shadow dom

MDV Element hosting the module @host {

} tbranden scope css

Factory methods rather than constructors

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment