Upgrade to Pro — share decks privately, control downloads, hide ads and more …

progressive enhancement for JS apps

progressive enhancement for JS apps

When progressive enhancement–offering a basic version of a feature that all clients can use as a default–was introduced as a concept, JavaScript applications seemed as relevant as flying cars. As JS became more powerful, it seemed we'd reach a point where we could forget about PE entirely. For the things we originally used it for, we now have rock-solid libraries and polyfills to provide abstractions that make PE easy. But as JS has advanced, we've started writing things that can't be polyfilled. We know now how to progressively enhance widgets and user interactions. We'll talk about how we progressively enhance entire applications, and why it's potentially more important than ever that we do so.

Garann Means

May 09, 2014
Tweet

More Decks by Garann Means

Other Decks in Technology

Transcript

  1. progressive
    enhancement for
    JS apps
    Garann Means @garannm

    View Slide

  2. why this talk?

    View Slide

  3. a little case study
    tons and tons of JS
    real-time
    collaborative
    offline capabilities*
    fallback: a textarea

    View Slide

  4. the fix: progressive
    enhancement

    View Slide

  5. 2003-2008

    View Slide

  6. 2003-2008
    “let’s use JS for everything!”

    View Slide

  7. 2003-2008
    “let’s use JS for everything!”
    “what, people have JS turned off?”

    View Slide

  8. 2003-2008
    “let’s use JS for everything!”
    “what, people have JS turned off?”
    “what, browser support is inconsistent?”

    View Slide

  9. 2003-2008
    “let’s use JS for everything!”
    “what, people have JS turned off?”
    “what, browser support is inconsistent?”
    “who cares, our customers are cutting edge”

    View Slide

  10. 2003-2008
    “let’s use JS for everything!”
    “what, people have JS turned off?”
    “what, browser support is inconsistent?”
    “who cares, our customers are cutting edge”
    from yesterday: “if you’re seeing a broken
    experience, your environment is broken”

    View Slide

  11. 2003-2008
    “let’s use JS for everything!”
    “what, people have JS turned off?”
    “what, browser support is inconsistent?”
    “who cares, our customers are cutting edge”
    from yesterday: “if you’re seeing a broken
    experience, your environment is broken”
    “oh weird our business failed”

    View Slide

  12. progressive enhancement:
    deliver a usable baseline experience
    offer an upgraded experience to more powerful
    browsers

    View Slide

  13. vs. graceful degradation
    code for the most powerful clients
    problem? deliver a lesser experience

    View Slide

  14. why’d we switch?
    degradation is harder to plan accurately
    degradation makes things even slower for less
    powerful clients
    hope for the best, plan for the worst

    View Slide

  15. this is a best practice
    on the front-end

    View Slide

  16. JS apps

    View Slide

  17. JS apps
    “oh, but this isn’t for people who don’t have
    JavaScript”

    View Slide

  18. JS apps
    “oh, but this isn’t for people who don’t have
    JavaScript”
    “..or people returning to the site”

    View Slide

  19. JS apps
    “oh, but this isn’t for people who don’t have
    JavaScript”
    “..or people returning to the site”
    “..or people going through a tunnel on a train”

    View Slide

  20. *ahem*

    View Slide

  21. have we learned nothing?
    obviously we want the best experience possible
    this is the internet; “possible” changes
    brittle expectations of our users means a worse
    experience
    for everyone

    View Slide

  22. what to do with
    that thinking?

    View Slide

  23. a method
    build servers as if every client is text-only
    build clients as if the internet will disappear
    tomorrow
    expect people not to use the app the way you
    expected

    View Slide

  24. some simple principles
    state is shared between client and server
    the client takes responsibility for state offline
    and attempts to sync when it comes online
    actions, by default, occur on both client and
    server

    View Slide

  25. “computer, enhance!”

    View Slide

  26. “computer, enhance!”
    is JS available?
    great, run the client-side app

    View Slide

  27. “computer, enhance!”
    is JS available?
    great, run the client-side app
    are we online?
    great, send updates to the server

    View Slide

  28. “computer, enhance!”
    is JS available?
    great, run the client-side app
    are we online?
    great, send updates to the server
    client and server aren’t in sync?
    no problem, put them in sync

    View Slide

  29. sharing state is more
    than just sharing
    state

    View Slide

  30. this is a state
    $( “#form-step-2” ).show();

    View Slide

  31. (this is a shared state)
    body.registration-step-2 #form-step-2 {
    display: block;
    }

    View Slide

  32. this one isn’t being shared
    $( “#form-step-2” ).show();

    View Slide

  33. state runs the app
    interface changes
    fetching data
    app logic updates the state
    state changes trigger everything else

    View Slide

  34. we can still do this
    $( “#form-step-2” ).show();

    View Slide

  35. it just works differently
    $( “#form-step-2” )[
    registration_step == 2 ?
    “show” :
    “hide”
    ]();

    View Slide

  36. protip: CSS is an
    observer by default

    View Slide

  37. same on the server
    app.get( “/register/:step?”,
    function( req, res ) {
    var step =
    req.params.step || registration_step;
    res.render(
    “reg_step_” + step, registration_data );
    });

    View Slide

  38. question: where is
    registration_step set?

    View Slide

  39. glad you asked!
    updates about state are separate from updates
    as a result of state
    data updates are chunked
    state updates are instant (if possible)

    View Slide

  40. separate state updates
    make syncing easier

    View Slide

  41. let’s say our user goes offline
    > my_app.state.registration_step
    1

    View Slide

  42. then clicks submit
    $( “#form-step-1” ).on( “submit”, function(){
    set_registration_step( 2 );
    return false;
    });

    View Slide

  43. it ’s fine
    > my_app.state.registration_step
    2

    View Slide

  44. everything handled
    function set_registration_step( step ) {
    my_app.state.registration_step = step;
    update_state( “registration_step_” + step );
    var data = get_data( step - 1 );
    save_data( ( step - 1 ), data );
    $( “form-step-” + step ).show();
    }

    View Slide

  45. and stored
    function save_data( step, data ) {
    db.put( data, “registration_” + step,
    function( err, response ) {
    if ( err ) {
    show_error( “Data not saved!” );
    }
    });
    }

    View Slide

  46. note: plan your storage
    small apps may be able to be stored entirely
    only the current workflow in large apps may be
    able to be stored
    the more changes happen offline, the less room
    for potentially used data

    View Slide

  47. if you accept data,
    you have to hang
    onto it

    View Slide

  48. how does the client know it ’s
    offline?
    poll for server updates
    catch a socket error
    sending data via normal XHR
    wait for an error saving

    View Slide

  49. hope for the best,
    plan for the worst
    function update_state( state ) {
    state_cache.push( state )
    var req = $.ajax({
    type: “POST”,
    url: “/update_state”,
    data: state_cache
    }).done( function() {
    state_cache = [];
    }).fail( function( xhr, err ) {
    if ( err == “timeout” ) start_heartbeat();
    });
    }

    View Slide

  50. offline, the client is on
    its own

    View Slide

  51. how do know you’re back?
    try it
    navigator.onLine
    probably some sort of heartbeat
    no matter your strategy

    View Slide

  52. and then:
    send client’s current state
    follow a defined path from server’s current state
    to client’s
    or send specific steps from the client
    sync server data with client

    View Slide

  53. tl;dr, offline is scary

    View Slide

  54. so let ’s talk actions


    View Slide

  55. actions in the interface
    never trigger actions directly
    actions are triggered by state changes
    state changes update client and server
    then the action itself occurs

    View Slide

  56. this guy
    $( “#form-step-2” ).show();

    View Slide

  57. and this one
    $( “#form-step-2” ).save();

    View Slide

  58. saving (for example)
    fetches user-entered data from the interface
    stores it in an application variable
    adds it to the local data store
    notifies the server that a save occurred
    syncs data

    View Slide

  59. ..then the server
    default: fetches user-entered data from the client
    enhanced: syncs with client DB
    stores it in an application variable
    adds it to the backend data store
    notifies any other connected clients that there are
    changes

    View Slide

  60. same thing happens
    on both sides, why?

    View Slide

  61. other connected clients &
    reloads
    fetches user-entered data from the server
    stores it in an application variable
    adds it to its local data store
    updates its interface

    View Slide

  62. if JS disappears
    we use HTML behaviors (links, buttons) to skip
    right to the server step
    concurrent users receive immediately-expiring
    data

    View Slide

  63. we need state reflected in:
    client and server variables, ofc
    the URL
    the CSS

    View Slide

  64. tada: it ’s progressive!
    our server has a version of the app and can
    render its interface
    client side can continue without the server, in
    case we go offline
    real-time communication and multiple
    concurrent users follow the same pattern

    View Slide

  65. our app is now
    usable by multiple clients
    in multiple locations
    and easier to continue extending
    by layering functionality, as with progressive
    enhancement

    View Slide

  66. thanks!
    @garannm [email protected]

    View Slide