Lessons Learned while Building an iPhone Site

The Explore Page in the iPhone site

A few weeks ago we released a version of the Flickr site tailored specifically for the iPhone. Developing this site was very different from any other project I’ve worked on; there seems to be a new set of frontend rules for developing high-end mobile sites. A lot of the current best practices get thrown out the window in the quest for minimum page weight and fastest load times over slow cellular connections.

Here are a few of the lessons we learned (sometimes painfully) while developing this site.

1. Don’t Use a JavaScript Library or CSS Framework

This was one of the hardest things for me to come to terms with. I’m a huge fan of libraries, especially YUI, mostly because they allow me to spend my time creating new stuff instead of working around crazy browser quirks. But these libraries walk a fine line; by definition, they must work across a wide array of browsers and offer enough features to make them worth using. This means they potentially contain a lot of code that you don’t care about and won’t use. This code is dead weight to your site.

With such a high percentage of normal web users on broadband connections, we’ve gotten cavalier about what we can include in our pages. 250 KB of JavaScript or more isn’t uncommon for a large site these days. But for sites that are meant to be viewed over slow cellular connections like EDGE, 250 KB is an impossible amount of data. The only way to get the size of your JavaScript down is to selectively pull code out of libraries, and include only what you use. This means you can rip out code meant only for browsers that you won’t support (modular libraries like the new YUI 3.0 allow you to only include the code you use, preventing this problem somewhat).

The same goes for CSS. Frameworks make development faster and your final product more robust, but they, like the JavaScript libraries, include code for situations you won’t have to deal with. Every line in your CSS must be custom; each property must be scrutinized to ensure it’s needed.

2. Load Page Fragments Instead of Full Pages

Loading fragments saves 92.2% of the page size

When navigating through a site, most of what changes from page to page is the actual content; the JavaScript, CSS, header and footer stay mostly the same. We can use this to our advantage by only loading the part of each page that changes. We did this by hijacking all links of the page: when a link is clicked, we intercept the event, fetch the page fragment using Ajax, and insert the HTML into a new div. This has several benefits:

  • Since you control the entire life cycle of the page fetch, you can display loading indicators or a wireframe version of the page while new pages load
  • All pages that have been fetched will exist within the DOM; clicking the back button (or clicking on a link for a page that has already been fetched) results in an instantaneous page load
  • The page fragments are extremely small; ours are about 800 bytes (gzipped) on average

Using this system complicates your code a bit. You need JavaScript to handle the hijacking, the page fragment insertion, and the address bar hash changes (which allow the back and forward buttons to work normally). You also need your backend to recognize requests made with Ajax, and to only send the page content instead of the full HTML document. And lastly, if you want normal URLs to work, and each of your pages to be bookmarkable, you will need even more JavaScript.

Despite these downsides, the benefits can’t be ignored. The extra JavaScript code is a one-time cost, but the extra page content that we would have downloaded is saved for every page load. We found it was worth the complication and additional JS in order to dramatically reduce the time it took to load each page.

3. Don’t Build for Just One Device

It’s really tempting to build the site for just the iPhone: you can use modern CSS (including things like CSS3 selectors and transformations), you don’t have to hack around annoying browser quirks, and testing is extremely easy. But any single device, even one as ubiquitous as the iPhone, has a limited share of the mobile market, especially internationally. Rarely can you justify the cost of creating a one-off site for a very small number of your users.

Luckily the current generation of high-end mobile browsers is excellent in terms of support for modern features. Many phones use a WebKit derivative, including the iPhone, and Symbian and Android phones. Other phones either come with or can use Opera Mobile or the new mobile version of Firefox (called Fennec). For the most part, very few changes are needed in order to support these browsers.

Most of the differences lie in layout. It’s important to structure your pages around a grid that can expand as a percentage of the page width. This allows your layouts to work on many different screen sizes and orientations. The iPhone, for example, allows both landscape and portrait viewing styles, which have vastly different layout requirements. By using percentages, you can have the content fill the screen regardless of orientation. Another option is to detect viewport width and height, and use JavaScript to dynamically adjust classes based on those measurements (but we found this was overkill; CSS can handle most situations on its own).

4. Optimize Everything

The browsers on mobile devices operate under much stricter constraints than their desktop cousins. Slower CPUs, smaller amounts of memory, and smaller hard drives mean that less data can be cached. On the iPhone, for instance, only files smaller than 25 KB are cached. This puts very specific limits of the size of your files. For a large site like Flickr, 25 KB worth of JavaScript and CSS barely scratches the surface. To put our files under the limit, we ran everything through the YUI Compressor using the most aggressive settings. We ran all images through compression tools as well (we like pngout and Smushit), reducing each image file by an average of 40%. We also made heavy use of sprites, where possible.

In the end, we were able to go from 90+ second load times over EDGE to less than 7 for an empty cache experience. Using page fragments, we are able to load and display new pages in under a second (though the images in those pages take longer to load). These are not trivial gains, and make the difference between a good mobile experience and a one that is so awful the user gives up halfway through the page load.

5. Tell the User What is Happening

Once we hijacked all clicks actions in order to load page fragments, it wasn’t always clear to the user if anything was happening when they clicked on a link. This is especially true on touch devices, where it is difficult to know if the device even detected your action. To combat this problem, we added loading indicators to every link. These tell the user that something is happening, and reassures them that their action was detected. It also makes the pages seem to load much faster, since something is happening right away; if the indicators weren’t there, it would seem like nothing was happening for a few seconds, and then the page would load suddenly. In our testing, these indicators were the difference between a UI that seems snappy and responsive, and one that seemed slow and inconsistent.

Loading indicators

One Easy Option

The iUI framework implements a lot of these practices for you, and might be a good place to start in developing any mobile site (though keep in mind it was developed specifically for the iPhone). We found it especially useful in the early stages of development, though eventually we pulled it out and wrote custom code to run the site.

A Little About The Flickr Bike


A sum of its parts @ Yahoo! Video (for ppl using RSS readers)

Shanan’s been driving me insane riding the Flickr Bike around the office. If you don’t know about the Flickr Bike, in a nutshell we have 20 purple bikes tricked out with Nokia N95s geotagging photos and uploading them to Flickr (of course) as people cycle round, there’s a video (Flickr’s handlebar cameras) over on cnet, and a frankly awesome write up on Lifehacker.

This being the Dev Blog we’re interested in looking under the hood, to use an awful metaphor, and finding out how things work. Fortunately for us Josh wrote up a blog post last week, including links to source code and all that good stuff, meaning I can simple point to that, Huzzah for the internet!

Read techy stuff over at ~~> Coding a Networked Bike

If you’re more of a moving pictures with sounds than words person, then Lifehacker has 6 videos up that are worth a watch: The Making of the Flickr Bikes. The seconds one of which I included at the start of this blog post.

Oh and fyi, they’re Electra bikes painted #7B0099 (Pantone 2602 C).

5 Questions for Jim Bumgardner

We’ve been keeping a careful eye on our Sister Blog to see what they’re up to. Something that’s particularly caught our eye is "5 Questions ", asking the same 5 questions to the Flickrverse, with the last question being who we should ask next. And so, we hope, it goes on and on.

Banana-bub

This is our version, asking questions of those that develop, hack and fiddle with Flickr in new and interesting ways. Of course we couldn’t start with anyone else but KrazyDad (aka Jim Bumgardner).

Jim founded the Flickr Hacks group back in the day, a great place to hang out and ask question if you want to learn how to bend Flickr to your will. In 2006 he also coauthored the Flickr Hacks book for O’Reilly and happily for us he hasn’t stopped tinkering with Flickr yet.

So, without any further ado, 5 Questions for Jim Bumgardner:

1. What are you currently building that integrates with Flickr, or a past favorite that you think is cool, neat, popular and worth telling folks about? Or both.

Jim: It seems like I’m always building something that integrates Flickr. A recent favorite is this interactive mosaic that shows the most interesting photos of the week.

The photos are arranged to form a spiral, a form that appears quite frequently in my work.

www.coverpop.com/pop/flickr_interesting
Coverpop: Most Interesting Photos of the Week

I have a "cron job" which runs on one of my computers at home, which updates this mosaic every week, so the photos in it are always fresh. Incidentally, I prefer to call this process a "cron joy." Oh, nerd humor…

2. What are the best tricks or tips you’ve learned working with the Flickr API?

Jim: I think every Flickr hackr should have access to a powerful high level
graphics library. My library of choice is ImageMagick combined with the Perl programming language (it also works nicely with Ruby), but the GD library, which works with various languages, and PIL, for Python, are also good.

I not only use ImageMagick for building mosaics and graphs, but also for "under the hood" kinds of things, like measuring the average colors of photos for the Colr Pickr (see below).

3. As a Flickr developer what would you like to see Flickr do more of and why?

Jim: One of the very first Flickr hacks I made was the Colr Pickr

www.krazydad.com/colrpickr/

…which allows photos to be selected by color. Since that appeared, I’ve worked on, and seen some fancier variations on the concept, that allow larger quantities of Flickr photos to be selected using multiple colors. But all these systems, require that thousands or even millions of thumbnails be downloaded and analyzed for color. This is because Flickr does not supply "average color" information in its APIs, and cannot provide the color search functionality that this data would enable.

flickr Colr Pickr

I would like to see Flickr provide, via it’s APIs, the three most common colors in each photo (using cluster analysis), and provide a way to search for photos which match one, two, or three colors. These parameters, similar to geocode searches, would need to be combined with some other search parameters, such as tags, to narrow
the field down.

A feature like this would be a godsend to designers. I’ve got sample code for the color analysis, if anyone’s interested… :)

4. What excites you about Flick and hacking? What do you think you’ll build next or would like someone else to build so you don’t have to?

Jim: One thing that excites me is the ability to access large quantities of photos that contain valuable metadata, such as the time the photo was taken, or the geocoded location. I used the ‘date taken’ data to construct this very cool graph of sunsets:

A year of sunsets

While most digital cameras store the time within photos, these days, not enough of them automatically store the location. We have to rely on photographers adding the geocoded information manually, and sadly, not enough of them are geeky enough to do it. I’m looking forward to the day, a few years from now, when most of the new photos on Flickr will also contain geocoded information, as this will enable me to make apps which enable a kind of instant photo-journalism of heavily photographed events, such as rallies and parades. We’re seeing the beginnings of these kind of apps now, but we’re barely scratching the surface.

5. Besides your own, what Flickr projects and hacks do you use on a regular basis? Who should we interview next?

mc-50 map of FlickrLand: flickr's social network

Jim: GustavoG has made some amazing graphs which exploit and illustrate the Flickr social network.

Dan: Thank you, Jim. Next up for our thrilling installment of 5 Questions, GustavoG .

Images from krazydad / jbum , earthhopper and GustavoG.

Flickr Digs Ganglia

A number of people have asked us: where did the pretty graphs from last week’s post come from?

My Day

The answer: Ganglia.

It takes a lot of hardware and software to make a site like Flickr run smoothly. The operations team is responsible for scaling up our monitoring platform to collect all of the metrics we need to make informed decisions about where and when to add new hosts and how urgently, understand how different types of hardware perform with similar real life workloads, determine bottlenecks, and more. Flickr uses Ganglia to collect, aggregate, and visualize those metrics.

So, what is Ganglia? Briefly, "Ganglia is a scalable distributed monitoring system for high-performance computing systems such as clusters and grids"* originally developed at the University of California, Berkeley. Ganglia is typically run by administrators of High Performance Clusters, large groups of machines working together to complete tasks. While we have some machines organized into the traditional cluster configuration, for example for log crunching, we simply define a cluster under Ganglia as a group of machines that do similar things but don’t necessarily interact with one another. For example, all of our web servers in each site are one cluster and all caches another. Our databases are broken up into multiple clusters based on functionality. This way, boxes that should be doing the same kind of work can be easily compared against one another.

Once Ganglia is up and running you’ll see a number of system level statistics reported by each node. You can also easily report custom metrics and have them appear on graphs along with the built in metrics. Before the latest release (the 3.1.x line), this meant some code that calls gmetric and entry in the crontab for scheduling execution of that code. Ganglia 3.1 offers an additional facility for injecting custom metrics that easy to use and offers some additional power and flexibility over the gmetric + cron approach.

For more information on how Ganglia works, see the excellent references on the Ganglia Wiki . The community is active and very helpful.

If you’re just getting started with Ganglia, here are some pointers to save you headaches later:

  • Don’t use the default multicast address 239.2.11.71 for any clusters. You will start gmond without a config file and it will join that first cluster you defined and you will not be happy that your summary graphs are messy.
  • Do put your RRDs on a ramdisk/tmpfs. Your disk(s) will thank you. Don’t forget to setup a job to sync those files to some persistent storage periodically – we do it every 10 minutes.
  • Do consider the frequency of data collection and how long you need to keep the data around (and at what resolution). Do you need to know what the 5 minute load average was on your hosts one year ago today (maybe in relation to other metrics)? Do you need to know how many uploads per second those hosts were doing then (certainly)? While individual metric stores can be resized to keep data around for various amounts of time, typically it’s easier to find the least common denominator – the largest amount of time you think you need this information for – and set that in gmetad.conf. The default gmetad config stores data as:
    • 15 second average for about an hour
    • 6 minute average for about a day
    • 42 minute average for about a week
    • 2 hour 48 minute average for about a month
    • 1 day average for about a year

Once you’re up and running with Ganglia you’ll have access to something like the graph from the last post, what we call a stacked graph. Stacked graphs are intended to provide a quick overview of the relative performance of each host in a cluster. The code has been submitted to the Ganglia project for all to use.

Check out http://ganglia.info for more information and stay tuned for more posts about how Flickr uses Ganglia.

What’s in a Resource?

“Flickr is incredibly lucky, because the core of what we do is focused around images.”

If you’ve ever heard me talk about Internationalizing Flickr, you’ve probably heard me say those words. And it’s true – more than almost any other website, we deal primarily with content which is language-agnostic (and, to a great extent, culturally agnostic).

No matter where we live or what language we speak, our reactions to, say, fluffy kittens have a remarkably similar range (being, approximately, “aww”, “ew” or “achoo!”…)

When we first began to define what Flickr’s international incarnation would look like, our primary concern was preserving this global, cross-cultural feeling – a sense that our members’ photos and our visitors come from all over the world, but that the images on Flickr can “speak” to anyone.

It’s for that reason that Flickr isn’t divided into national silos – there’s no Flickr France, Flickr Brazil or Flickr USA. Much like Highlander, there can be only one Flickr, and it happens to be accessible in a multi-lingual interface.

All well and good, so far.

But the biggest issue I wrestled with (and occasionally still do) was what to do with the site’s URLs. The structure we planned for the site required us to take a definite position on a philosophical issue – what, in actual fact, constitutes the “Resource” defined by the Uniform Resource Locator?

There are two possible schools of thought on this – one which would argue that the photo page http://www.flickr.com/photos/hitherto/257018778/ with a French interface is materially different to the same page when presented with an English interface. The French page, we might argue, should be named http://fr.flickr.com/photos/hitherto/257018778/, http://www.flickr.com/fr-FR/photos/hitherto/257018778/ or something similar.

On the other hand (and, in fact, the hand we eventually chose), the real “resource” here is the photo (and associated metadata) which the page points to – the interface is immaterial to the content we’re presenting. The big advantage of this approach, especially in a multi-lingual world, is that everyone gets the experience best suited to them.

A Korean friend, for example, can send me a link to a photo and I will see it on Flickr with the English interface familiar to me. She, meanwhile, can happily browse Flickr in Korean.

Things perhaps get murkier if we consider other areas of the site – the FAQs at http://flickr.com/help/faq/, for example. Here, all the content is provided by Flickr, and since all of it is in a particular visitor’s chosen language, the entire resource is different.

Even here, though, the principle can be made to apply. If an English-speaking German friend asks where they can get help on Flickr, I don’t have to know which language they prefer to view the site in; I can just point them to the FAQ page, and they will have access to the resource (“help with Flickr”) which they needed.

Now, admittedly, working for a large multi-national company puts me in contact with more than my fair share of people who speak multiple languages, so maybe this matters more to me than to most people. But as our society grows more connected and more mobile, I like to think that the incidences of these kinds of cross-cultural exchanges will only grow.

The biggest downside to our current URL approach comes when search engines try to index our content. Since we don’t have language-specific URLs (and search engine crawlers aren’t in the habit of changing their language settings and retaining the necessary cookies), everything which search engines index on Flickr comes from our English-language interface.

As it happens, depending on how smart the search engines are feeling, this isn’t too much of a problem – we do try to surface photo titles and descriptions so that the abstracts make sense. Still, the results returned by Yahoo! and Google for “Buzios” (a beach resort peninsula near Rio in Brazil) give some idea of the nature of the problem.

Occasionally, when I’m hit by a case of perfectionism, such things keep me awake at night. And I’m sure that someone, somewhere is wailing and gnashing their teeth over how “Flickr are doing URLs wrong”.

All in all, though, I think we made the right decision.

Flickr Engineers Do It Offline

It seems that using queuing systems in web apps is the new hottness . While the basic idea itself certainly isn’t new, its application to modern, large, scalable sites seems to be. At the very least, it’s something that deserves talking about — so here’s how Flickr does it, to the tune of 11 million tasks a day.

But first, a use case! Every time you upload a photo to Flickr, we need to tell three different classes of people about it: 1) You, 2) Your contacts, 3) Everyone else. In a basic application, we’d have a table of your photos and a table containing people who call you a contact. A simple join of these tables, every time someone checks the latest photos from their contacts, works fine. If you’re browsing around everyone’s photos or doing a search, you can just check the single photos table.

Obviously not going to fly here.

For scale, Flickr separates these three lookups into three different places. When you upload that photo, we immediately tell you about it, and get out of your way so you can go off and marvel at it. We don’t make you wait while we tell your contacts about it, insert it into the search system, notify third-party partners, etc. Instead, we insert several jobs into our queueing system to do these steps "later". In practice, every single one of these actions subsequently takes place within 15 seconds of your upload while you’re free to do something else.

Currently, we have ten job queues running in parallel. At creation, each job is randomly assigned a queue and inserted — usually at the end of the queue, but it is possible for certain high-priority tasks to jump to the front. The queues each have a master process running on dedicated hardware. The master is responsible for fetching jobs from the queue, marking them as in-process, giving them to local children to do the work, and marking them as complete when the child is finished. Each task has an ID, task type, arguments, status, create/update date, retry count, and priority. Tasks detect their own errors and either halt or put themselves back in the queue for retry later, when hopefully things have gotten better. Tasks also lock against the objects they’re running on (like accounts or groups) to make sure that other tasks don’t stomp on their data. Masters have an in-memory copy of the queue for speed, but it’s backed by mysql for persistent storage, so we never lose a task.

There are plenty of off-the-shelf popular messaging servers out there . We wrote our own, of course. This isn’t just because we think we’re hot shit, there are also practical reasons. The primary one was that we didn’t need yet another piece of complicated architecture to maintain. The other was that for consistency and maintainability, the queue needed to run on the exact same code as the rest of the site (yes, that means our queuing system is written in php). This makes bug fixes and hacking easier, since we only need to do them in one place. So every time we deploy , the masters detect that, tell all their children to cleanly finish up what they’re doing, and restart themselves to take advantage of the latest and greatest.

It’s not just boring-old notifications, privacy changes, deletes, and denormalization calculations for our queue, no. The system also makes it easy for us to write backfills that automatically parallelize, detect errors, retry, and backoff from overloaded servers. As an essential part of the move-data-to-another-cluster pattern and the omg-we’ve-been-writing-the-wrong-value-there bug fix, this is quickly becoming the most common use of our queue.

The Flickr engineering team is obsessed with making pages load as quickly as possible. To that end, we’re refactoring large amounts of our code to do only the essential work up front, and rely on our queueing system to do the rest.

Open! Hack! Day!

Once again Yahoo! is opening its doors to a horde of unwashed hackers. You should join us!

In case you missed last years Beck-filled extravaganza, Open Hack Day gives developers 24 hours to build a cool hack and demonstrate it to a packed audience and celebrity judges.

Better yet, some of the Flickr staffers will be on hand to answer any of your burning API questions and judge the Best Flickr Hack category (and yes, there are prizes).

Open Hack Day kicks off Friday with talks on Yahoo! APIs and technologies. Flickr-related talks include:

  • Getting Started with the Flickr API – Friday, Sept. 12 from 11:00am to 11:50am
  • Building a Purple Pedal GPS-Flickr Bike – Friday, Sept. 12 from 12:00pm to 12:50pm

It runs from Friday, Sept. 12 to Saturday, Sept. 13, at Yahoo! HQ in Sunnyvale, CA. If you’re interested, go to the Open Hack Day website to register.

Machine Tags, last.fm and Rock’n’Roll

You Rock, go to gigs and take photos, yes?

Bloc Party Photo (in a Photo)

Love last.fm too?

Yeah well, so do we. Turns out they love us as well. For over a year now they’ve been encouraging their users to machine tag flickr photos with last.fm event ids. This is what Martin had to say …

“As a geek I’m quite intrigued by Flickr’s machine tags feature we’re using to create this Last.fm/Flickr integration — it can become the basis for a number of interesting Flickr tools, and I’m confident people will come up with all kind of great ideas. (I’m personally waiting for someone to develop a Flickr tool to automatically geotag your event photos based on the venue address provided by Last.fm).”

(It takes us a while, but we’ll probably get round to that geotagging thing ‘soon’ Martin)

Here’s how they do it; from a specific event page on last.fm you’ll see this …

last fm help

… telling you which machine tag to use.

You took some photos at the gig? Well then, throw the tag in there and computers will automatically do the rest. From last.fm’s end, they grab the photos from flickr to show on each event page … it’s also a great way to find other people who were at the same gig as you!

From our end (as of a few weeks ago thanks to Cal) it’ll look like this …

last.fm machine tag

… a rather fetching last.fm icon, giving you the badge of honor telling everyone that you were really there and therefore how you loved LCD Soundsystem before everyone else.

If its an event we know about then we’ll already have the name. If its a brand spanking new event, we’ll get our system to talk to last.fm’s system, last.fm’s system will invite our system in for coffee, our system will play hard to get for a while, and then in the morning over fried eggs and bacon our system will have the new event name (honestly this is how it works, you should Cal’s code!).

So how many photos are tagged with last.fm events? Well around 621,793 last time I checked.

See more photos from Heineken Open’er Festival 2007 (last.fm).

Photo by alex-pl