YSlow development: custom rulesets

June 20th, 2012. Tagged: performance, yslow

(This is part 3. See part one and part two.)

There are two concepts to remember when working on your YSlow extensions and customizations:

  • rules (or "recommendations" if you will, or "best practices" or simply "lint checks"), and
  • rulesets which are lists of rules

An example rule is "Reduce HTTP requests". An example ruleset is "Small site or blog" (which is less strict than the default ruleset, because it assumes a small site has no CDN budget for example)

YSlow has a number of rules defined. How many? Easy to check once you have your setup from the last blog post. Open the console and go:

>>> Object.keys(YSLOW.controller.rules).length
23

And how many rulesets?

>>>Object.keys(YSLOW.controller.rulesets)
["ydefault", "yslow1", "yblog"]

Each ruleset has an id (e.g. ydefault), friendly name, list of rules and list of weights for each rule:

>>> YSLOW.controller.rulesets.ydefault
Object
  id: "ydefault"
  name: "YSlow(V2)"
  rules: Object
  weights: Object

The weights define what is the relative importance of each rule in the final score. And the rules contain rule-name => rule-config pairs. Because each rule is configurable. For an example configuration consider the "Thou shalt use CDN" rule. The patterns that match CDN hostnames are configurable. So is the number of points subtracted from the score for each violation.

(I can talk more about scores, but it's not all that important. The thinking was that people might be offended by and disagree with the scores. So we should let them customize the scoring algo)

Alrighty, enough talking, let's create one new custom ruleset.

New ruleset from the UI

  1. Click "Edit" next to the rulesets dropdown. A list of rules appear each with a helpful hint on mouseover and a friendly checkbox for your checking pleasure
  2. Click "New Set" to clear all default checks
  3. Check the most "duh!" rules, those that require no effort and are just sanity
  4. Click "Save ruleset as..."
  5. Type a name, like "Duh", save

Congratulations! You have a new ruleset.

If that wasn't the bookmarklet version, YSlow would remember this new ruleset. But YSlow doesn't (yet) remember settings in bookmarklet version. (Try another YSlow run in a different tab if you don't believe it).

But you can still save your ruleset, and even share it with others in your team.

Coded ruleset

This above was all-UI way of creating the ruleset. Behind the UI there's a simple JS object (that can be serialized to JSON for future use) that defines the ruleset as explained above.

>>>JSON.stringify(YSLOW.controller.rulesets.Duh)
"{"custom":true,"rules":{"ycompress":{},"yredirects":{},"yno404":{},"yemptysrc":{}},"weights":{},"id":"Duh","name":"Duh"}"

Tada!

Now just take this JSON string, paste into your mystuff/stuff.js (from the previous post), clean it up a little and add a call to the YSlow API to register this new rule.

parent.YUI = parent.YUI || YUI;
parent.YSLOW = YSLOW;
 
var duh = {
  id: "duh",
  name: "Duh",
  rules: {
    ycompress: {},
    yredirects: {},
    yno404: {},
    yemptysrc: {}
  },
  weights: {} 
};
 
YSLOW.registerRuleset(duh);

Than build and push:

$ make bookmarklet config="config-phpied.js"; \
  scp build/bookmarklet/* \
  username@perfplanet.com:~/phpied.com/files/yslow

So we have our own rule and we can run it and it can spit out reports.

(Note: Small correction from the previous post: in the Makefile your mystuff.js should go before the bookmarklet controller, which is responsible for the initialization. Because you want your registerRuleset() call to run before the initialization)

(Another Note: Disable Chrome's cache if you're testing with Chrome, because it's pretty aggressive in this bookmarklet scenario)

If we decide to tweak the scores and weights a little bit (take out 50 out 100 points for a single non-gzipped component and increase the rule's relative weight), we can do:

var duh = {
  id: "duh",
  name: "Duh",
  rules: {
    ycompress: {
      points: 50
    },
    yredirects: {},
    yno404: {},
    yemptysrc: {}
  },
  weights: {
    ycompress: 10,
    yredirects: 3,
    yno404: 3,
    yemptysrc: 5
  } 
};

The the result of running the tweaked ruleset on the same page is different this time:

You can inspect each rule's default config like:

>>> YSLOW.controller.rules.ycompress.config

Object
  min_filesize: 500
  points: 11
  types: Array[5]
    0: "doc"
    1: "iframe"
    2: "xhr"
    3: "js"
    4: "css"

Adios

This is it for tonight, next time: how to write your own rules.

pssst, a hack to make your new rule the default because bookmarklets don't remember preferences:

// HACK
YSLOW.util.Preference.getPref = function(name, def) {
  return name === "defaultRuleset" ? 'duh' : def;
};

And the final version of mystuff/stuff.js for completeness (and without global variables this time):

 
YSLOW.registerRuleset({
  id: "duh",
  name: "Duh",
  rules: {
    ycompress: {
      points: 50
    },
    yredirects: {},
    yno404: {},
    yemptysrc: {}
  },
  weights: {
    ycompress: 10,
    yredirects: 3,
    yno404: 3,
    yemptysrc: 5
  } 
});
  
 
// HACK
YSLOW.util.Preference.getPref = function(name, def) {
  return name === "defaultRuleset" ? 'duh' : def;
};

// DEBUG 
parent.YUI = parent.YUI || YUI;
parent.YSLOW = YSLOW;

Tell your friends about this post on Facebook and Twitter

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov