engineering the new linkedin profile

67
Engineering the New Profile Josh Clemm Tech Lead for the New Profile March 2013 | LinkedIn David Fleming Senior Product Manager at Zoomjax San Francisco Bay Area | Software Previous Education Golden Phase, FixDex Silicon Valley Business Academy

Upload: josh-clemm

Post on 01-Nov-2014

11.155 views

Category:

Technology


1 download

DESCRIPTION

Overview of the new frontend architecture used for the New Profile at LinkedIn.

TRANSCRIPT

Page 1: Engineering the New LinkedIn Profile

Engineering the New Profile

Josh Clemm Tech Lead for the New Profile

March 2013 | LinkedIn

David Fleming!Senior Product Manager at Zoomjax!San Francisco Bay Area | Software!!Previous!Education!

Golden Phase, FixDex!Silicon Valley Business Academy!

Page 2: Engineering the New LinkedIn Profile

Let's compare...

Page 3: Engineering the New LinkedIn Profile

Really a brand new product Refreshed Look & Feel Simplified Design

Surfaced New & Interactive Modules

New Data Insights

Page 4: Engineering the New LinkedIn Profile

New Features Improved in-line

editing experience Modules with In-line

searching & pagination

Page 5: Engineering the New LinkedIn Profile

The New Profile - Goals ●  Represent your entire professional identity

o  Not just your resume

o  Activity, Groups, Following, Connections, Insights about you and your network

Page 6: Engineering the New LinkedIn Profile

The New Profile - Goals ●  Represent your entire professional identity

o  Not just your resume

o  Activity, Groups, Following, Connections, Insights about you and your network

●  Needs to be more interactive

o  Keep users engaged on the page

o  Inline pagination, editing, searching

Page 7: Engineering the New LinkedIn Profile

The New Profile - Goals ●  Represent your entire professional identity

o  Not just your resume

o  Activity, Groups, Following, Connections, Insights about you and your network

●  Needs to be more interactive

o  Keep users engaged on the page

o  Inline pagination, editing, searching

●  Needs to be fluid, flexible, fast o  Progressive rendering

o  Maintain high performance

Page 8: Engineering the New LinkedIn Profile

So how did we achieve all that?

Page 9: Engineering the New LinkedIn Profile

Using a lot of Technologies

Page 10: Engineering the New LinkedIn Profile

And in particular...

Page 11: Engineering the New LinkedIn Profile

Let's look at our High Level Frontend Architecture

Page 12: Engineering the New LinkedIn Profile

Groups Content Service

Connections Content Service

Profile Content Service

Client/Browser CDN

Load Balancer

SCDS

Fizzy

Profile Web App

Profile Content Service

Profile Web App

Connections Content Service

Groups Content Service

Dust/JS/CSS Server

Retrieve Data Models from

Mid-tier

/profile/view?id=32 top_card.tl, background.tl

/profile/topcard /profile/background

Page 13: Engineering the New LinkedIn Profile

Groups Content Service

Connections Content Service

Profile Content Service

Client/Browser CDN

Load Balancer

SCDS

Fizzy

Profile Web App

Profile Content Service

Profile Web App

Connections Content Service

Groups Content Service

Dust/JS/CSS Server

Retrieve Data Models

/profile/view?id=32 top_card.tl, background.tl

/profile/topcard /profile/background

Our new architecture uses new tech at all layers

Page 14: Engineering the New LinkedIn Profile

Groups Content Service

Connections Content Service

Profile Content Service

Client/Browser CDN

Load Balancer

SCDS

Fizzy

Profile Web App

Profile Content Service

Profile Web App

Connections Content Service

Groups Content Service

Dust/JS/CSS Server

Retrieve Data Models

/profile/view?id=32 top_card.tl, background.tl

/profile/topcard /profile/background

Let's start at the bottom with Mappers

Page 15: Engineering the New LinkedIn Profile

Mappers - JSON endpoints ●  Convert data models from mid-tier services

into JSON

●  Each have an unique endpoint URL ( /profile/positions?id=42 )

Positions Mapper

JSON

"positions": [{ "position": {"id:{}"}, ]}

Biz Profile Model

Profile Model

Rich Media Rest Model

References Model

Profile Flex Model

Page 16: Engineering the New LinkedIn Profile

Mappers - an example public class PictureMapper extends ProfileParametersAwareMapper !{ ! private PictureContentMap pictureContentMap; ! private static final int ZOOMABLE_DIMENSION = 225; ! ! @Override ! public void doService() ! { ! PictureContentModel picCM = getContent(PictureContentModel.class); //declare content needs ! picCM.criteria().setId(getVieweeId()); //supply any input params ! ! assemble(); //invoke framework to retrieve declared content ! ! if (isResolvedWithoutErrors(picCM)) ! { ! //all went well, create new content map to hold output (JavaBean-like objects) ! pictureContentMap = ContentMap.proxyNew(PictureContentMap.class); ! ! Integer pictureWidth = picCM.getPictureWidth(); ! pictureContentMap.setIsZoomable(pictureWidth >= ZOOMABLE_DIMENSION); ! ! if(pictureWidth != null && pictureWidth > 0) ! { ! pictureContentMap.setWidth(pictureWidth); ! pictureContentMap.setHeight(picCM.getPictureHeight()); ! } ! pictureContentMap.setPictureID(picCM.getPictureID()); ! } ! } ! // tell framework to add our map to final output (uses Jackson to process into JSON) ! addOutput(pictureContentMap); ! } !

1 !2 !3 !4 !5 !6 !7 !8 !9 !

10 !11 !12 !13 !14 !15 !16 !17 !18 !19 !20 !21 !22 !23 !24 !25 !26 !27 !28 !29 !30 !31 !32 !

Page 17: Engineering the New LinkedIn Profile

Mappers - an example public class PictureMapper extends ProfileParametersAwareMapper !{ ! private PictureContentMap pictureContentMap; ! private static final int ZOOMABLE_DIMENSION = 225; ! ! @Override ! public void doService() ! { ! PictureContentModel picCM = getContent(PictureContentModel.class); //declare content needs ! picCM.criteria().setId(getVieweeId()); //supply any input params ! ! assemble(); //invoke framework to retrieve declared content ! ! if (isResolvedWithoutErrors(picCM)) ! { ! //all went well, create new content map to hold output (JavaBean-like objects) ! pictureContentMap = ContentMap.proxyNew(PictureContentMap.class); ! ! Integer pictureWidth = picCM.getPictureWidth(); ! pictureContentMap.setIsZoomable(pictureWidth >= ZOOMABLE_DIMENSION); ! ! if(pictureWidth != null && pictureWidth > 0) ! { ! pictureContentMap.setWidth(pictureWidth); ! pictureContentMap.setHeight(picCM.getPictureHeight()); ! } ! pictureContentMap.setPictureID(picCM.getPictureID()); ! } ! } ! // tell framework to add our map to final output (uses Jackson to process into JSON) ! addOutput(pictureContentMap); ! } !

1 !2 !3 !4 !5 !6 !7 !8 !9 !

10 !11 !12 !13 !14 !15 !16 !17 !18 !19 !20 !21 !22 !23 !24 !25 !26 !27 !28 !29 !30 !31 !32 !

Declare the data you need

Set the data you want coming back as JSON

Page 18: Engineering the New LinkedIn Profile

Mappers - features ●  Modular

o  A single mapper can retrieve data for a section §  Positions, Educations, Groups, etc.

●  Reusable & Combinable

o  Mappers can be used more than once §  Positions Mapper is used for Positions section and the

positions part of Top Card

o  You can Aggregate Mappers under a common root element and a new URL endpoint

Page 19: Engineering the New LinkedIn Profile

Profile's Many Mappers

●  Each section on Profile has either a single Mapper or Aggregated Mapper (like Top Card) for its data

Page 20: Engineering the New LinkedIn Profile

Profile's Many Mappers

●  Each section on Profile has either a single Mapper or Aggregated Mapper (like Top Card) for its data

Summary Mapper

Positions Mapper

Educations Mapper

Picture Mapper

Top Card Mapper

"TopCard": { "positions": {}, "educations":{}, "picture":{}}

Profile Web App

Connections Mapper

JSON "Summary": { "summary": "I'm an experienced..."}

JSON

Page 21: Engineering the New LinkedIn Profile

So we have these Mappers that return JSON for each section.

Who calls each one?

Page 22: Engineering the New LinkedIn Profile

Groups Content Service

Connections Content Service

Profile Content Service

Client/Browser CDN

Load Balancer

SCDS

Fizzy

Profile Web App

Profile Content Service

Profile Web App

Connections Content Service

Groups Content Service

Dust/JS/CSS Server

Retrieve Data Models

/profile/view?id=32 top_card.tl, background.tl

/profile/topcard /profile/background

Fizzy - the UI aggregator

Page 23: Engineering the New LinkedIn Profile

Fizzy

●  Fizzy is an UI aggregator in 2 parts: o  Fizzy Server fetches the content you

want o  Fizzy Client renders it when ready

●  Your base page defines its structure and

which UI components it needs (called "embeds")

*Fizzy Server is an Apache Traffic Server Plugin, Fizzy Client is a JS library

Page 24: Engineering the New LinkedIn Profile

Profile's Embeds

Top Card Embed

Activity Embed

Embed

Embed

Embed

Embed

Yet another embed

Page 25: Engineering the New LinkedIn Profile

Profile's Embeds in code <html> <body> ... <div id=“wrapper”> <div id=“profile”> <script type=“embed” fs-id=“topcard” fs-uri=“/profile/topcard”/> <script type=“embed” fs-id=“background” fs-uri=“/profile/background”/> ... <script type=“embed” fs-id=“connections” fs-uri=“/profile/connections”/> </div> <div id=“insights”> <script type=“embed” fs-id=“people_you_may_know” fs-uri=“/profile/pymk”/> <script type=“embed” fs-id=“strength_meter” fs-uri=“/profile/strength”/> ... <script type=“embed” fs-id=“in_common” fs-uri=“/profile/incommon”/> </div> </div> </body> </html>

1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19

Page 26: Engineering the New LinkedIn Profile

Profile's Embeds in code <html> <body> ... <div id=“wrapper”> <div id=“profile”> <script type=“embed” fs-id=“topcard” fs-uri=“/profile/topcard”/> <script type=“embed” fs-id=“background” fs-uri=“/profile/background”/> ... <script type=“embed” fs-id=“connections” fs-uri=“/profile/connections”/> </div> <div id=“insights”> <script type=“embed” fs-id=“people_you_may_know” fs-uri=“/profile/pymk”/> <script type=“embed” fs-id=“strength_meter” fs-uri=“/profile/strength”/> ... <script type=“embed” fs-id=“in_common” fs-uri=“/profile/incommon”/> </div> </div> </body> </html>

1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19

Each embed specifies a Mapper endpoint that Fizzy will fetch

Page 27: Engineering the New LinkedIn Profile

Fizzy fetches data and sends it to the browser.

What's rendering the actual

markup?

Page 28: Engineering the New LinkedIn Profile

Groups Content Service

Connections Content Service

Profile Content Service

Client/Browser CDN

Load Balancer

SCDS

Fizzy

Profile Web App

Profile Content Service

Profile Web App

Connections Content Service

Groups Content Service

Dust/JS/CSS Server

Retrieve Data Models

/profile/view?id=32 top_card.tl, background.tl

/profile/topcard /profile/background

You guessed it... Dust client templates

Page 29: Engineering the New LinkedIn Profile

{Dust} client templates

●  LinkedIn's latest and greatest rendering layer

●  Logic-less client template language

●  Profile page made up of many* templates

●  Templates + JSON = full markup!

*over 400 actually

Page 30: Engineering the New LinkedIn Profile

{Dust} - what it looks like

<div id="top_card"> <h4>{name}</h4> <h5>{headline}</h5> {#info} <h6>{location} | {industry}</h6> {/info} </div>

{ "name": "Frank Stallone", "headline": "Actor and Less Famous Brother", "info":{ "location": "Hollywood", "industry": "Entertainment" } }

Page 31: Engineering the New LinkedIn Profile

{Dust} - why it's cool for profile

●  Cached markup in CDNs and browsers o  Members browse many profiles in a row

●  Very DRY and reusable o  Improved development speed

o  Templates within templates within templates... §  date range, degree badge, formatted summary field

●  Super easy to refresh a section on the page o  Re-render and replace o  Useful in pagination, inline searching, and inline

editing

Page 32: Engineering the New LinkedIn Profile

{Dust} on profile <hgroup> {>"tl/apps/profile/v2/embed/company_logo"/} <h4> <a href="{link_title_pivot}" name='title'>{title_highlight|s}</a> {?selfView} <span class="edit-tools"> <a class="edit-section">{i18n_Edit}</a> </span> {/selfView} </h4> <h5> {?companyName} {>"tl/apps/profile/v2/embed/company_link" track_param="prof-exp"/} {/companyName} </h5> </hgroup> <span class="experience-date-locale"> {>"tl/apps/profile/v2/partial/daterange"/} {! Location !} {@pre.fmt key="fmt_location" type="geo.region" value="{location}" render="false"/} {?fmt_location}<span class="locality">{fmt_location}</span> {:else} {?locationName} <span class="locality">{locationName}</span> {/locationName} {/fmt_location} </span> {>"tl/apps/profile/v2/partial/summary_field" _summary=summary/} {>"tl/apps/profile/v2/partial/associated_content" trkCodePrefix="exp"/}

Page 33: Engineering the New LinkedIn Profile

{Dust} on profile <hgroup> {>"tl/apps/profile/v2/embed/company_logo"/} <h4> <a href="{link_title_pivot}" name='title'>{title_highlight|s}</a> {?selfView} <span class="edit-tools"> <a class="edit-section">{i18n_Edit}</a> </span> {/selfView} </h4> <h5> {?companyName} {>"tl/apps/profile/v2/embed/company_link" track_param="prof-exp"/} {/companyName} </h5> </hgroup> <span class="experience-date-locale"> {>"tl/apps/profile/v2/partial/daterange"/} {! Location !} {@pre.fmt key="fmt_location" type="geo.region" value="{location}" render="false"/} {?fmt_location}<span class="locality">{fmt_location}</span> {:else} {?locationName} <span class="locality">{locationName}</span> {/locationName} {/fmt_location} </span> {>"tl/apps/profile/v2/partial/summary_field" _summary=summary/} {>"tl/apps/profile/v2/partial/associated_content" trkCodePrefix="exp"/}

Partial templates like this are used on almost all the background sections

Page 34: Engineering the New LinkedIn Profile

We have our Mappers, Fizzy, and Dust templates...

Let's bring it all together.

Page 35: Engineering the New LinkedIn Profile

Feature: Inline Editing

●  Dust + Mappers make this easy ●  Dust templates for view and edit

●  Mappers return just the data we need to refresh

"View" template

"Edit" template

Summary Section

Page 36: Engineering the New LinkedIn Profile

Feature: Inline Editing

●  Dust + Mappers make this easy ●  Dust templates for view and edit

●  Mappers return just the data we need to refresh

"View" template

"Edit" template

Summary Section When edit link is clicked, render "edit" template

Page 37: Engineering the New LinkedIn Profile

Feature: Inline Editing

●  Dust + Mappers make this easy ●  Dust templates for view and edit

●  Mappers return just the data we need to refresh

"View" template

"Edit" template

Summary Section

On submit, our endpoint sends back JSON, and we re-render

Page 38: Engineering the New LinkedIn Profile

Inline editing - Example

Either the "view" or "edit" template is shown at a time

Page 39: Engineering the New LinkedIn Profile

Inline editing sounds easy...

●  Issue: Profile data is highly coupled with different sections.

o  Adding/editing a position needs to be reflected on Top Card...

o  Deleting a project needs to also be removed from any associated position...

●  Solution: since we have mappers for each section,

we know which to re-fetch upon a save and refresh the respective templates

Page 40: Engineering the New LinkedIn Profile

What about pagination, search?

●  Same idea but easier (not coupled with other sections)

●  Mappers can take URL offset params

Switch out a partial template containing just list of profiles

Page 41: Engineering the New LinkedIn Profile

Let's talk performance

Page 42: Engineering the New LinkedIn Profile

High Performance Page

●  Profile is the most trafficked page on LinkedIn

●  Profile fetches 48+ different types of content on each page load o  This content results in 250+ total

downstream calls

Page 43: Engineering the New LinkedIn Profile

High Performance Page

●  Profile is the most trafficked page on LinkedIn

●  Profile fetches 48+ different types of content on each page load o  This content results in 250+ total

downstream calls

Bottom Line: We need to be fast, but need to consider downstream fanout

Page 44: Engineering the New LinkedIn Profile

High Performance - Parallel Requests

Fizzy

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

In the beginning, Profile had 15 embeds with 15 different endpoints

which is great for speed...

Page 45: Engineering the New LinkedIn Profile

High Performance - Parallel Requests

Fizzy

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

That's a lot of downstream calls, often requesting the same data too.

Content Service

Content Service

Page 46: Engineering the New LinkedIn Profile

High Performance - Parallel Requests

Fizzy

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

and those calls call more and more... until...

Page 47: Engineering the New LinkedIn Profile

High Performance - Parallel Requests

Fizzy

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Profile App

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

Content Service

aaaaand the site's down

Page 48: Engineering the New LinkedIn Profile

High Performance - Parallel Requests

●  Tradeoff: Speed vs Scalability

●  15 parallel calls will be fast but with Profile's load will take down site

Page 49: Engineering the New LinkedIn Profile

High Performance - Parallel Requests

●  Tradeoff: Speed vs Scalability

●  15 parallel calls will be fast but with Profile's load will take down site

Batched Endpoints to the rescue

Page 50: Engineering the New LinkedIn Profile

Batching Calls to Mappers

Originally, we had separate endpoints (URIs) for each embed

<html> <body> ... <div id=“wrapper”> <div id=“profile”> <script type=“embed” fs-id=“topcard” fs-uri=“/profile/topcard”/> <script type=“embed” fs-id=“background” fs-uri=“/profile/background”/> ... <script type=“embed” fs-id=“connections” fs-uri=“/profile/connections”/> </div> <div id=“insights”> <script type=“embed” fs-id=“peeps_you_may_know” fs-uri=“/profile/pymk”/> <script type=“embed” fs-id=“strength_meter” fs-uri=“/profile/strength”/> ... <script type=“embed” fs-id=“in_common” fs-uri=“/profile/incommon”/> </div> </div> </body> </html>

1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19

Page 51: Engineering the New LinkedIn Profile

Batching Calls to Mappers

We now tell framework to batch these two endpoints (Fizzy knows how to deliver the right data to the right embed)

<html> <body> ... <div id=“wrapper”> <div id=“profile”> <script type=“embed” fs-id=“topcard” fs-uri=“/profile/mappers?a=topcard,background”/> <script type=“embed” fs-id=“background” fs-uri=“/profile/mappers?a=topcard,background”/> ... <script type=“embed” fs-id=“connections” fs-uri=“/profile/connections”/> </div> <div id=“insights”> <script type=“embed” fs-id=“peeps_you_may_know” fs-uri=“/profile/pymk”/> <script type=“embed” fs-id=“strength_meter” fs-uri=“/profile/strength”/> ... <script type=“embed” fs-id=“in_common” fs-uri=“/profile/incommon”/> </div> </div> </body> </html>

1 2 3 4 5 6 7 8 9

10 11 12 13 14 15 16 17 18 19

Page 52: Engineering the New LinkedIn Profile

Batching Calls in Profile

●  Today, we use between 3-5 parallel requests to Profile

●  It's a good balance of speed vs. scalability

●  Batching requests that need the same data has the added benefit of less downstream calls

Page 53: Engineering the New LinkedIn Profile

Progressive Rendering With parallel fetch, Profile modules render when ready for improved perceived performance

Page 54: Engineering the New LinkedIn Profile

Progressive Rendering With parallel fetch, Profile modules render when ready for improved perceived performance

Page 55: Engineering the New LinkedIn Profile

How else to improve page load times?

Page 56: Engineering the New LinkedIn Profile

Profiles can be long...

Embed

Embed

Embed

Embed

Embed

Embed

Embed

Profile Page

Page 57: Engineering the New LinkedIn Profile

Optimize for Above the Fold

Embed

Embed

Embed

Embed

Embed

Embed

Embed

Profile Page

The fold

No need to render

No need to render or fetch

Page 58: Engineering the New LinkedIn Profile

Optimize for Above the Fold

●  The New Profile renders above the fold as fast as we can o  Deferring everything at the bottom o  Reduces the static content we need to initially

download

Page 59: Engineering the New LinkedIn Profile

Optimize for Above the Fold

●  The New Profile renders above the fold as fast as we can o  Deferring everything at the bottom o  Reduces the static content we need to initially

download

Sorry guys, we’ll get to you later

Page 60: Engineering the New LinkedIn Profile

Optimize for Above the Fold

●  The New Profile defers fetching the modules at the bottom of the page o  Improves server side assembly times o  Lowers payload and improves network time

o  Improves client rendering times

Page 61: Engineering the New LinkedIn Profile

Optimize for Above the Fold

●  The New Profile defers fetching the modules at the bottom of the page o  Improves server side assembly times o  Lowers payload and improves network time

o  Improves client rendering times

Go fetch!

Page 62: Engineering the New LinkedIn Profile

So we covered the product, technologies, and some

features... let's revisit our goals.

Page 63: Engineering the New LinkedIn Profile

Revisiting the New Profile Goals ●  Needs to surface your entire professional identity

o  Easily expose new data endpoints

●  Needs to be more interactive

o  Inline editing, searching

●  Needs to be

o  Fluid o  Flexible

o  Fast

Page 64: Engineering the New LinkedIn Profile

Revisiting the New Profile Goals ●  Needs to surface your entire professional identity

o  Easily expose new data endpoints (Mappers)

●  Needs to be more interactive

o  Inline editing, searching (Dust + Mappers)

●  Needs to be

o  Fluid (Fizzy progressive rendering) o  Flexible (Fizzy deferred rendering)

o  Fast (Fizzy parallel calls + defer fetch, batching Mappers, Dust template caching)

Page 65: Engineering the New LinkedIn Profile

Takeaways ●  Mappers for each module makes sense

o  You can combine and reuse with ease

o  A must if refreshing part of page

●  When structuring your page, start with many

embeds

o  You can control number of requests, when embeds are rendered, and when to fetch data

●  Many partial templates are a good thing

o  Allows you finer control over what to re-render

o  Improves developer speed

Page 66: Engineering the New LinkedIn Profile

Takeaways cont. ●  Make these technologies work for you

o  Our Mappers are structured to guarantee minimal downstream calls

o  Take advantage of decoupling rendering layer

with server side endpoints §  Engineers can build endpoints

§  Web devs can start the templates with mocked JSON data

Page 67: Engineering the New LinkedIn Profile

Questions?