code posts:

Wednesday, July 7th 2010

Which loads faster?

I’m pleased as punch to announce whichloadsfaster.com, an open-source web performance tool where pages compete head-to-head in your browser to see who’s fastest!

It started as an off-hand remark by my buddy, Ricardo: “You should make a tool that can load two web pages side by side so you can see which one loads faster.” I backpedaled a little at first, the way an overworked programmer does when someone suggests sticking another iron in the fire: “Um, I don’t know if that would really work out without writing a browser plugin.” This, of course, meant something more like, “Dude, I’m lazy, and that sounds like a lot of work just to make another toy for our sales and marketing guys.”

Ricardo had me though, it would be cool to load two pages side-by-side and see the differences in real time. Dan, one of the aforementioned sales and marketing guys, was grinning next to us, enthused in an infectious way we hadn’t seen nearly enough of since the arrival of his newborn. “Ok guys,” I entreated, “maybe we can do it with iframes. I’ve been having a lot of fun with jQuery lately, so let me play around with it and see what I come up with.”

It was a lot of fun to see two pages race against each other! Performance testing can be rather bland—staring at waterfalls, repeating tests—but watching a competition seems to be one of those things that humans are hard-wired to enjoy, and it tickled me like Elmo to think that I could take something I’m passionate about—web performance—and make it a bit more appealing!

Don’t get me wrong, there are many good tools for web performance testing (notably, the open source webpagetest.org and showslow.com, which I will be working to integrate), but there are a few areas where I think whichloadsfaster adds some wicked weapons to the web performance warchest:

  1. Competition. Since it’s easy to pit pages against each other, it becomes natural to check the performance of your most (and least) favorite sites, adding to the argument that speed is a competitive advantage.
  2. Testing in real time, while you watch. This has a big impact factor because a “real” test holds your attention.
  3. Support for many browsers (IE 8/7/6, Firefox 3.0+, Chrome, Safari, most of mobile WebKit). Because of the detailed and invasive nature of performance testing, most performance tools are browser-specific. We make the tradeoff of collecting fewer events for the convenience of running the tool in every browser with no install.
  4. Finally, there is a sharing link that developers can send out to run on their friends’ browsers. This feature is something I hope to develop further by adding a beacon API to automatically retrieve the results (see below).

Moving forward, I would like to improve whichloadsfaster in three major areas:

  1. Automation of test results. The plan is to make a beacon API and a couple of example servers to collect the data (php, rails, django). This way, developers can just send out the link and sit back and see their performance results roll in across different locations and browsers. Fully automated testing would be as simple as causing a remote test machine to load the URL you want with your desired browser. This could be done with selenium or a similar library, or possibly even built into whichloadsfaster (have to work around caching issues).
  2. User perception testing. Since we’re right in front of the user, we can ask them which page appeared to load faster and get a better handle on that ephemeral but most important metric, time to first interaction. I’m really excited about this one!
  3. Integration with other tools. I plan to add a link to compare your pages on webpagetest.org using the video film strip feature, and also to test the individual pages in the waterfall tool. Since this project is about spreading the web performance gospel, I’m open to linking to any useful performance tool.

A call for help

The one problem that really bugs me is that whichloadsfaster doesn’t play well with sites that try to break out of the iframe. This includes major sites that users want to compare, like myspace, twitter and nytimes.com. It’s a terrible user experience to type in one of these and watch it unexpectedly take over the screen. I’m completely sympathetic with using framebusting to avoid clickjacking attacks, but I truly think this project is a legitimate reason to frame a site, and I want to create an excellent user experience (I mean, have you noticed the keyboard shortcuts?).

I’ve successfully tested a framebuster-buster that prevents sites from breaking out, but one of the side effects is that it also prevents outgoing links. In the end, the users should at least know what is going on and why the site can’t be compared. I’ll continue to work on this, but if you or a developer you know has expertise in this area, I would love suggestions and advice via email or the github issue.

Thanks for your feedback and involvement. Here’s to making the web just work for everybody on the planet!

Tuesday, February 16th 2010

The FastSoft engineers often give presentations to bring each-other up to date on technical issues. These presentations are appropriately called “speedups” (get it? fastsoft? speedups? har har). This speedup, given by @kriskowal, is a tricks and tips discussion on his favorite editor, vim.

“Slides” for this presentation:

Or, for those without javascript enabled, here is a plain version:

^w hjkl move to windows

^w HJKL move windows

^w +- horizontal resize
^w = even splits
^w10<> vertical resize

O
I  ia  A
o

gg
0     ^      $
G

{
}

:!

   /----------------------\
   |:e :e# :vs :sp ^w hjkl|
   |:e :e# :vs :sp ^w hjkl|
   |:e :e# :vs :sp ^w hjkl|
   |:e :e# :vs :sp ^w hjkl|
   \----------------------/

:n :N

Vv^V o
>> <<
=
:set et sw ts
:!expand unexpand
gq
:set ve=all
comment/uncomment
:set incsearch n N
:noh
:set tw

highlight OverLength ctermbg=red ctermfg=white guibg=red guifg=white
match OverLength '\%78v.*'

:set modeline
:set wrap lbr number
/ n .
Friday, June 19th 2009

ShareThis links on your tumblog

Those nifty buttons are a great way to make your blog a better place for your readers. ShareThis has worked pretty hard at making them easy to integrate into your site. When you sign up, you get some button code that you can plop down inside your post (like I did earlier in this paragraph), or better yet, inside your post template next to the comment button.

This is fine for a single post, but what if you want users to be able to share posts on the main page without having to click through? Just pasting the script down for each post has the negative effect of linking to your home page instead of the entry you’re trying to share! This is not so helpful later on when your post has wriggled it’s way down into the archives.

Fortunately the ShareThis API allows you to include their script in your <head></head> tag once and then add this snippet with the proper link and other info needed to share your post:

You could fill one of these out manually and put it in each post, but that would be no fun, would it? I’m a lazy guy, so I googled a bit and turned up this automatic solution, which uses prototype (a popular javascript library) to voodoo the information out of your document and into the snippet. I’m more of a jquery guy myself, and got the whole thing working using my favorite library, but it made me uneasy that this was relying on the structure of the page extract information to ShareThis. This is likely to break if you change your post layout.

To get around these problems and make it easer for non javascript hackers, I decided to use tumblr template tags (yay alliteration) to create the ShareThis snippet directly. This gives you control over what fields from tumblr go into ShareThis, and improve the quality of your outgoing links. As you look at the snippet, keep in mind that tumblr template blocks like {block:Photo}{/block:Photo} only appear if the entry is a photo, so you can customize what data you send for each type of post:

To use this script, first paste your ShareThis script <head>Inside the head section</head>, then paste the apove script wherever you want your ShareThis link to go on the individual blog post. For me, this was at the end of the posts loop:

{block:Posts}
 ...
 <!--INSERT SCRIPT HERE-->
{/block:Posts}

Notes:

  • If you’re editing the script, make sure every key: ‘value’ pair has a comma after it, except for the last one.
  • The ShareThis publish date is in ISO 8601 format. Pretty easy to reproduce with tumblr template tags.
  • Tumblr isn’t very consistent in having the same metadata across post types (why? I assume this was a design decision?). Chat posts can have titles, but videos and audio posts cannot. Most of these shenanigans are to map the inconsistent post fields onto the ShareThis API in a way that makes sense.
  • Using tumblr’s template tags, you may end up with content in the javascript that is not properly escaped. This is a weakness of this solution that is addressed by pulling sanitized data out of the DOM with javascript, but that depends on the layout of your particular template.

I did a fair bit of testing on the links this script produces, but it could certainly use more. Let me know how it works for you, and feel free to fork it on github.

I hope you find this useful!

Was the post too technical for you? Not technical enough?

Monday, November 10th 2008

Django Login Required Middleware

I’ve been doing a lot programming in the Django web framework lately and thought this code snippet that forces a user to login to your site before viewing any pages might come in handy. If you’re already a djangoperson, skip ahead to the code, otherwise, I’ll give a bit of background.

In Django, your code that responds to each incoming request is organized into functions called views which take your HTTP request and turn it into a page that the user can view. It’s a common practice in Django to augment your views using a python construct called a decorator. This allows you add additional functionality without having to write a bunch of code. For example, Django provides a decorator called login_required that takes one of your views and prevents a user from seeing it unless they are logged in:

@login_required      # This is the decorator! One line... simple.
def my_view(request):
    # Do something here to turn the request into an HTML page.
    # ...

Sometimes it’s a real pain to use the login_required decorator all over the views of your complicated site. What if you forget to add it to view that contains sensitive information? Fortunately, Django allows you to write middleware that gets access to each request so you can add functionality that can be applied to your whole site. My middleware simply intercepts each request and redirects users to the site login page if they haven’t logged in. It also allows you to give of exceptions (in the form of regular expressions), i.e. pages that can be viewed without logging in:

from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile

EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
    EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]

class LoginRequiredMiddleware:
    """
    Middleware that requires a user to be authenticated to view any page other
    than LOGIN_URL. Exemptions to this requirement can optionally be specified
    in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
    you can copy from your urls.py).

    Requires authentication middleware and template context processors to be
    loaded. You'll get an error if they aren't.
    """
    def process_request(self, request):
        assert hasattr(request, 'user'), "The Login Required middleware\
 requires authentication middleware to be installed. Edit your\
 MIDDLEWARE_CLASSES setting to insert\
 'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
 work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
 'django.core.context_processors.auth'."
        if not request.user.is_authenticated():
            path = request.path_info.lstrip('/')
            if not any(m.match(path) for m in EXEMPT_URLS):
                return HttpResponseRedirect(settings.LOGIN_URL)

Example:

# settings.py

LOGIN_URL = '/login/'

LOGIN_EXEMPT_URLS = (
 r'^about\.html$',
 r'^legal/', # allow any URL under /legal/*
) 

MIDDLEWARE_CLASSES = (
    # ...
    'python.path.to.LoginRequiredMiddleware',
)

While writing this, I found similar solutions by davyd and a discussion on the django-users list, but neither had the flexibility of using regular expressions.

Please let me know if this was useful to you, or if you find any problems!

random photos