« December 2012 | Main | February 2013 »

January 2013


Mission: Beyond The Mobile Browser

This post comes from Arnaud B., an engineer who likes to look after Yelp's mobile site, deal with mobile browsers and curse at them. Here he breaks down two JavaScript tricks used at Yelp to deliver a top-notch mobile site experience. Read on for the full explanation!

Last year we published an article explaining the reasoning behind the birth of our mobile site, Mission: Mobile Makeover. Since then, we’ve been hard at work, dedicated to improving this new Yelp property and transform it into an app-like experience. Ideally? Forget about your browser, and keep on Yelpin’!

Today, we’ll focus on some of the work we’ve accomplished during the past year or so. We’ll talk about two technical challenges faced lately: how to boost interaction speed on mobile, and how to make the most out of the screen sitting in your pocket.

Boosting Interaction Speed

Clicks are a core concept on the web because it drives most form interactions and navigation. To support double clicking, mobile browsers have no choice but to wait a short amount of time (typically, around 300ms) after your finger has lifted off the screen to fire a “click” event.

Since Yelp’s mobile site doesn’t require double clicks this behavior is actually detrimental to us: it makes interactions and navigation feel slower. That’s why we decided to boost interaction speed with a global shim that mitigates the delay that a user typically experiences between the moment when her finger lifts off the screen and the moment when a “click” event fires in JavaScript.

The general principle behind the change is:
If a user triggers a “touchstart” event at point A, followed by a “touchend” event at point B and if the two points are not far away from each other (both in distance and time), then it’s safe to assume we have a click intent. Otherwise, the intent was a drag, or something entirely different...but definitely not a click!

To accomplish this, we listen to “touchstart”, “touchmove” and “touchend” events. If we spot a click intent when a “touchend” event is fired, we immediately trigger a “click” event via JavaScript and prevent the default action. As a result, the “click” event fires ~300ms earlier than it would otherwise have.

If we fail to spot a click intent (maybe “touchstart” and “touchend” events were too far away from each other in distance, which means we have a dragging movement), we simply let the browser handle things for us.


Quick notes about this:

  1. This idea is not new. Google has a really good article about it, mobile HTML5Boilerplate has fast buttons in its JavaScript helpers, and the Financial times app recently open sourced their solution for faster clicks on touch-enabled devices.
  2. Not all devices have this problem. In our experiments, we noticed that the latest Android devices handle touch events and clicks much faster than they used to.
  3. If you want to implement fast buttons on your own, we highly suggest that you develop in Chrome Canary. This browser lets you emulate touch events, and also override a bunch of browser properties like the browser’s UA, width, height, etc. For example, here’s a screenshot of the setup used at Yelp to iterate on faster clicks.


Above is a picture of search suggestions on Yelp’s mobile site. This is the quintessential example of a feature that needs fast interactions to exist. With delayed clicks (on form inputs and suggestions) the experience would feel sloppy and awkward. With faster clicks, we’re getting closer to an app-like experience on Yelp’s mobile site.

Making The Most Out Of Small Screens

Let’s take a look at our search map page:


This page has several fixed height parts (3 action bars, search form) and one dynamic height part (the map).
Since our mobile site is going to be displayed on a small screen, we have to take advantage of every pixel we can get. That yields two independent goals:
  1. Get as many available pixels as we can 
  2. Use 100% of the available space to draw content

The first goal is difficult because the available space to display your Web app is typically dependent on the OS. When working inside a browser, there’s only one thing you can kick out of the way: the browser chrome (containing the URL bar). Getting rid of it is pretty simple: we have to tell the browser to scroll to the top of the page. It’s a well-known trick, and the code fits on one line:
window.scrollTo(0, 0);
There is a catch though: Android won’t work with the code above, and requires a slightly different version:
// For Android
window.scrollTo(0, 1);
Now comes in the hard part: we have to draw our layout (containing both fixed and flexible height parts) on the available screen. The only flexible part is the map so it’s going to fill whatever space we have left after placing the fixed height elements:
var mapHeight = screenHeight - totalFixedHeightsFromOtherElements;
We know the value of  totalFixedHeightFromOtherElements. However we don’t know screenHeight. How tall is the screen exactly? How can we do that in JavaScript? Well, there are a lot of different APIs out there. See for yourself:
  • window.innerHeight
  • window.outerHeight
  • screen.height
  • screeen.availHeight
  • pageYOffset
  • document.body.clientHeight
  • document.body.offsetHeight
  • document.documentElement.offsetHeight
  • document.documentElement.clientHeight
Given the plethora of options available we took several devices, did some testing and iterated to come up with a solution. The code we are using right now looks something like this:
var width = screen.width;
var height = screen.height;
if (screen.width > window.innerWidth
  height = document.documentElement.clientHeight;
  width = document.documentElement.clientWidth;

This code is not pretty to look at but it solves the problem at hand — client-side screen size detection — pretty well for us. Let’s see why.

First, we found that screen.width/height was the most consistent API out there considering the devices we support (iOS and Android > 2.2). If you want to scare yourself, please have a look at the massive testing table James Pearce assembled! You will get a sense of how fragmented mobile devices/browsers are these days.

The code snippet above solves another problem: some devices report screen.width/height in physical pixels where others report it in CSS pixels. This matters for devices with double density display. For instance the Nexus 7 reports a screen.height of 1280px.

However, document.documentElement.clientWidth always reports a value in CSS pixels, so we fall back to that API if we spot that screen.width/height is giving us a bigger value than expected.

Why don’t we use document.documentElement.clientWidth all the time then? You guessed it already: it’s not accurate enough, for instance on iOS (it reports 356px instead of the expected 480px because of the browser and system bars).

As you can see this complex problem doesn’t have a definite answer. We’re still iterating on our solution at the time of writing.

By the way you’ll probably find surprising things if you fire up real devices. For instance did you know Nexus7’s reported devicePixelRatio was 1.3250000476837158? Yup, that’s insane.

Going forward

The mobile site is still a young Yelp property. Barely out of its infancy, actually. Since its introduction we’ve been busy working on delivering the best experience for small screens, trying to make you forget you are in a browser.

Fostering faster touch interaction, giving better feedback, making the most out of every available pixel, taking advantage of new HTML5 APIs... this is barely scraping the surface of the ideas we have to improve our mobile site going forward.

We are truly excited about what’s ahead. If you are too, you should think about joining us —we’re hiring ;)


Bringing Health Inspection Scores to Yelp

Our post today is by Will L., an engineering intern on one of Yelp’s backend teams this past fall. Will walks us through the challenges of bringing restaurant health inspection scores to Yelp, a feature we announced today at the United States Conference of Mayors in Washington, DC.

As you may have seen on our official blog, we are very excited about our initial release of the Local Inspection Value-Entry Specification (LIVES). LIVES is an open data standard crafted by Yelp in partnership with the cities of San Francisco and New York to allow municipalities to publish restaurant health inspection information in a machine-readable format.

In this post, we want to take you behind the scenes and give you an overview of all the steps and actions that have happened from getting the standard off the ground to having all of the health inspection information showing up on Yelp.

Inspection page_sf (1)

The Local Inspection Value-Entry Specification was first drafted by Yelp engineer John Boiles (also known at Yelp for his Kinect hacking and the legendary KegMate) in mid-June 2012 and was followed by several collaborative revisions with key members of city health departments. Individuals within the cities of San Francisco, New York, and Philadelphia were instrumental to the process of refining the standard with their domain expertise and feedback. On January 9th, 2013, the latest version (1.0) was published.

A LIVES feed contains several comma-separated value (CSV) files to encapsulate the feed data in easy-to-read textual representation: businesses (businesses.csv), inspections (inspections.csv), related violations (violations.csv), score mappings for municipality-specific conventions (legend.csv), and finally data about the feed itself (feed_info.csv). Below is a snapshot of a portion of businesses.csv taken from the LIVES feed provided by the City of San Francisco:

Once the specification began shaping up at the beginning of October, a team of engineers at Yelp started to build a system to process and display LIVES data on our site. The first step was to come up with a scalable and maintainable system based on the requirements and constraints of the standard. While the LIVES standard is currently in use by two cities, Yelp is calling upon municipalities all across the US to share their health inspection data. As such, scalability played a critical role in our design process.

One of the more interesting and challenging aspects of the project centered around matching up a city’s record for a business to its equivalent on Yelp. While this may sound simple at first, it proves technically challenging when you realize cities are more interested in the legal representation of a business whereas Yelp focuses on what you would see if you were standing in front of the business on the street. For example, Starbucks may register itself as “Starbucks Coffee Company” with the City of San Francisco but will show up as just “Starbucks” on Yelp. Similar problems arise with addresses and phone numbers, all of which are attributes we use to help pinpoint the right business on Yelp (e.g., a chain might use a central number for registrations but have its individual numbers on Yelp).

While matching a set of data to a business is something we do routinely here at Yelp (after all, a search on Yelp is a very similar problem), the stakes for this project were much higher, especially in regards to false positives when matching. Just imagine how a 5-star restaurant with a perfect health record would feel if we incorrectly associated a failing inspection with their profile on Yelp.

To fine tune our matching, we ran several sample data sets from San Francisco and New York City through our tools and evaluated our results, paying particular attention to false negatives and false positives. Through a combination of normalization of the raw data from the municipalities and tweaks to how we weigh each piece of data (name, address, and phone number), we were able to dramatically minimize the number of false positives. Matching business records is never a completed project, however, so we’re constantly collecting metrics on how it’s performing with new data sets and tweaking its algorithms and weights appropriately.

Once we had all of the various implementation pieces glued together, the last step was to implement a rollout strategy. At Yelp, we’ve developed several tools to assist in this process to limit the exposure of a new feature. We’re able to release a feature to our internal users only, expose it to only a certain portion of public traffic, or whitelist the feature for certain businesses only. By combining all of these, we’re able to iterate and deploy features quickly all while keeping risks low.

We still have a lot of work to do with LIVES. Besides continuing our gradual rollout of the feature, our priority is to advocate for the adoption of the standard with municipalities so that more health inspection data is available publicly and can be displayed on Yelp. Since LIVES is an open standard, this not only benefits consumers wondering if that food stand on the corner of the street is a good choice; it also allows other organizations, such as research institutions, to use this data to spot trends and perhaps prevent future foodborne illness outbreaks. We’re equally interested in this data and plan on looking at how the average scores evolve across cities as we make this data more readily available to consumers like you. LIVES was one of Yelp’s first forays into developing an open standard. We’re definitely hooked and look forward to working with more local governments in the future to iterate on this standard and help share the wealth of information they have on local businesses.


More January Events at Yelp

We host several external meetup groups every month, but sometimes, they want us to present, too! SF Hadoop Users invited Sudarshan Gaikaiwair, one of our Yelptastic engineers to speak about mrjob, our MapReduce framework.

mrjob is an open source project that runs on top of Hadoop. While mrjob can also be used with your own hadoop cluster, it is great for getting started very quickly with Amazon's EMR offering. mrjob allows programmers with very little Hadoop or EMR experience to quickly write scripts that can process terabytes of logs on hundreds of machines.

This talk covers the basics of MapReduce and presents code examples of how to write mrjob programs based on the problems Yelp faces.

Tech Talk: SF Hadoop Users Meetup with Sudarshan G. from Yelp Studios on Vimeo.

Coming up in the next few days will be several exciting meetups, including a Python developer “speed dating” event! Don’t worry, it’s strictly professional, courtesy of HackerX. We work closely with a variety of meetup groups all with the intention of providing our guests with great learning experiences. Stay tuned to learn about TIZEN, Revitalizing Unloved Devices, Data Visualization and much more!


January Events at Yelp!

Want to know what widows, rivers, and pig bristles have to do with typography?  Listen to Carolina de Bartolo (@carodebartolo) explain why Typography Matters in the video below!  Designers + Geeks is an engaging meetup that covers everything from beautiful architecture to mobile development.  At an event hosted at Yelp in November, Carolina covered topics ranging from the history of typography to practical advice on choosing fonts for your project.


Designers + Geeks is just one of the many community events that we host in our spacious 10th floor lounge.  But fair warning: once you attend one event here, with our view of MOMA, two kegs, drinks, and snacks,  you may end up wanting to attend all our events!  Here’s what’s in the pipeline for the first half of January:

See you at Yelp!