A detailed look into the unique engineering challenges and solutions which went into delivering a key Yahoo! product that needed to provide a rich cross-device application without compromising on the 'native' experience.
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Livestand : Learnings. YUI Conf 2011
1. Livestand
Learnings
Wednesday, November 9, 2011
52
2. Livestand
Learnings
http://blog.jeffreymcmanus.com/157/learnings-is-a-stupid-stupid-word/
Wednesday, November 9, 2011
52
3. The next hour of your life*
• Livestand
• WebApp, Native
• YUI
• JS
• CSS
• HTML
• YQL, Data
• Hybrid
Wednesday, November 9, 2011
2.49 (2min)
4. The next hour of your life*
• Livestand
• WebApp, Native
• YUI
• JS
• CSS
• HTML
• YQL, Data
• Hybrid
* I completely understand if this suddenly makes you rethink your
path in life and you’d like to leave to catch the next flight to Hawaii
Wednesday, November 9, 2011
2.49 (2min)
5. Livestand
• Rich Content Presentation
• Highly Customizable Content
• Publishers - Custom Content Experiences
• Users - Personalized Experiences
• Shared Experience Across Devices
• Rich Ads
Wednesday, November 9, 2011
5.40 (3min), Demo (2min). Livestand - more than a “digital magazine”. Heavy duty Y!
infrastructure driving content personalization. Target devices: Not just iOS, or even iOS +
Android. Anything which runs a Web stack.
7. Native
• Runtime(s) which can access device h/w
• Runtime(s) which may perform better
• Runtime(s) across which we couldn’t reuse code
• Runtime(s) we’d need to find developers for
• Closed runtime(s)
Wednesday, November 9, 2011
10:30 (3min)
8. WebApp
• A common runtime across devices - shared code
• A runtime we have (experienced) developers for
• An open runtime
• A runtime with limited access to the device h/w
• A runtime which may have performance constraints
Wednesday, November 9, 2011
12.41 (2min)
9. Hybrid
• A common runtime across devices - shared code
• A runtime we have (experienced) developers for
• An open runtime
• A runtime with limited access to the device h/w
• A runtime which may have performance constraints
Wednesday, November 9, 2011
12.41 (2min)
10. Livestand
• Virtually all of the UI and App Logic is JS, HTML, CSS
• Native Services Layer
Wednesday, November 9, 2011
14:40 (2min). Most UI/App Logic sits in the WebStack. This is the logic which is most likely to
change on a weekly/monthly basis in reaction to UED/Product requirements. Native Layer is
fairly stable/util layer. Doesn’t need to be modified (anywhere near) as frequently.
11. Livestand
• Virtually all of the UI and App Logic is JS, HTML, CSS
• Native Services Layer
• Multi-User Authentication
• YQL Caching
• Local URL Routing, Package Management
• Memory Management / Thread Control
• Multi-WebView
• Deployment Management
Wednesday, November 9, 2011
14:40 (2min). Most UI/App Logic sits in the WebStack. This is the logic which is most likely to
change on a weekly/monthly basis in reaction to UED/Product requirements. Native Layer is
fairly stable/util layer. Doesn’t need to be modified (anywhere near) as frequently.
12. YUI
• Modules, Dependencies
• JS Utils (oop, each, bind ...)
• Node
• Event
• Gestures - flick, move, tap*
• Intl
• IO
Wednesday, November 9, 2011
15:36 (1). Core areas of YUI used - Modules/Loader Dependency management (to drive
composition of independently developed/custom modules onto a single page) and the
abstraction layers - to not only drive the “Cross Device” product goal, but also so that as
more and more (independently developed) module code is shipped, we don’t need to worry
about whether or not module A has WebKit specific code and won’t port to Opera.
13. Modules and Dependencies
News
Device
Sports Stocks Weather
Cloud
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
14. Modules and Dependencies
News
Device
Sports Stocks Weather
Mojito MVC Framework YUI
YChrome - Hybrid Bridge
Cloud
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
15. Modules and Dependencies
News
Device
Sports Stocks Weather
Mojito MVC Framework YUI
YChrome - Hybrid Bridge
Package Repository YQL User Preferences
Cloud
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
16. Modules and Dependencies
News
Device
Sports Stocks Weather
Mojito MVC Framework YUI
YChrome - Hybrid Bridge
Package Repository YQL User Preferences
HTML
Stocks Stocks
Stocks CSS Cloud
Feed Data Preferences
JS
Internal and External Modules Internal and External Publisher Data User Preferences and Profiles
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
17. Modules and Dependencies
News
Device
Sports Stocks Weather
Mojito MVC Framework YUI
YChrome - Hybrid Bridge
Package Repository YQL User Preferences
HTML
Stocks Stocks
Stocks CSS Cloud
Feed Data Preferences
JS
Internal and External Modules Internal and External Publisher Data User Preferences and Profiles
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
18. Modules and Dependencies
Stocks
News
YUI.add(“stocks.model”, fn() {
Sports Stocks Weather
}, ..., {requires:[io, json]})
Mojito MVC Framework YUI
YChrome - Hybrid Bridge
YUI.add(“stocks.binder”, fn() {
}, Package Repository
..., {requires:[ls-vitality-looper]})
YQL User Preferences
HTML
Stocks Stocks
Stocks CSS
Feed Data Preferences
YUI.add(“stocks.controller”, fn() {
JS
}, ..., {requires:[mojito-partial-addon]})
Internal and External Modules Internal and External Publisher Data User Preferences and Profiles
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
19. Modules and Dependencies
News
Sports Stocks Weather
Mojito MVC Framework YUI
YChrome - Hybrid Bridge
Package Repository YQL User Preferences
HTML
Stocks Stocks
Stocks CSS
Feed Data Preferences
JS
Internal and External Modules Internal and External Publisher Data User Preferences and Profiles
Wednesday, November 9, 2011
20 (4.3min) Stocks, News, etc. are independent modular implementations (Mojits, as they are
called in the Mojito world). Mojits are defined as YUI modules and have their own set of
dependencies. Mojito uses Loader (either at run-time or build-time - configurable), to
determine the composite set of dependencies which make up the page.
20. Node, Event, Gestures
Going Through 10,000 Lines of Code With
A Fine Toothed Comb
Wednesday, November 9, 2011
As mentioned before - don’t want to have to weed through 10,000 lines of independently
developed, 3rd party code, in order to figure out which Mojit may be hardcoding WebKit
references, or Firefox references ... and so may not work in the next environment we need to
support. That’s why it makes sense to have an abstraction layer to work against (Node, Event,
Gestures, IO etc.) even if you think you may not need it today.
21. Node, Event, Gestures
Going Through 10,000 Lines of Code With
A Fine Toothed Comb
Wednesday, November 9, 2011
As mentioned before - don’t want to have to weed through 10,000 lines of independently
developed, 3rd party code, in order to figure out which Mojit may be hardcoding WebKit
references, or Firefox references ... and so may not work in the next environment we need to
support. That’s why it makes sense to have an abstraction layer to work against (Node, Event,
Gestures, IO etc.) even if you think you may not need it today.
22. Node & NodeList
Wednesday, November 9, 2011
22:43 (2.4min) Aside from the abstraction, you also get sugar - NodeList iteration,
setContent, ancestor, and even Array-like methods - push/pop/shift/unshift etc.
23. Node & NodeList
item = e.target.ancestor('li[data-name]');
icon.getComputedStyle('backgroundImage');
Wednesday, November 9, 2011
22:43 (2.4min) Aside from the abstraction, you also get sugar - NodeList iteration,
setContent, ancestor, and even Array-like methods - push/pop/shift/unshift etc.
24. Node & NodeList
node.all('[data-image],[data-bg-image]').each(
function(node){
...
ancestor = ... ?
node : node.ancestor('[data-index]');
node.one('.summary').setContent(art.title);
...
}
)
Wednesday, November 9, 2011
22:43 (2.4min) Aside from the abstraction, you also get sugar - NodeList iteration,
setContent, ancestor, and even Array-like methods - push/pop/shift/unshift etc.
25. Node & NodeList
children = node.get('childNodes');
...
while (parent.get('scrollHeight') < h
&& children.size()){
lastChild = children.shift();
node.appendChild(lastChild);
}
Wednesday, November 9, 2011
22:43 (2.4min) Aside from the abstraction, you also get sugar - NodeList iteration,
setContent, ancestor, and even Array-like methods - push/pop/shift/unshift etc.
27. Event
this.handle = btn.on(
'click',
onClickNextDay
);
...
this.handle.detach();
Wednesday, November 9, 2011
24:00 (1.5 min) Same for event. Sugar: once, delegate, handle.detach()
28. Event
Y.one('window').on(
'webviewWillAppear',
onWebviewAppeared
);
Wednesday, November 9, 2011
24:00 (1.5 min) Same for event. Sugar: once, delegate, handle.detach()
29. Event
Y.one('#foreground').once(
'contentready',
initView
);
Wednesday, November 9, 2011
24:00 (1.5 min) Same for event. Sugar: once, delegate, handle.detach()
30. Gestures
• flick
zodiac.on('flick', handleZodiacFlick);
• gesture movestart, move, moveend
publications.delegate('gesturemovestart',
startPubEdit, '[data-name]');
publication.on('gesturemove', movePub);
publication.once('gesturemoveend', endPubEdit);
• tap
node.delegate('tap', handleTap, '[data-index]');
Wednesday, November 9, 2011
25.41 (1.5min) The sugar also works for gestures. Aside from using flick, gesturemove*,
Livestand developed their own Tap gesture which needs to be rolled back into YUI, to work
around the ~400ms click delay.
31. Wednesday, November 9, 2011
27.20 (1:40min) What a gesture/synthetic event impl looks like. Ability to add low-level
“gateway” criteria before notifying listeners - a right click is not a tap, two fingers is not a tap
etc.
32. Y.Event.define(EVENTS.TAP, {
})
Wednesday, November 9, 2011
27.20 (1:40min) What a gesture/synthetic event impl looks like. Ability to add low-level
“gateway” criteria before notifying listeners - a right click is not a tap, two fingers is not a tap
etc.
33. Y.Event.define(EVENTS.TAP, {
var EVENTS = TOUCH ? {
START : 'touchstart'
...
} : {
START : 'mousedown'
...
};
on : function (...) {
node.on(EVENTS.START, this.gestureStart ...);
},
delegate : function (...) {
...
},
...
})
Wednesday, November 9, 2011
27.20 (1:40min) What a gesture/synthetic event impl looks like. Ability to add low-level
“gateway” criteria before notifying listeners - a right click is not a tap, two fingers is not a tap
etc.
34. Y.Event.define(EVENTS.TAP, {
...
gestureStart : function(...) {
// Right/middle clicks aren’t a tap gesture
if (e.button && e.button !== 1) { return; }
...
},
gestureEnd : function (...) {
var endXY = TOUCHES ? [e.changedTouches[0].pageX,...]
: [e.pageX,...];
...
// If the mouse/finger moved, it’s not a tap gesture
if (Math.abs(endXY[0] - startXY[0]) < THRESHOLD
&& Math.abs(endXY[1] - startXY[1]) < THRESHOLD) {
e.type = EVENTS.TAP;
e.pageX = endXY[0];
e.pageY = endXY[1];
...
notifier.fire(e);
}
}
})
Wednesday, November 9, 2011
27.20 (1:40min) What a gesture/synthetic event impl looks like. Ability to add low-level
“gateway” criteria before notifying listeners - a right click is not a tap, two fingers is not a tap
etc.
35. Feeding Back Into YUI
• Loader : Pre-compute dependencies off device
• Get : Parallel Dispatch Support
• Feature Testing : Cache Across Instances
• K-Weight Reduction : Lighter Passthrough
• Tap Gesture
• ScrollView
Wednesday, November 9, 2011
32.16 (5min) a) Moved Loader costs off-device, to a build-time step. Basically don’t do at
run-time what you could do a build-time. b) Added Get parallel dispatch support to 3.4.0
Loader will be upgraded to work with it for 3.5.0 c) Maybe there’s some stuff we could cache
across Y instances. e.g. Feature Test results. d). node-core - a passthrough abstraction layer,
if you really just want to develop for iOS right now, but would like the safety net of being able
36. ScrollView
• Scale
10’s of instances / page
• H/W Acceleration Memory Usage
Limit GPU Composite layer size, by only maintaining N of M
pages of content in the DOM at a time
Wednesday, November 9, 2011
35.06 (3min) Possible Lighter ScrollView, without custom events, for cases where you don’t
need a rich API to work with (ala NodePlugins).
37. Offline Loader
5.2s
2.9s
Wednesday, November 9, 2011
36.56 (2min) Savings by moving Loader off-device
38. JS Profiler
Wednesday, November 9, 2011
38:27 (1.5min), 3.00 demo: Awesome on-device profiling support (hoping to open source!).
Hooks into JavaScript core on the native side to collect function call data, on-device, and
dumps trace data which is viewable in a slightly modified version of Android’s TraceView
(modified to show stack trace).
39. JS
• Most JS cost impact is around retrieving and parsing code
• Minification reduces parsing costs - parsing costs
• File I/O is expensive - Combo’ing JS is valuable, even on device
• JIT compilation still not available for iOS UIWebViews
Wednesday, November 9, 2011
43.16 (2min)
40. CSS
• H/W Acceleration Has Its Price
• There’s a significant memory cost
•Flipping from 3D to 2D to try and free up memory introduces flicker
•visibility:hidden reduces memory cost, but still has a runtime cost
•Removing content from the DOM is better
• translateZ introduces stacking context
• Experimental/glitchy
• Inlined Structural CSS
• IO cost savings, and also to avoid CSS application race conditions with JS
• Surprising Reflow and Style Recalculation Triggers
Wednesday, November 9, 2011
47 (3.5min)
41. Wednesday, November 9, 2011
50.47 (4min) Interesting Trace information. Hooks added to WebCore, show trace from JS
layer to WebCore.
42. elem.focus() causes layout()?
willLayoutImpl:
1 WebCore WebCore::InspectorInstrumentation::willLayout(...)
2 WebCore WebCore::FrameView::layout(...)
3 WebCore WebCore::Document::updateLayout()
4 WebCore WebCore::Document::updateLayoutIgnorePendingStylesheets()
5 WebCore WebCore::Element::focus(...)
6 WebCore WebCore::jsElementPrototypeFunctionFocus(...)
..
9 JavaScriptCore JSC::Interpreter::execute(...)
Wednesday, November 9, 2011
50.47 (4min) Interesting Trace information. Hooks added to WebCore, show trace from JS
layer to WebCore.
43. get scrollLeft cause style recalc?
willRecalculateStyleImpl:
1 WebCore WebCore::InspectorInstrumentation::willRecalculateStyle(...)
2 WebCore WebCore::Document::recalcStyle(Node::StyleChange)
3 WebCore WebCore::Document::updateStyleIfNeeded()
4 WebCore WebCore::Document::updateLayout()
5 WebCore WebCore::Document::updateLayoutIgnorePendingStylesheets()
6 WebCore WebCore::Element::scrollLeft() const
7 WebCore WebCore::jsElementScrollLeft(...)
8 JavaScriptCore JSC::PropertySlot::getValue(...) const
9 JavaScriptCore JSC::JSValue::get(...) const
Wednesday, November 9, 2011
50.47 (4min) Interesting Trace information. Hooks added to WebCore, show trace from JS
layer to WebCore.
44. get offsetWidth causes style recalc?
willRecalculateStyleImpl:
1 WebCore WebCore::InspectorInstrumentation::willRecalculateStyle(...)
2 WebCore WebCore::Document::recalcStyle(Node::StyleChange)
3 WebCore WebCore::Document::updateStyleIfNeeded()
4 WebCore WebCore::Document::updateLayout()
5 WebCore WebCore::Document::updateLayoutIgnorePendingStylesheets()
6 WebCore WebCore::Element::offsetWidth()
7 WebCore WebCore::jsElementOffsetWidth(...)
8 JavaScriptCore JSC::PropertySlot::getValue() const
9 JavaScriptCore JSC::JSValue::get() const
Wednesday, November 9, 2011
50.47 (4min) Interesting Trace information. Hooks added to WebCore, show trace from JS
layer to WebCore.
45. HTML
• Templating - Handlebars/Mustache
• Pre-build Templates : HTML t0 JS
• Many Templates, Single Use : Mustache may be better
• <VIDEO> appears to have a memory leak, when resetting src
• <VIDEO>, like <INPUT>, swallows touch events
• Time Analysis
• 200-300 ms after assigning a URL to a webview, before any io
Wednesday, November 9, 2011
2min
46. YQL - Data
• Pre-generated Data
• Offload Join/Aggregation costs to Mojito Server
• Offload Schema Normalization to YQL
• Local, Native, Offline Cache
• Work Around Promptless 5MB Web Storage Limit
• Leverage Native Thread Management
• Segregate Multi-User Databases
• Down The Road
• Pre-generated Seed SQL
• Support Sparse Records
• Native Connection Limit
• YQL Connection - 4 connection limit (same as browser)
Wednesday, November 9, 2011
3min. This is Huge! Mojito can run mojits on either the client or the (nodejs) server. So same
mojit code can be used to dispatch YQL requests on the server, to offload YQL request
combination/aggregation costs from device to the cloud. Huge!. Other than that, also makes
sense to offload data normalization costs from device to YQL. Chose native layer offline
caching to work around 5MB limit in Web SQL storage. Also allowed us to distribute workload
47. Hybrid
• Bridging Technique/Costs
• XHR/Background URL access
• Modifying window.location url
• Native Object/Class exposed to JS
• Caching YQL/Data Natively
• Multi Webviews
• Threading Concerns
• Experiments
• Using WebViews as WebWorkers
• Native JSON Parsing
Wednesday, November 9, 2011
Livestand used XHR, to have the JS client call the Native layer. Aside from not having to deal with window navigation, also allowed Native layer to
avoid memory copy. XHR response pointer could just be moved to the payload created by the Native layer. Multi-WebViews were used to prime
the basic stack for the page and user was switched between WebViews when moving from one type of page to the next, reducing time to initial
view. However only 1 JS thread across WebViews, so had to be careful about when they were primed.
48. Aside from “Wear Sunscreen”
• (Really) Understand the technologies you’re working with
• IO, Data Access costs and DOM are still the key performance pieces
• Progressive Enhancement is your friend
• Don’t do at run time what you can do at build time
• Think twice, tattoo once.
If it’s on your face, thrice really wouldn’t hurt.
Wednesday, November 9, 2011
2min JS/Web Developer moving from the one-step up formalization from a few years ago (OO
development, separation of concerns, JS performance analysis) to areas where you really need
to understand what the JS engines and browsers are doing (e.g. H/W acceleration). Overall IO/
Parsing/DOM/Data Access turned out to be the biggest chunk of cost while rendering the
page (as opposed to pure run-time JS costs). Progressive enhancement is still valid and can
49. Livestand Content Thanks To:
Andres Garza
Curtis Harvey
Daryl Low
Ganesan Sriram
Kris Giesing
Ric Allinson
Rob Simutis
Me:
Satyen Desai (sdesai@yahoo-inc.com)
Wednesday, November 9, 2011
1min These folks not only provided a lot of the meaty content for this talk, but they along
with many others contributed to Mojito, Livestand, YUI and YQL to deliver the experience as
you see it today.