[@IndeedEng] Building Indeed Resume Search

Post on 19-May-2015

2.648 views 3 download

Tags:

description

Video available: http://youtu.be/qcnP5gQGBaU Software engineer David Tulig will dive into the architecture of Indeed’s Resume Instant Search and our use of the Google Closure tools. David will explain how we write maintainable, efficient JavaScript components for Resume Instant Search and other Indeed products. He will discuss how we create templates that run on both client and server, providing fast initial page load time and search engine-friendly pages with the responsiveness of client-side rendering. Speaker: David Tulig is a software engineer on the Job Search team at Indeed. David has worked on employer, resume, and job search products during his 4 years at Indeed.

Transcript of [@IndeedEng] Building Indeed Resume Search

Building Indeed Resume SearchDavid Tulig

DAVID TULIGTECH LEADJOB SEARCH TEAM

I help people get jobs.

Jobseekers Job

Jobseekers Job

We learned from Job Search

The web evolved

PRINCIPLES

SimpleFastComprehensiveRelevant

PRINCIPLES

SimpleFastComprehensiveRelevant

GOALS

Responsive user experience

javas

javas

javascriptjavascript jqueryjavascript htmljavascript html css

javascript

javascriptjavascript jqueryjavascript htmljavascript html css

Maintainable code

Re-usable components

Separate presentation and logic

Fast

GOALS

Responsive UXMaintainable codeFast

Library support

Google Closure Tools

Closure Library

array asserts async color crypt cssom date disposable dom ds editor events format functions fx gears global graphics history i18n iter json locale math messaging module net positioning proto proto2 pubsub result soy spell stats storage string structs style testing text tweak ui userAgent vec

array asserts async color crypt cssom date disposable dom ds editor events format functions fx gears global graphics history i18n iter json locale math messaging module net positioning proto proto2 pubsub result soy spell stats storage string structs style testing text tweak ui userAgent vec

Modular and independent

Application

dom events style

Application

dom events style

arrayobject

Application

dom events style

arrayobject string

Application

dom events style

arrayobject string

Application

dom events style

arrayobject string

Application

dom

events

style

array

object

string

goog.provide('indeed.Search');

goog.require('goog.dom');goog.require('goog.events');

/* Search */indeed.Search.validateLocation =

function() {...};

goog.provide('indeed.Search');

goog.require('goog.dom');goog.require('goog.events');

/* Search */indeed.Search.validateLocation =

function() {...};

Application

dom events style

arrayobject string

Unit testing

Closure Templates

Closure Compiler

whitespace_only

simple_optimizations

advanced_optimizations

Type checking

Unused code removal

/** Alias for getElementById. If a DOM node ispassed in then we just return */goog.dom.getElement = function(element) {...};

/** Looks up elements by both tag and class name,using browser native functions */goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {...};

/** Returns an array of all the elementswith the provided className. */goog.dom.getElementsByClass = function(className, opt_el) {...};

/** Returns the first element with the provided className. */goog.dom.getElementByClass = function(className, opt_el) {...};

/** Alias for getElementById. If a DOM node ispassed in then we just return */goog.dom.getElement = function(element) {...};

/** Looks up elements by both tag and class name,using browser native functions */goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {...};

/** Returns the first element with the provided className. */goog.dom.getElementByClass = function(className, opt_el) {...};

/** Alias for getElementById. If a DOM node ispassed in then we just return */goog.dom.getElement = function(element) {...};

/** Looks up elements by both tag and class name,using browser native functions */goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {...};

/** Returns an array of all the elementswith the provided className. */goog.dom.getElementsByClass = function(className, opt_el) {...};

/** Returns the first element with the provided className. */goog.dom.getElementByClass = function(className, opt_el) {...};

/** Alias for getElementById. If a DOM node ispassed in then we just return */goog.dom.getElement = function(element) {...};

/** Looks up elements by both tag and class name,using browser native functions */goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {...};

/** Returns the first element with the provided className. */goog.dom.getElementByClass = function(className, opt_el) {...};

/** Alias for getElementById. If a DOM node ispassed in then we just return */goog.dom.getElement = function(element) {...};

/** Returns an array of all the elementswith the provided className. */goog.dom.getElementsByClass = function(className, opt_el) {...};

Aggressive renaming

Let's build Resume Search

Resume Search architecture

Browser Server

q=javasc

Browser Server

q=javasc

{ "suggestions" : [ "javascript", "javascript jquery", "javascript html", "javascript html css", "javascript ajax" ]}

AutoCompleteAutoComplete

Search

Preview

AutocompleteSearchPreview

LocationAutocomplete

Search Preview

QueryAutocomplete

A Possible Approach: Tightly Coupled Modules

Use events

{ "suggestions" : [ "javascript", "javascript jquery", "javascript html", "javascript html css", "javascript ajax" ]}

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

1. N

ew te

xt "j

avas

c"

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

2. Get suggestions

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

3. Suggestions

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

4. Render

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

6. Click on "javascript"

Autocomplete

Text HandlerDrop Down

HandlerRPC Handler

6. S

et te

xt to

"jav

ascr

ipt"

Search App

AutoComplete

Text HandlerDrop Down

Handler

Search

RPC Handler

Search App

AutoComplete

Text HandlerDrop Down

Handler

Search

RPC Handler

Awesome

Search App

AutoComplete

Text HandlerDrop Down

Handler

Search

RPC Handler

Search App

AutoComplete

Text HandlerDrop Down

Handler

Search

RPC Handler

Search App

AutoComplete

Text HandlerDrop Down

Handler

Search

RPC Handler

User interface

Templates

Server or client?

indeed.com/resumes?q=java

indeed.com/resumes?q=java

HTML

Time

indeed.com/resumes?q=java

LOAD JSHTML

Time

indeed.com/resumes?q=java

AJAX RESULTSLOAD JSHTML

indeed.com/resumes?q=java

JS REQ JS RESP AJAX REQ AJAX RESPHTML + DATA AJAX RESULTSLOAD JS

Time

indeed.com/resumes?q=java

JS REQ JS RESPHTML + DATA LOAD JS

indeed.com/resumes?q=java

HTML + RESULTS

Time

indeed.com/resumes?q=java

HTML + RESULTS JS REQ JS RESPLOAD JS

indeed.com/resumes?q=java

HTML + RESULTS JS RESPLOAD JS

Server or client?

Server AND client!

Don't repeat yourself

Closure Templates

template.soy

Closure Template Compilernew TofuRenderer()

template.js

one template. all requests.

/resumes?q=javascript Server Rendering

/resumes?q=javascript Server Rendering

Controller

/resumes?q=javascript

Controller

Model

Server Rendering

/resumes?q=javascript

Controller

Model

new TofuRenderer()

Server Rendering

/resumes?q=javascript

Controller

Model

new TofuRenderer()

HTML

Server Rendering

/instant?q=javascriptClient Rendering

/instant?q=javascript

Controller

Client Rendering

/instant?q=javascript

Controller

Model

Client Rendering

/instant?q=javascript

Controller

Model

JSON

Client Rendering

/instant?q=javascript

Controller

Model

template.js

JSON

Client Rendering

/instant?q=javascript

Controller

Model

template.js

JSON

Client Rendering

HTML

/resumes?q=javascript /instant?q=javascript

Controller

Model

new TofuRenderer()

HTML

template.js

JSON

Don't repeat yourself

Application

Application

Browser

Library

Browser

Library

Browser

Templates

Library

Browser

Templates Application

HTML LIBRARY TEMPLATES APPLICATION

Time

COMBINED

Time

HTML

COMPILED

Time

HTML

Application size

whitespace_only 126k

whitespace_only 126k

simple_optimizations 108k

whitespace_only 126k

simple_optimizations 108k

49k advanced_optimizations

RESUME SEARCH

Re-usable componentsResponsive UXFast initial load

[video of http://www.indeed.com/resumes]

We learned from Resume Search

jobsearch.js

jobsearch.js 41k

jobsearch.js 41k

jobsearch_v2.js 35k

6K MATTERS

6K MATTERS

6k reduction

6K MATTERS

6k reduction546 GB / month

6K MATTERS

6k reduction546 GB / month428 hours / month

david tuligtwitter: @dtulig

indeed.com/resumes

engineering.indeed.com