When CityEats.com by the Food Network begun it was treated much like a web site. I had inherited the front-end code from a truly talented dev I’d worked with before and respected. But the site had pivoted over the past 2 years into a multi-headed web app. Originally a Groupon competitor, it had gone through several iterations, landing more recently as an OpenTable competitor. It was in sore need of optimization and restructuring to aid desktop and mobile render speeds.

This post describes how I used Rails manifests to approach this restructuring, and reduced the initial homepage CSS foot print by 40% for desktop, and 23% for mobile. For the sake of brevity, I will round up file sizes.

This post also targets the homepage, but similar reduction across the site were experienced.

The Numbers

  • Desktop before: 74 KB, after: 29 KB
  • Mobile before: 496 KB, after: 114 KB

Whatever, show me.

I don’t care, why or what, just show me the code.

The History

There were certain assumptions made when the site was designed. It resembled Apple.com, at the time, in how it loaded it’s CSS and JS:

  • It pulled all of the CSS for the entire site on the homepage.
  • Mobile pulled all of the CSS for the desktop, and then applied mobile styles via media-queries

The latter was done, as it was decided, in tandem with the product owner that:

  • we didn’t want to spend extra time developing a mobile version, and
  • chances are it was faster to apply a bandaid to the original styles than to write mobile styles from scratch, or
  • have a duplicate but similar set of styles that would have to be maintained separately.

It quickly became apparent, that the styles for mobile on a good portion of the pages, were radically different, and when styles changed on the desktop often the product owner wanted mobile to remain the same.  So mobile became bloated with copied sections of old desktop styles, and more effort was put into maintaining a separate look while over-writing the desktop redesign.  Short story – it became a mess.

It also became apparent that the approach of downloading all of the CSS on the initial homepage hit wasn’t necessarily good for mobile speeds, nor the speed of the desktop homepage.  The original focus was to speed up the homepage, and limit that initial file hit. (A separate task involved spriting all images into a single file using Compass – but that’s another post.)  My goals were to try and find a way that we could cache what needed caching and pull only the files needed on the individual pages, and this, potentially would have a trickle down benefit for mobile.  My strategy became to find a way to separate groups of pages, and isolate some of the larger pages in the user flow – so that no one page pulled gobs of CSS that it didn’t use – but that we utilized caching when possible to speed up rendering. Some constraints that we faced:

  • Files were currently split somewhat ambiguously into 3 files. The reason for this was that I found an issue in IE when releasing our switch to Rails 3.0, and the asset pipeline.  Originally when I inherited the site it was making 8 HTTP requests for various CSS files (fonts, reset, global, public, and various vendor stylesheets). When switching to the asset pipeline, we went from 8 HTTP requests to 1 and quickly hit the stylesheet limits in IE 6-9 which basically stops parsing/rendering CSS at a specific selector per sheet (4095) in the CSS.
  • We weren’t going to do a deep redesign or refactoring of the CSS (to say responsive) as there was so much of it and we literally had one sprint to get something in place.

So what I settled on was keeping the 3 HTTP request.  I feel in some ways this could at some point be reduced to two.  But what I settled on was a default-base.css, a default-module.css and a default-page.css.

  • default-base would house reset, fonts, and global styles applied to all pages of the site.
  • default-module would house OOCSS for items used in multiple places (not all) in the site (review form styles, vendor styles)
  • default-page would house page-specific styles. The default would house those styles that applied to the smaller static pages. Everything else would be broken out into specific areas or pages. For example, the user profile area had it’s own file that addressed multiple pages, but the restaurant detail page, would pull CSS just for that page given it had a larger footprint.

This approach allowed us to overwrite the module manifest (this was decided to be overkill, given its footprint was pretty minimal) or page level manifests as needed.  The focus for this iteration was to tackle the page level.  In theory this would allow us to ditch the page-level ID that was applied to the body for each pages’ hook (necessary when the styles are in one file), simplifying CSS parsing – a further advantage.  The cons, we would need to maintain multiple manifest files for various areas/pages of the site.  Currently this meant maintaining separate manifests for each page or area.  I didn’t feel this was a disadvantage. I pitched it to the other 4 members of my front-end department, and my partner on the team and they felt this was a decent tradeoff.

The Code

Layout


-# stylesheets
- @base_css ||= "default-base"
- @module_css ||= "default-module"
- @page_css ||= "default-page"

!!!
%html.nojs
 %head
  = stylesheet_link_tag @base_css, :media => 'screen'
  = stylesheet_link_tag @module_css, :media => 'screen'
  = stylesheet_link_tag @page_css, :media => 'screen'

View

If supplying an over-ride in the view do the following, otherwise default would be pulled.

- @page_css = "page-restaurant-detail"

Manifests

default-base
/*
*=# BASE
*= require_self
*= require reset
*= require sass/global
*/
default-module
/*
*=# VENDOR / MODULAR STYLES
*= require_self
*= require sass/unbranded
*= require sass/reviews
*= require sass/pagination
*= require jquery.rating
*= require sass/jquery.tools.scrollable
*= require chosen
*= require sass/vendor-overrides
*/
default-page
/*
*=# page level CSS
*= require_self
*= require sass/public
*/

Page level styles

This is an example of what would replace page-default.css as needed.

/*
*= require_self
*= require sass/page-restaurant-detail
*/

The Results

Of course I didn’t record my findings when things were fresh in my browser, so I had to cull together several screenshots I’d taken when I was analyzing the file sizes, and also fire up the current version of the mobile site, to see the current state of the site. Also note, in the two versions of the desktop, one is showing the New York metro, and the other is showing the DC metro. But the styles pulled for both are the same. And I’m not looking at parsing, or render times – just file-size to keep apples and apples here.

File sizes before any optimization was done, but after Rails asset pipeline.

File sizes before any optimization was done, but after Rails asset pipeline.

After refactoring was applied, isolating HP styles, caching global and vendor.

After refactoring was applied, isolating HP styles, caching global and vendor.

This shows the file size pulled in the mobile.css file. It's pulling all of desktop and mobile on the initial HP view.

This shows the file size pulled in the mobile.css file. It’s pulling all of desktop and mobile on the initial HP view.

This shows the CE site today, 3 HTTP requests, but only pulling what's needed and caching globals.

This shows the CE site today, 3 HTTP requests, but only pulling what’s needed and caching globals.

Well it feels like the first day on the new job. I’d like to say that I don’t normally wake up at 2:30 a.m. but that seems to be more the norm these days, not the exception. The difference, however, isn’t waking up thinking about CSS, or what task I need to do tomorrow on a particular website. This time, it’s with this feeling of “What is tomorrow going to bring?” It is unofficially my first dedicated day on the new job, and as spaceninja says – this new employer doesn’t really give you time off.

I’m still trying to wrap my head around not having a job. Since I can remember I’ve always had a full-time job. Not to piss off stay at home moms out there – I do understand it doesn’t get any fuller or full-time than being a mom. But identity-wise for me, I’ve always worked as a front-end developer. And now after 10+ years that’s not the main thing on my business card. I’ve worried about how it will feel to not have that as my main identity. Which is weird because it’s not like I was a huge evangelist, or a particularly active voice in the web community. But at my core, that’s really what I’ve been the longest. So this new role is a tad daunting, to say the least! And frankly I don’t know how I feel about it yet. I think I’ll really miss it. The last two years working surrounded by developers has been a treat and made me realize that I need that. I enjoy being around them almost as much as I’ve enjoyed having seriously top-notch designers handing me comps. The environment of collaboration around a technical and aesthetically enjoyable task is one I think I will seriously miss.

I didn’t realize this until I took briefly, prior to Pop Art, a job where I was the sole web/technical person. The people were wonderful, but I felt lonely! I missed the challenges, commraderie and problem solving you only get when working on a project that requires more than just you to pull it off. So there’s a part of me that’s a smidge worried about this new horizon. Especially since, as a shock this may be to everyone, I haven’t quite embraced myself as a mom yet.

This came as a bit of a surprise to some of my birthing class cohorts, as I sat there with my belly protruding out into the middle of the room. One woman said that she felt like a mom from day one because she had to now think of another person before she ate or drank something, or did something as simple as cross the street. And I get that – I have that now too. I had this weird moment the other day on my way home – new bus stop that means my short cut for getting home is crossing active train tracks. After a really long night, I got there just as a train stopped on the tracks in front of me – barring my way. I could have easily walked behind it – and my unpregnant stupid daredevil self would have just booked it. But thinking, wow how stupid would it be for me to get backed over by a train after 36 weeks of not eating rare meat or nitrates? I turned around and walked a seriously long way out of my way to get home.

So I get the thinking of another being thing. But I couldn’t help but note – this was said by a woman who was going to keep her job. And while that definitely has it’s own sacrifices and challenges that I completely respect, I think it still allows you this piece or facet of who you are to remain. That said, choosing to eat better, exercise and make choices for an unborn child, for me is slightly different than having a new helpless person in the world who is solely dependent on you. Being pregnant is a package deal. Yes you make choices that effect another, but in a way you don’t have a choice. It’s not like you can put it down and walk away. It’s always with you. As a mother – as a parent – I think you make choices everyday. You prioritize your life in a certain way that’s different than when a child is a part of you. And that’s the great unknown for me.

I’m not sure I’m really expressing the weird fight in my head over my identity. Work has really always defined me, for better or worse. It doesn’t mean I’m particularly good at it! It just means that’s the one constant I’ve had in my life that has been a part of who I am. And I know that I don’t have to stop that. I can continue being active in something that I enjoy – that’s a part of me. But I’m realizing that I’m laying down that mantle a bit and picking up this unknown definition of who I’ll be. That feels as foreign to me as ice fishing. I’ve fished before = I’ve held babies before. So I should be alright, right? Uh huh.

Not that I’m mortified. I’m somewhat ready. And excited. I’ve fed fantasies about this life for literally years. And honestly – I didn’t think I’d be lucky enough to stay home with my kid. It was a recent realization that we might be able to pull it off – with some sacrifices of course. And that was something I’ve always wanted. From the time I knew I wanted to have a kid, I knew I wanted to stay home (not to say I’ll want that in another 3-6 months! I honestly don’t know if I’ll be able to hack it) and be the one to witness it from the get go. But the reality of life – at times being the bigger bread winner – seemed to mean I wouldn’t necessarily get the choice to stay home, and that if I chose to have a child it would be with that big compromise. Which was really a hard choice for me. But as my child bearing years began to wane, it seemed worth any compromise, and we jumped in with both feet just hoping it would turn out!

Things of course have changed. I’m no longer the primary bread winner. While we have counted on both of our salaries, Papi is taking on more so that I can stay home. It’s a decision we’ve both made, but I have to say I slightly worry about how much he’s going to have to work, and how much I’m going to have to take care of household things. Kind of blows my mind how I’m looking at a June Cleaver life. I know it doesn’t have to be that, and unless June dropped a lot of f-bombs, I’m sure it won’t be. But it’s sure different from agency life and a bevvy of deadlines.

The one thing procrastination and cramming prepared me for in college, seemed to be life in an agency. The self imposed turnaround times seemed to jive completely with the “Wait for it.. wait for it… ok… now GO!” mentality of last minute pull-it-off-ness that sometimes defines work at an agency. I’m not sure I’ll miss that! However it doesn’t mean that I’ve solved my procrastination streak, and I wonder how this will serve me in my new role. “Sorry kid, we’re walking home from the hospital because I didn’t get the car seat installed in time. Yeah, I know we live in Milwaukie.”

That said, in theory I have another 2 weeks to prepare for her impending arrival. I hope to fill it with walks, naps, reducing my cankles with legs-up-the-wall yoga poses, preparing the baby’s room – even though for the first few months at least – her room will be our room, and the otherwise general preparing of laundry washing, car-seat installing, purchasing last minute unglamorous items that no one will give you at a shower – like diaper pails and breast pumps.

I don’t know that this will turn into a “mommy-blog” but I do think it funny that the first time I really write about being a mom – is the first day I’m not something else. And the floundering feeling that’s left me. I left work today thinking about how I moved to Portland with a goal to some day work for an agency. And now that I have, and it’s been this crazy whirlwind of job-getting-changing, house selling-buying, remodeling and now pregnancy – I had this weird anti-climactic feeling of “Oh god, now what?”

And then I smiled really big because I don’t have to go to work on Monday, I don’t have a newborn yet, which means we can still go out to dinner! So Papi and I packed it off to try a new place as we’ve been doing these days. Whooping it up, if you will, before we’re struck with a demanding infant. We tried Papa Hadyn. Papi insisting on the risotto. His quest to find one that rivals one he had at a restaurant we wish we could remember the name of, in of all places, Scottsdale, AZ that someone had given us a gift certificate to. It had turned out to be one of the best things we’ve ever put in our mouths. Being his first and only risotto, the bar was cast unusually high, and he’s been looking to recreate that experience ever since.

After a mediocre mixed green salad and riding the heels of his disappointment in a lumpy, dry and possibly undercooked risotto, I had to hold back rubbing it in too much by visibly enjoying my Muscovy duck. My own stab at a description – seared duck over wine infused chard and trumpet mushrooms lying on some of the softest gnocchi pillows I’ve ever put in my mouth. Now granted – I’m a pregnant lady – who dined on warm Coffee Plant pastries (yes, note that’s plural: molasses ginger cookie AND blueberry coffee cake) at 10:30 a.m. this morning – which served as lunch as well. So I could have swallowed a small piglet in one gulp, not chewed and claimed it the best thing to ever put in my gullet. But seriously how do you go wrong with a form of crispy duck skin. How?

However the real, and most likely only reason, we were there – other than it’s nearby and we’ve never been – is that Papi is a dessert hound, says the woman who ate confection as a meal not 8 hours previous. (Just an aside, I do feel slightly bad for pointing at my swollen ankles this morning when the midwife raised her eyebrow at my most recent weight gain. Ok, maybe it’s not ALL water weight. But if you’re going to have to suffer sudden onset of cankles, there should be some perk, right?) He ordered the Cassata a “Kahlua and espresso-soaked sponge cake with bittersweet chocolate-ricotta filling”. It was served with raspberry puree and chocolate. Thankfully we split it. And it was the cherry on my day.

Papi and I have never been the romantic dinner couple. But tonight and our recent night out to Ken’s Artisan Pizza, have kind of ignited that for us. So I waddled out of there on a cloud as a pensive mother to be, no longer gainfully employed front-end web developer glad to have someone I love to take me out to eat.

Recently we were asked by a client if traffic from their test site was being posted to a Google Analytics (GA) account. It turns out it is. Not only that, so is any version of the site hosted on their development and test servers, and any developer’s local site.

We tested this by placing several pages on various websites and subdomains. For example pages were placed on www.popart.com, test.popart.com, and blogs.popart.com as well as www.clientsite.com and test.clientsite.com all with the same GATC.

Data was collected under that profile for each of these pages in Google Analytics regardless of domain or subdomain.

How Can We Eliminate Development Traffic From Our Statistics?

Removing the GATC from all but the live site was one option, but having two sets of files would require us to remember to insert this code when pages go live raising the concern of releasing a page without a GATC and losing data.

Also this solution would not address the possibility that someone could accidentally or intentionally copy our code to their site, meaning we’d be collecting stats from their site as well.

Instead by using filters we can target and collect only the site traffic we’re interested in. Moving forward when creating a GA website profile, in addition, we will create a second GA web profile to which we can apply our filters. This allows one profile to be untouched with all of our master data, and one that only collects data from our target domains.

Creating a Second Web Profile

  1. Select Create New Website Profile, and then choose Add a Profile for an existing domain.
  2. Select the Domain from the drop down for http://www.mysite.com and give a profile name that suggests it’s the filtered version of your data.
  3. Select continue.
By creating a filter we only collect data from our specific domains.

By creating a filter we only collect data from our specific domains.

This sets up a second profile that uses your original GATC – not a new one. So any data collected moving forward will collect in BOTH of your profiles.

If you are doing this to a site profile that already contains data – be aware that the second profile does NOT pull the historical data. While it is a profile that uses the same GATC it is not a duplicate. Any previous filters, reports or goals are NOT included in this second profile – you’d need to add those manually.

The filter works only on stats collected moving forward. So the goal for this second profile is to only include web traffic from http://www.mysite.com, and http://mysite.com. What we want to exclude is everything else. Excluding the chaff from development environments and different domains was easy – subdomains prove a bit trickier. But we found a filter that seems to work for these purposes.

Adding a Filter

  1. From the home view that allows you to see your Web profiles listed, you’ll want to select Edit.
  2. Scroll down to Filters Applied to Profile and click Add Filter. Give the filter an intuitive name such as “Only www.mysite.com traffic” or some such. Under filter type select Custom Filter, Include.
  3. Under Filter Field select Hostname.
  4. Under Filter Pattern we’ve used the following reg ex: ^mysite\.com|www\.mysite\.com The caret tells analytics to ignore subdomains, then we add an ‘or’ that adds back www.mysite.com.
  5. Now note if there are any other subdomains that you WANT to include in this profile such as blogs.mysite.com, you would want to add that at the end after a | (pipe). So any additional subdomains you WANT to track should make the pattern look like this.^mysite\.com|www\.mysite\.com|nameosubdomain\.mysite\.com“What kind of special characters can I use?” gives a more in depth explanation of the characters used and other filter options.
  6. Case Sensitive – No.
  7. Save Changes.
By creating a second web profile we have a safe place to apply filters that won't effect your data.

By creating a second web profile we have a safe place to apply filters that won't effect your data.

It takes at least 24 hours for this profile to show data, however it begins collecting data immediately. Depending on the speed of setting up the filter, you might get some traffic from other domains. But the majority should be just from the domains you specified.

This is Great But What About My Historical Data?

GA does allow you to view your data by Hostname and it also allows you to create custom reporting. While this may not solve all of your problems it can give you insight to specific questions. In the scenario presented by our client they had numbers on one of their goal pages that was higher than the number of completed e-commerce transactions. By doing the following they should be able to view the page and exclude numbers from different domains.

Viewing by Hostname

  1. In your original profile that contains all data (including historical data) navigate the following in the sidebar:
    Visitors/Network Properties/Hostnames/
  2. In this case we’re interested in the numbers of unique visitors to our goal page from both www.mysite.com and mysite.com.
  3. Select www.mysite.com to view the details.
  4. After clicking the domain, use the dropdown next to Dimension to select Landing Page, locate the name of the goal page under the listed pages. You can narrow the time frame to only select the days you’re interested in.
  5. You’d need to do this again under mysite.com, and then add the two numbers to get an overall view of the goal page data.

Another way to do this that might be helpful should you need to refer to this data again in the future is to create a report.

Creating a Custom Report

  1. Click Custom Reporting in the sidebar, then in the upper right of that page, select Create new Custom report.
  2. The one I created I titled “Pageviews by hostname, page and day”. Give it a title that means something to you.
  3. Then under Metrics, select Content – I chose Unique Pageviews but select the metric you are interested in tracking. Drag it over to the blue box.
  4. Under Dimensions you’ll choose three:
    1. Systems/Hostname – drag over to first subdimension box,
      Content/Page – drag to second subdimensions box
      Visitors/Day – drag to third

    I’m not sure the importance of order here, I think it allows you the drilldown so if it makes more sense for you to pick the day before the page then you’d probably want to switch the order on this.

  5. Save the report and then you should be able to click on this under your Custom Reporting anytime in the future. You will still need to add the two numbers together from your two domains, but this shows you a day by day break out as you click to drill down into the numbers.
Custom reporting doesn't permanently affect your data.

Custom reporting doesn't permanently affect your data.

This may be useful to you in case you don’t want to add the filter, or you find this is a better way to view historical data for comparison. You could essentially do both – since the filtered profile is separate from this one.

While I looked into a good portion of this on my own, the idea of creating a second website profile and applying a filter that just pulls traffic from your site was only one example from a chapter called “Best Practices Configuration Guide” from Advanced Web Metrics with Google Analytics by Brian Clifton. I would highly recommend this book to anyone who wants a more thorough understanding of what GA can offer.

Note:
This was originally posted on my work blog, and I’m re-posting it here for archival purposes.

An old post – I wanted to write even prior to making the bun in the oven news public.

It’s been so long – once again, since I’ve posted. I’ve spent the last 3 months with one singular focus: trying not to puke. There’s a part of me that wonders if this isn’t some evolutionary attention getter. Voice on high: You will focus on nothing else other than you, your digestion and the well being of your child. Knitting? God who cares. In depth mind processes aka thought? Who has the energy.

I did stumble upon something that I wish I would have tried a long time ago: Sea sickness bands. I’m not sure what took me so long. But it was the late night session of being face down in my toilet after a rather greasy noodle meal at lunch and the 5th episode of Anthony Bourdain’s No Reservations, that was the clincher. I would try anything to avoid feeling this way, even if it was to resort to a harmless yet not proven accupressure method. (And to avoid watching cooking shows when all I can manage to put down is a bowl of Cream-o-wheat.).)

I’m happy to say that I’ve felt so much better this week (Update: this wasn’t to last but the respite was nice). With the bands, and some other modifications: I essentially ate nothing of color for the first few days after the puke incident and proceeded to eat nothing warm (and thus smelly) – cold food only: fruit, and my current mainstay – sushi. I’ve eaten non-raw sushi for probably every other meal this week. I must confess I’m getting a bit sick of it. But it’s held me together. It digests easily, has vegetables, and occasionally protein.

It’s been such an insular focus, and oddly I feel like the most uninteresting person on the planet given all my thought and energy is going into the not puking and reading all that I can on birthing and preparing for giving birth. Yes, I’m jumping the gun a bit, but I’m trying to figure out where and how I want to have this kid. It’s been relatively all consuming. I have managed to squeeze some news reading in, riding my bike to work a bit more, and am planning to go to an art show with a friend tonight so the horizons are widening. But it’s been a strange state: The one thing that is consuming my attention and focus and every bodily function is something that is the biggest secret. Because I refuse to make it all public, especially at work, until I’m out of the miscarriage woods.

Meanwhile I feel like I do my weird rituals to keep from puking: like eating saltines like they’re going out of style, drinking lemonade like it’s the nectar of the gods, and not removing my sea-bands except for showers and sleep. I have a fear that I’ll wake up one morning to find my hands pinched off – two bloody stubs with a form of sophisticated rubber band wrapped around each.

Which by the way brings up the issue of rituals. I never was superstitious until I mountain biked. I think it was the lack of confidence in my own abilities. I couldn’t depend on myself to win a race, but if I did everything just so, and I had my lucky string with me – like the first time I won a race – well, then, I could win another one. And so it went. Same with the nausea and “morning sickness” (There’s a special hell awaiting the asshole who named this ‘morning’ sickness – since it strikes most folks, including myself, all day long.) Since I’ve put on my sea-bands god help the person who tries to wrench them from my body to wash them. I’m pretty sure I’m going to have to ask my Felipe to wash them while I sleep.

So I told the folks at work yesterday that I’m having a kid. I had been guarding my secret as best as someone can who snarfs down saltines all day, is really finicky when it comes to choosing lunch spots, and who seems to have lots of flu-like symptoms. Again. It went well. I’m not sure why I was intimidated. I think if anything, I’m relatively private about some things (I know, I can hear folks who know me chortling from here. But seriously!) and telling someone something like that just felt strange to say the least. But I actually discussed so many things with them that it set me at ease.

That said it’s made it all so real. I went from zero-to-pregnant in one day. I had been waiting to pass the 12 week mark just to be on the safe side. And now all of the sudden it feels like that day I peed on the stick all over again.

Very exciting. I have more to write, but given I’ve been up since 4 am, I think I’ll wait until I’m coherent. I do have one post I wrote prior to “going public” that I’ll post. But hoping I’ll do more updating here, because it seems like I’m always astonished during this experience with the changes. From simple things like my insanely intense dreams to seeing an ultrasound for the first time. It’s already been a crazy ride.