Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Transitioning to Sass at Scale (SassConf 2015 -...

Dan Na
November 11, 2015

Transitioning to Sass at Scale (SassConf 2015 - Austin, TX)

SassConf 2015, Austin, TX - November 2015. 45min

CSS preprocessors like Sass add a variety of functions that streamline CSS development: variables, nesting, functions, mixins, etc. The documentation is great, the tools are mature, and starting a new project using Sass has a clear and straight-forward workflow. But transitioning a large legacy codebase from CSS to Sass is a different story. CSS syntax errors that may be harmless in production can completely prevent Sass from compiling. But fixing those errors creates a far juicier problem: will we introduce visual bugs by fixing syntax bugs?

At Etsy we faced this exact question multiplied across 2000+ CSS files. During this talk I’ll discuss the tools we used and built throughout our Sass workflow, from the initial transformation of CSS files using Abstract Syntax Trees (ASTs) to the libsass-powered Sass -> CSS render pipeline we have running on all development machines. I’ll cover some of the tools we’ve built in-house to mitigate some of the biggest potential pitfalls of Sass (SCSS live lint), how we ramped up our development and production services to gain confidence in our process and how this entire effort led to a single 1.2M line push that didn’t break production and had minimal impact to developer and designer workflows.

Dan Na

November 11, 2015
Tweet

More Decks by Dan Na

Other Decks in Technology

Transcript

  1. Sass Conf 2015 – Austin, TX @dxna Dan Na Transitioning

    to Sass at Scale @dxna Hi, my name is Dan Na and I’m here from Etsy in Brooklyn, NY. I’m super psyched to be here. Today I’m going to talk about how we transitioned all of our CSS to Sass in October 2014, which was roughly a year ago.
  2. @dxna http://talks.danielna.com/sassconf-2015 Resources for this talk can be found at

    this URL, and I’ll also link to that page on my twitter.
  3. 3 For the unfamiliar Etsy is the world’s handmade marketplace,

    where people around the world connect, both online and offline, to make, sell and buy unique goods. We sell a ton of stuff across all different categories - clothing, jewelry, woodworkings, craft supplies, you name it. You can find 32M items on our marketplace, like this beautiful piece of Austin-related artwork.
  4. 4 Like I said, we have a lot of items.

    This being Austin I figured I’d find something that catered more to the locals. This is a steak you can hang around your neck just in case you’ve gone over 15 minutes without eating meat.
  5. Dan Na | @dxna 5 Performance & Front-end Infrastructure Daniel

    Dan Lara Kristyn Allison Takashi Sufian Natalya Mike Moishe I’m an engineer on the Front-end infrastructure team. The front-end infrastructure team is tightly coupled to the performance team, and while we’re different teams our responsibilities are highly complementary. Between us we handle things like site performance, the building and maintenance of asset pipelines for CSS and JS, and monitoring front-end errors and issues in production. I feel enormously fortunate to go to work with these great people everyday.
  6. Dan Na | @dxna Transitioning to SCSS at Scale https://codeascraft.com/2015/02/02/

    transitioning-to-scss-at-scale/ 6 In February of this year I published a blog post on code as craft — our engineering blog — about our transition from CSS to Sass. That post got a good amount of traction in the developer community and we had a surprising amount of organizations talk to us about how they were facing similar challenges when looking at their own conversion. And that’s awesome, and part of the reason why we write the engineering blog in the first place. We want to share learnings and open up a dialogue across the industry. I’m super excited today to share about what we’ve learned, and I can imagine literally no more appropriate conference in the world to do that than SassConf.
  7. Dan Na | @dxna Why was this scary? 7 Here’s

    why this transition was scary.
  8. Dan Na | @dxna 8 21.7M active buyers AS OF

    JUNE 30, 2015 We have 21.7M Active buyers
  9. Dan Na | @dxna 9 1.5M active sellers AS OF

    JUNE 30, 2015 We have 1.5M active sellers
  10. Dan Na | @dxna 10 $1.93B annual GMS IN 2014

    And we sold $1.93B worth of merchandise in 2014. Needless to say, we have a lot of traffic and commerce coming through our site on a daily basis.
  11. Dan Na | @dxna CSS exists on every page every

    visitor sees. 11 CSS exists on every page that every visitor sees. Our members trust us to make changes to the site with their best interests at heart and it’s our responsibility to take that trust seriously. If our conversion breaks CSS in ways that we can’t detect or we can’t understand, real people’s livelihoods are affected. What if the add-to-cart button is hidden on some page we don’t account for, or what if people can’t add to their favorites, or what if users can’t do some other type of functionality that’s core to the buying and selling experience? Broken CSS means our sellers earn less income, and that’s a really big deal.
  12. Dan Na | @dxna Challenges of a large, legacy codebase

    Performing the conversion Gaining confidence Long-term maintainability 12 Agenda Here’s what I’m going to talk about today. I’m going to talk about the challenges we faced when working with a large, legacy codebase, the process of actually performing the conversion, how we gained confidence that our changes would be successful, the constructs we put in place to make sure our Sass was usable and maintainable moving forward.
  13. 14 This is the first iteration of the Etsy homepage,

    in July 2005. I doubt that a single line of code from this iteration of the site exists today. It was probably with tables, and frankly I wouldn’t be surprised to find a frameset somewhere. The reason this screenshot is interesting is that it’s worth noting that Etsy has been a live site for over 10 human-years. In web years that’s like a millennium. Over the course of 10 years there have been millions of changes committed by hundreds of developers, with the addition and subtraction of countless technologies to our tech stack. Which is a long-winded way of saying that when there’s a lot of code of varying age in a single codebase, making wide sweeping changes can become difficult to reason about.
  14. 15 This is Etsy’s homepage as of last month. As

    you can tell we’ve adopted a much more modern aesthetic. We’ve also exploded in functionality in 10 years, which this screenshot only briefly captures. In addition to the website, there are native apps, recommender systems, inventory management systems, shipping management systems, we manage our own payments, an ads platform, a home-grown data analytics stack, and internal tools that enable us to respond to customer inquiries. At face value we’re a simple e-commerce site, but the technology and tooling under the hood go far deeper than simply buying listed items. We have 10 years of backend and front-end code to support an extensive suite of tools.
  15. Dan Na | @dxna Etsy CSS, October 2014 • 2,000+

    individual CSS files • 400,000+ lines of CSS 16 And 10 years of code results in a lot of CSS. Before our conversion, an inventory of our CSS showed over 400,000 lines of CSS composing over 2000 individual files. This is all within a codebase under heavy active development. We push code to production 40 times a day. But things worked. So why look at Sass at all?
  16. 17 It all started with a single product team, and

    the rebuild and redesign of one of our major seller tools, the Listings Manager. This is the tool that enables sellers to list and manage items on the site. And there’s a lot to say about this rebuild: it’s a beautiful JS single page app that is infinitely better than its predecessor. For all of the details there’s a blog post on code as craft that talks all about it. For the purposes of this presentation, the most notable thing about this work was the work of the designers on this project in crafting an incredibly beautiful and elegant CSS pattern library. They built it in Sass, and it used the power of mixins and variables and functions to create a modular, reusable and extensible way to build beautiful new pages easily and with minimal markup.
  17. 18 And unknowingly to these designers at the time, their

    work became a compelling case for adoption of Sass across the company. The listings manager Sass patterns have since become the foundation for our new site-wide styleguide. This is a screenshot of our redesigned search functionality using that new style guide. This is what it looks like when you search etsy.com for cute dog costumes.
  18. Dan Na | @dxna For engineering organizations of any size,

    there are huge benefits to having designers who code. 19 I can’t say this enough, for engineering organizations of any size, there are huge benefits to having designers who code.
  19. Dan Na | @dxna 20 A CONVERSATION THAT PROBABLY HAPPENED

    But you might ask yourself: why hadn’t we adopted a CSS pre-processor already? This is a conversation that I can imagine happening between developers and designers for years before I even joined the company. [x] “So, what about Sass?” [x] Are you going to do it? [x] “Good point.” This is typical, right? You can imagine this happening at a lot of different companies about a lot of different technologies. But what what are we really asking when we respond to this type of question with “are you going to do it?”
  20. Dan Na | @dxna 20 A CONVERSATION THAT PROBABLY HAPPENED

    “So, what about Sass?” But you might ask yourself: why hadn’t we adopted a CSS pre-processor already? This is a conversation that I can imagine happening between developers and designers for years before I even joined the company. [x] “So, what about Sass?” [x] Are you going to do it? [x] “Good point.” This is typical, right? You can imagine this happening at a lot of different companies about a lot of different technologies. But what what are we really asking when we respond to this type of question with “are you going to do it?”
  21. Dan Na | @dxna 20 A CONVERSATION THAT PROBABLY HAPPENED

    “So, what about Sass?” Are you going to do it? But you might ask yourself: why hadn’t we adopted a CSS pre-processor already? This is a conversation that I can imagine happening between developers and designers for years before I even joined the company. [x] “So, what about Sass?” [x] Are you going to do it? [x] “Good point.” This is typical, right? You can imagine this happening at a lot of different companies about a lot of different technologies. But what what are we really asking when we respond to this type of question with “are you going to do it?”
  22. Dan Na | @dxna 20 A CONVERSATION THAT PROBABLY HAPPENED

    “So, what about Sass?” Are you going to do it? “Good point.” But you might ask yourself: why hadn’t we adopted a CSS pre-processor already? This is a conversation that I can imagine happening between developers and designers for years before I even joined the company. [x] “So, what about Sass?” [x] Are you going to do it? [x] “Good point.” This is typical, right? You can imagine this happening at a lot of different companies about a lot of different technologies. But what what are we really asking when we respond to this type of question with “are you going to do it?”
  23. Dan Na | @dxna “Are you going to do it?”

    • Evaluate available tools • Do the technical work • Fix broken things • Educate everyone else 21 It’s a loaded question. Are you going to evaluate the tools available and pick the best one? Are you going to actually implement the technical work? Are you going to be on the hook for fixing anything that breaks, as well as understand the problem space enough to be able to respond to weird edge cases and errors? And ultimately, and arguably most importantly, are you going to lead the education effort that teaches everyone else who works here how to use this stuff, now and into the future? This doesn’t even include stuff that doesn’t map to discrete tasks — dealing with optimizations and performance issues, and everything else that “just needs to get done.” Frankly, what engineer had the time to do this on top of implementing their own product work? It’s difficult and kind of a resource issue. So we stuck with vanilla CSS, and it grew over time, and the site was fine. But we knew things could be better.
  24. Dan Na | @dxna Front-end Infrastructure Help Etsy build predictable

    and maintainable web applications by building and maintaining libraries, frameworks, and tools for front-end engineers and designers. 22 Luckily, in early 2014 the Front-end Infrastructure team was created to tackle hairy front-end problems just like this one. Our mission statement is to help Etsy build predictable and maintainable web applications by building and maintaining libraries, frameworks, and tools for front-end engineers and designers.
  25. 23 When we revisited preprocessing our CSS, our initial inclinations

    were to go with Sass. Not only because of the work of the designers on the Listings Manager, but also because we found Sass to be incredibly well documented, and there was a rich open source ecosystem with a lot of awesome third-party tooling available. We also knew that because SCSS syntax is a superset of CSS syntax, the bulk of our vanilla CSS would work, right out of the box.
  26. 24 But realistically one of the biggest drivers of our

    confidence around moving to Sass was the maturity and development around libsass, which is the C/C++ port of the Ruby Sass engine. At our scale and with our number of files slow rendering performance was a non-starter. Libsass was the single reason we were confident about pre-processing our CSS. We knew it’d be fast enough not to slow down developer or deploy workflows.
  27. Dan Na | @dxna 26 Steps (in theory) In theory

    a CSS to Sass conversion is really easy. [x] First, SCSS is a superset of CSS syntax, so in theory, a conversion is as simple as renaming our perfect, error-less CSS files to SCSS. [x] Second, we can install Sass/libsass as a render step to render our Sass back down to CSS. [x] And boom, we’re celebrated by our coworkers as geniuses and go home early. Simple. In theory.
  28. Dan Na | @dxna 26 Steps (in theory) 1. Rename

    homepage.css -> homepage.scss In theory a CSS to Sass conversion is really easy. [x] First, SCSS is a superset of CSS syntax, so in theory, a conversion is as simple as renaming our perfect, error-less CSS files to SCSS. [x] Second, we can install Sass/libsass as a render step to render our Sass back down to CSS. [x] And boom, we’re celebrated by our coworkers as geniuses and go home early. Simple. In theory.
  29. Dan Na | @dxna 26 Steps (in theory) 1. Rename

    homepage.css -> homepage.scss 2. Render homepage.scss -> homepage.css for production In theory a CSS to Sass conversion is really easy. [x] First, SCSS is a superset of CSS syntax, so in theory, a conversion is as simple as renaming our perfect, error-less CSS files to SCSS. [x] Second, we can install Sass/libsass as a render step to render our Sass back down to CSS. [x] And boom, we’re celebrated by our coworkers as geniuses and go home early. Simple. In theory.
  30. Dan Na | @dxna 26 Steps (in theory) 1. Rename

    homepage.css -> homepage.scss 2. Render homepage.scss -> homepage.css for production 3. Sign autographs, go home early In theory a CSS to Sass conversion is really easy. [x] First, SCSS is a superset of CSS syntax, so in theory, a conversion is as simple as renaming our perfect, error-less CSS files to SCSS. [x] Second, we can install Sass/libsass as a render step to render our Sass back down to CSS. [x] And boom, we’re celebrated by our coworkers as geniuses and go home early. Simple. In theory.
  31. Dan Na | @dxna “In theory, theory and practice are

    the same. In practice, they’re different.” - Attributed to lots of people 27 Which leads me to one of my favorite quotes about software development, which the internet has told me can be attributed to Yogi Berra, Albert Einstein and Warren Buffet: In theory, theory and practice are the same. In practice they’re different.
  32. Dan Na | @dxna body { background_color: green; } 28

    Let’s talk about CSS. What happens when you incorrectly apply a property to an element, in this case a mistyped background-color property on a body-tag? In this example that underscore should be a hyphen.
  33. Dan Na | @dxna body { background_color: green; } 29

    Spec hackers and browser vendors are smart. The default browser behavior is to skip properties it doesn’t understand and process the rest of your document. It’s as if that background-color isn’t even there.
  34. Dan Na | @dxna CSS fails quietly. 30 CSS fails

    quietly. Errors in your source, which may or may not have accumulated over 10 years, are simply ignored. And in a lot of ways that’s really convenient, until you want to convert to Sass, which is a compiled language.
  35. Dan Na | @dxna rgb(0, 255, 0) 31 Here’s an

    example. Here’s an rgb color value, where you can declare red, green and blue values to determine a color. The minimum value for any of these ranges is 0, and the maximum value is 255.
  36. Dan Na | @dxna body { background-color: rgb(-10, 25555, -10);

    } 32 Let’s revisit that body tag. What happens in a browser when you declare this color value? Notice that it’s not 0-255. It’s -10, 25 thousand, -10.
  37. Dan Na | @dxna body { background-color: rgb(-10, 25555, -10);

    background-color: rgb(0, 255, 0); } 33 The body background becomes green. The CSS spec dictates that RGB values should be clipped at 0 and 255 respectively, so this background-color becomes red-0, green-255, blue-0 under the hood. So in this case, these errors aren’t ignored, they’re accommodated.
  38. Dan Na | @dxna Sass is compiled. 34 But Sass,

    unlike CSS, is compiled. That’s what gives you all the functionality of mixins and functions and variables and various programming constructs. That means that even if we renamed all of our files from .CSS to .SCSS, any syntax error in our source would prevent the sass renderer from compiling our code.
  39. 35 Here are the official Sass docs for the RGB

    function. While browsers let you use any integer value and clip them at 0 and 255, Sass restricts the value of this input explicitly from 0-255. That original -10, 25555 value would blow up the sass renderer. We’re going to have to go in and fix every instance of this RGB function in order to make this conversion happen.
  40. 35 Must be between 0 and 255 inclusive, or between

    0% and 100% inclusive Here are the official Sass docs for the RGB function. While browsers let you use any integer value and clip them at 0 and 255, Sass restricts the value of this input explicitly from 0-255. That original -10, 25555 value would blow up the sass renderer. We’re going to have to go in and fix every instance of this RGB function in order to make this conversion happen.
  41. Dan Na | @dxna This sucks because we have to

    go fix all of our broken CSS. 36 Here was one way of looking at our situation: man, this sucks because we have to go fix all of our broken CSS.
  42. Dan Na | @dxna This is awesome because we can

    fix all of our broken CSS. 37 Here’s the better way: this is awesome because we fix all of our broken CSS. Not only do we have the opportunity to fix all of our broken CSS in one fell swoop, but we can institute code styles programmatically to keep it clean and maintainable moving forward.
  43. Dan Na | @dxna scss-lint https://github.com/brigade/scss-lint 38 There’s this really

    awesome tool called SCSS-lint, and it’s a ruby gem, and you can have it look at Sass source code and tell you if it conforms to a set of lint rules that you define. So we could finally have consistency within our CSS when it comes to indentation, leading zeros, spacing and punctuation and a whole slew of other options. Once again our designers really lead the charge in defining our lint rules.
  44. Dan Na | @dxna Goals 1. Fix all errors in

    CSS that block compilation with libsass 2. Clean CSS source so there are zero lint-scss errors 3. Complete the conversion by renaming all cleaned files from .css to .scss 39 So here are the goals of this conversion. First, we’re going to fix all the errors in our CSS that block compilation with libsass. Concurrently to that we’re going to clean the formatting of our source so that we have zero lint errors. And lastly, our conversion is complete we can rename our CSS files to SCSS, run them through our lint with zero errors, and have all of our files perfectly compile with libsass.
  45. Dan Na | @dxna Open Questions 1. How do we

    make sweeping changes to our CSS source? 2. How do we confirm that our production site won’t break in small, difficult to detect ways? 40 This poses two questions: First, how do we do this? How do we actually make these changes across all of our files in a manageable way? Second, given that our site is too big and too complex to visually regression test every page, how can we confirm that in the process of making all these changes we won’t break our production CSS in small, difficult to detect ways?
  46. 41 The Wrong Way: Regular Expressions Before I talk about

    how we did this the right way, let’s talk about how not to do it. This is one of the top hits when you google “regular expressions gif.” I’m going to be using a JS-based regular expression syntax for the following examples. Also if you don’t know regular expressions don’t worry about it, it’s not that important to know the syntax for the purposes of this example, but in the right context regex feels like a superpower so I encourage you to learn them.
  47. Dan Na | @dxna Goal: Find any background color in

    CSS that’s set to green using regular expressions. 42 Let’s walk through a trivial example of using regex that gets unmanageable over time. Let’s say we want to identify any background color that’s green.
  48. Dan Na | @dxna body { background-color: green; } 43

    /\s(green);/g I’m going to have the markup at the top, the regular expression to match what I want across the bottom, and the match in green. This one is pretty simple; let’s match the word green, assuming it’s preceded by a space and ends with a semicolon. Cool.
  49. Dan Na | @dxna body { background-color: green; } 44

    /\s(green);/g section { color: green; } Oh shoot, but this also matches this section tag, that sets a font-color: green. That won’t work. Maybe we should specify that we want a background-color tag.
  50. Dan Na | @dxna body { background-color: green; } 45

    /background-color:\s(green);/g section { color: green; } Okay, that’s better. We’ll look for that background-color property, followed by a colon and a space, and that should differentiate background-colors. [x] Oh, shoot, this background-color won’t match. It’s missing that space between the colon and the property value.
  51. Dan Na | @dxna body { background-color: green; } 45

    /background-color:\s(green);/g section { color: green; } .green-box { background-color:green; } Okay, that’s better. We’ll look for that background-color property, followed by a colon and a space, and that should differentiate background-colors. [x] Oh, shoot, this background-color won’t match. It’s missing that space between the colon and the property value.
  52. Dan Na | @dxna body { background-color: green; } section

    { color: green; } .green-box { background-color:green; } 46 /background-color:\s?(green);/g Okay, simple enough. Let’s just make that space optional. Finally, things are looking good. [x] Oh crap, I completely forgot that you can define a background color in multiple ways. And the green in this declaration doesn’t end in a semicolon. Dang it.
  53. Dan Na | @dxna body { background-color: green; } section

    { color: green; } .green-box { background-color:green; } 46 /background-color:\s?(green);/g nav { background: url("./foo.jpg") top center green no-repeat; } Okay, simple enough. Let’s just make that space optional. Finally, things are looking good. [x] Oh crap, I completely forgot that you can define a background color in multiple ways. And the green in this declaration doesn’t end in a semicolon. Dang it.
  54. Dan Na | @dxna body { background-color: green; } section

    { color: green; } .green-box { background-color:green; } nav { background: url("./foo.jpg") top center green no-repeat; } 47 /background(-color){0,1}:.*(green);{0,1}/g Okay so let’s futz around with this, and we can make the -color part of background color optional, and that might appear 0 or 1 times, and then there’s a colon, followed by some amount of text, and then the word “green”, and then 0 or 1 semicolons. Cool, finally we can match the background-color of green. You can see how this would get really annoying. You’re adjusting a regular expression to add a space, or add a semicolon, or make something optional, and even when you think you’re done with a pretty good regex you’re not actually that confident it’ll work in all cases.
  55. 48 And this is what it feels like to do

    this for four days straight, because I did this for four days straight. But do not despair, for there is a better way.
  56. Dan Na | @dxna Abstract Syntax Trees (AST) 49 We

    can use abstract syntax trees, which are awesome. Abstract Syntax Trees sound intimidating and complicated but they’re not. ASTs are tree structures, not unlike JSON, that represent the structure and contents of code. It’s kind of like, once we strip away the formatting and spacing and all of the superficial stuff, what is the most meaningful representation of the underlying css? It’s clearer with an example.
  57. Dan Na | @dxna Rework CSS parser https://github.com/reworkcss/rework require(‘css’) 50

    We used the Rework CSS parser, which is great, and can be included in a nodejs by requiring the ‘css’ module on NPM.
  58. Dan Na | @dxna 51 body { background-color: green; color:

    red; font-size: 12px; } .main { color: blue; } Let’s look at this snippet of CSS. We have a body tag, a main class, a bunch of properties on each. How would an AST break up this CSS into its component parts?
  59. Dan Na | @dxna 52 body { background-color: green; color:

    red; font-size: 12px; } .main { color: blue; } Rules Well, first, CSS is composed of rules.
  60. Dan Na | @dxna 53 body { background-color: green; color:

    red; font-size: 12px; } .main { color: blue; } Selectors Those rules are made up of selectors, like applying specific CSS to the body element and any elements with a main class.
  61. Dan Na | @dxna 54 body { background-color: green; color:

    red; font-size: 12px; } .main { color: blue; } Declarations Each of those selectors are composed of declarations that define the CSS properties you’d like to apply to those elements.
  62. Dan Na | @dxna 55 body { background-color: green; color:

    red; font-size: 12px; } .main { color: blue; } Properties and Values And finally, each individual declaration has a property name and value. Let’s set the background- color to green, the text-color to red, and the font-size to 12px.
  63. Dan Na | @dxna 56 { "stylesheet": { "rules": [

    { "type": "rule", "selectors": [ "body" ], "declarations": [ … ], ] } } body { background-color: green; color: red; font-size: 12px; } Here’s how that CSS is represented as an AST. We’re breaking down the CSS on the left into its component parts: rules, selectors, and declarations. So within the array of rules, we have an object that represents the rule for the body selector, which has an array of declarations.
  64. Dan Na | @dxna 57 body { background-color: green; color:

    red; font-size: 12px; } "declarations": [ { "type": "declaration", "property": “background-color", "value": "green", ... }, { "type": "declaration", "property": "color", "value": "red", ... } ] And that array of declarations is itself composed of objects, which represent each property and value defined on that selector.
  65. Dan Na | @dxna ASTs transform code from strings into

    structured data. 58 So instead of dealing with source CSS, which may be riddled with differences in spacing or formatting, you deal with structured data in a format that you can expect. That’s the key here. ASTs transform code from strings into structured data. What does structured data enable you to do?
  66. Dan Na | @dxna Power of ASTs • Granularly target

    specific properties/selectors • Bound regex usage • Meaningfully compare CSS output 59 Instead of frustratingly iterating over a growing list of unmanageable regular expressions accounting for spaces and dashes and numbers and general human error, we can use ASTs to target specific properties that we knew were problematic. We can look for that background-color property or that background property specifically, and we can then use regex in a controlled, scoped manner to change those green values. It also allowed us to meaningfully compare CSS files that we changed. Let’s say you have two css files: one minified and one expanded. How would you know they’re the same? You parse them to ASTs and compare those ASTs. ASTs enable you to determine, despite any formatting or spacing differences, are those CSS files, at their core, the same.
  67. Dan Na | @dxna Equivalent ASTs meant that the CSS

    in production after our conversion was the same as the CSS before our conversion. 60 If the ASTs were the same before and after switching to Sass, no matter how we cleaned the CSS or formatted it to match our lint rules, we could be confident that the CSS we served to production after our conversion was the same as the CSS we served to production before our conversion. This saved us from the impossible task of visually regression testing our site.
  68. Dan Na | @dxna Fixed 171,244 scss-lint errors 61 Our

    conversion scripts fixed 171,000 lint errors.
  69. Dan Na | @dxna Conversion at a Glance • Hundreds

    of invalid CSS rules and selectors • Formatting @import paths • IE-specific selectors (filter:progid:….) and hacks 62 They also adjusted the formatting of hundreds of invalid CSS rules and selectors, as well as make our CSS import paths conform to Sass-style imports. They also addressed weird edge cases when it came to IE-specific selectors and hacks. But again, theory and practice are different in practice.
  70. Dan Na | @dxna Caveat: Converter scripts operate in a

    vacuum. 63 Here’s the caveat to the work to this point: iterating on converter scripts and fixing all of these CSS and lint errors is happening in a vacuum. We take all of our CSS source, copy it to some folder, and we iterate over these files with our converter scripts until we don’t have any errors. This process doesn’t capture how dynamic our codebase is — again, we push to production over 40 times a day. Our codebase is rapidly changing on a daily basis. So then we started to wonder: how can we test these converter scripts in a more realistic, constantly changing environment?
  71. 64 Gaining Confidence Which leads me to my next point:

    how did we build confidence that this stuff would work, not only in a vacuum, but in both of our dev and production pipelines?
  72. Dan Na | @dxna Gaining Confidence in Dev 65 So

    first let’s talk about gaining confidence in our scripts in dev.
  73. 66 Allison Local etsy.com Code This is how development works

    for engineers at Etsy. This is Allison and she is an engineer on the performance team. Allison does all her hacking on her own development version of etsy.com.
  74. 67 Request main.css CSS Process CSS Response Before Before our

    Sass conversion, CSS worked like this. There was a php script that lived in everyones developer environments that handled CSS requests. So, if Allison is working on a page that included main.css, that request for main.css would be sent to this php script.
  75. 68 CSS Process Request main.css CSS Response Find CSS file

    on disk Do stuff Before This script would then find main.css on disk, perform some file processing, and return the resulting CSS to the browser.
  76. 69 Sass Process Request main.css CSS Response After This is

    how things changed with the introduction of Sass. Everything stayed the same on the browser side, but we replaced that PHP code that used to handle our CSS processing with a node-js server we wrote from scratch.
  77. Dan Na | @dxna buildaSass • Node.js process running on

    VMs • node-sass (https://github.com/sass/node-sass) • Converter scripts baked in 70 We called this node-js server buildaSass. It would use node-sass, which is a node-js wrapper for libsass, to render Sass files to CSS. We would also add some extra file processing for source maps or other functionality as we saw fit. buildaSass is what enabled us to build confidence that our converter scripts would work under active development, because we baked those converter scripts into the server.
  78. 71 buildaSass Request main.css CSS Response After Let’s go back

    to this slide. What we did is we recruited a test group of developers and designers. Within this test group, we replaced their legacy PHP process with buildaSass. So when they refreshed their browser, all their CSS requests would go to buildaSass. It’s worth noting here that only CSS exists in our codebase at this time.
  79. 72 Request main.css CSS Response Convert CSS file to Sass

    Compile Sass using node-sass 
 to CSS buildaSass Normal CSS Processing Find CSS file on disk After This is what’s happening internally. buildaSass is different than the old PHP script in one major way — we were using our converter scripts to convert files to Sass, at request time, only within the server. So you’d make changes to main.css and refresh your browser. 
 The browser would request main.css, we’d find main.css on disk, convert main.css to sass using our scripts, compile that sass file with node-sass back down to CSS, and then do our normal processing and return the CSS response.
  80. Dan Na | @dxna Converting to Sass on the fly

    built confidence that our scripts would work with files under active development. 73 Converting to Sass on the fly built confidence that our scripts would work with files under active development. If things went wrong — if things looked off in the browser — those in the test group could ping us in chat and we could dig into what was wrong, and this worked to suss out some of the trickier edge-cases in our conversion scripts that we didn’t catch while working in a vacuum. We ran this test group for several weeks until the compiled Sass would run without incident.
  81. Dan Na | @dxna There is still no Sass in

    our codebase at this time. 74 I just want to underscore: There’s no checked-in Sass at this time, even though we’re actively compiling Sass. And this is only possible because libsass is fast enough to handle a dynamic conversion. If it wasn’t, returning CSS from the server would take forever and severely impact load times in dev. Pretty cool.
  82. Dan Na | @dxna Building CSS in Production 75 So

    that was dev. What about production?
  83. 76 CSS Source Legacy CSS Build PHP Process Here’s how

    our production CSS pipeline used to work: we’d push the button to deploy our site, our CSS would pass through the old PHP code that would concatenate, minify and do a bunch of stuff, and then that CSS would be sent off to web servers around the world and be live in production.
  84. 77 CSS Source Legacy CSS Build Converter Sass Build New

    CSS Build PHP Process buildaSass To build confidence on deploys we basically did the same thing we did in dev. We used buildaSass to convert to Sass on the fly and compile that back down into CSS. The only difference is we wouldn’t ship that new output CSS to production servers, we’d just let it sit on some servers internally. So we would build two versions of CSS for every deploy: the legacy CSS build which we would ship live, and the new Sass build, which we wouldn't ship anywhere.
  85. 78 CSS Source Legacy CSS Build Converter Sass Build New

    CSS Build PHP Process buildaSass AST Differ Parse to AST Parse to AST We could then compare the output of both builds. We wrote some code that would parse the CSS of both processes into Abstract Syntax Trees and diff the ASTs to make sure they were the same. Remember, this is how we’d circumvent the need to visually regression test each page. If ASTs match between two CSS files, no matter the formatting differences, no matter how they’re both minified, they’re the same.
  86. 79 CSS Source Legacy CSS Build Converter Sass Build New

    CSS Build PHP Process buildaSass 100% 0% After running things on both the developer and production side for several weeks, we could then begin the process of a staged rollout of our buildaSass output. This is how things looked initially: 100% of our production traffic was served our legacy CSS build.
  87. 80 CSS Source Legacy CSS Build Converter Sass Build New

    CSS Build PHP Process buildaSass 50% 50% We then moved to 50% of production traffic and left it for a couple days. What this means is that one out of every two users would get the new buildaSass CSS, whereas the other user would get the legacy CSS. We could monitor graphs and logs and our forums to make sure errors weren’t happening in places across the site.
  88. 81 CSS Source Legacy CSS Build Converter Sass Build New

    CSS Build PHP Process buildaSass 100% Eventually, after several days without incident, we turned the buildaSass output on for all of our users.
  89. 82 Sass Source Sass Build 100% New CSS Build buildaSass

    And this was the final piece of the puzzle. Finally we could delete all of our CSS source, replace it with the converted Sass source, remove the conversion scripts from buildaSass and turn everything on for everyone in dev and production.
  90. Dan Na | @dxna We experienced no outages or visual

    regressions across any of our pages, and no disruptions in dev or production. 83 And it was kind of shocking. As intimidating as a change of this scale was, we experienced “no outages or visual regressions” across any of our pages, and no disruption to the site or developer workflows. It really made the time we spent focusing on building confidence in our changes so worth it.
  91. Dan Na | @dxna There is only Sass in our

    codebase at this time. 84 So finally we reached the goal we came for. There was only Sass in our codebase at this time.
  92. Dan Na | @dxna 85 And as a random sidenote,

    since we had to simultaneously delete all of our CSS files and check-in all of our Sass files, I pushed a 1.2 million line commit to our internal github. That’s just kind of the cherry on top. I’m pretty sure I’m not topping that one for the rest of my career.
  93. 86 Building for the Future As my final topic, let’s

    talk about how we’ve put some things in place to help make our Sass maintainable and usable moving forward.
  94. Dan Na | @dxna scss-lint https://github.com/brigade/scss-lint 87 You’ve already seen

    this slide, but it’s worthwhile to put it in again. SCSS-lint is amazing, and we use it, and it will help keep the structure and formatting of our Sass consistent for as long as we use Sass. Just to note, there’s a new project named sass-lint that is the nodejs version of this tool. But the hard thing about suddenly instituting a lint is that it can be an annoyance to institute coding conventions when they didn’t exist previously.
  95. Dan Na | @dxna Sass Education Efforts • Brown-bag lunch

    sessions • Documentation on internal wiki • Available on internal chat 88 So there was a concerted education effort. We worked with our designers to lead brown bag lunch sessions, produce extensive documentation on our internal wiki, as well as be available on our internal chat to answer any questions from anyone in the company. And we still do that to this day. But, in my opinion, all this was not the most effective way we educated people on how to write proper Sass. By far our best education decision was to implement our in-browser SCSS lint.
  96. 89 Let’s say you’re a developer, happily hacking away on

    your local copy of etsy.com, and you make a change to a few Sass files. Little did you know those changes actually fail our lint rules. [x] When you refresh your page, we send a request to our buildaSass server that says “lint the Sass files included on this page.” We return the results of that lint into a closable dialog on the top right of the screen. [x] The dialog lists each file with errors, what those errors are, and the line numbers of where those errors occurred. This way developers and designers can have an active feedback loop about what rules they may be violating, and in a less blocking way than having to run through an entire suite of tests before pushing code. And naturally over time, as you get more familiar with the lint rules, the incidence of lint errors decreases.
  97. 89 Let’s say you’re a developer, happily hacking away on

    your local copy of etsy.com, and you make a change to a few Sass files. Little did you know those changes actually fail our lint rules. [x] When you refresh your page, we send a request to our buildaSass server that says “lint the Sass files included on this page.” We return the results of that lint into a closable dialog on the top right of the screen. [x] The dialog lists each file with errors, what those errors are, and the line numbers of where those errors occurred. This way developers and designers can have an active feedback loop about what rules they may be violating, and in a less blocking way than having to run through an entire suite of tests before pushing code. And naturally over time, as you get more familiar with the lint rules, the incidence of lint errors decreases.
  98. 89 Let’s say you’re a developer, happily hacking away on

    your local copy of etsy.com, and you make a change to a few Sass files. Little did you know those changes actually fail our lint rules. [x] When you refresh your page, we send a request to our buildaSass server that says “lint the Sass files included on this page.” We return the results of that lint into a closable dialog on the top right of the screen. [x] The dialog lists each file with errors, what those errors are, and the line numbers of where those errors occurred. This way developers and designers can have an active feedback loop about what rules they may be violating, and in a less blocking way than having to run through an entire suite of tests before pushing code. And naturally over time, as you get more familiar with the lint rules, the incidence of lint errors decreases.
  99. Dan Na | @dxna 1. No @extend or @function declarations

    90 In addition to the SCSS-lint rules, we’ve enforced some of our own rules about Sass. We don’t allow any use of @extend or custom @functions. The simple reason is because we want to protect developers from accidentally bloating their CSS, which is a performance concern. In an ideal world, extend and function declarations can add a lot of clarity to code when everyone has a perfect understanding of exactly what they do. In the real world, however, @extend can be super confusing, especially if you’re not a Sass expert. When implementing Sass we want to gain the benefits of programming constructs in our CSS without introducing their pitfalls — namely that too much abstraction can result in styles that are harder to understand, re-use, and even eliminate over time. And we’ve found that when building for the long term, not just in CSS but everywhere, optimizing for code that is easier to understand and easier to throw away is far more useful than typing fewer characters.
  100. Dan Na | @dxna 2. Global @mixin declarations only 91

    Second, we only declare global mixins to be used across the entire Etsy codebase. And this is again tied to abstraction and reusability — when we looked at our initial set of proposed mixins, we found that we were better off sharing them across all pages on the site. Mixins should do one thing, generically, with no side effects, and part of the contract of adding a mixin is that you’re on the hook for making sure that it’s compatible with all the browsers we care about. We want to avoid the scenario of having 20 different mixins that all define the same functionality.
  101. Dan Na | @dxna 3. Namespaced variables only, in project-specific

    folder structure 92 Our third rule is that we allow namespaced variables only, and those variables must be declared in a file in a project-specific folder structure. Mixins and variables in Sass both operate in a global scope, meaning variables and mixins with very generic names — “spacing”, “padding”, “margin” — can result in conflicts and confusion, both of which inhibit reuse over time.
  102. Dan Na | @dxna 93 /css/_variables.scss: $padding: 4px /css/cart/header/_variables.scss: $cart-header-padding:

    4px Our way around this is to make sure that variables are declared in a project specific folder and namespaced. So anyone looking at the code knows that your variable isn't just “padding”, but it’s “cart-header- padding.” This also greatly enhances searchability of the codebase.
  103. Dan Na | @dxna 4. Nesting level < 4 94

    Finally, we limit nesting levels to 4, mostly because over-nesting your CSS is a code smell that your selectors are too specific.
  104. Dan Na | @dxna Think about code as data, not

    strings. 96 It’s easier to reason about code when you approach it as data and not strings. Our use of Abstract Syntax Trees to meaningfully manipulate and compare CSS greatly simplified and bounded our problem space. And ASTs are a construct in most languages, and we use them to confidently make wide sweeping changes to our JavaScript codebase as well. I’ll link in my talk notes to my teammates Sufian Rhazi’s talk from this year at JSConf Budapest about this exact topic.
  105. Dan Na | @dxna We would love and use an

    official libSass AST. 97 As an addendum to that: if you work on Sass, we’d love and we would use a libSass AST that we could manipulate in Javascript.
  106. Dan Na | @dxna Build confidence in both programs and

    people. 98 Another learning from this project is that it’s important to build confidence in both programs and people. It was important for us not only to make sure our conversion scripts worked in a vacuum but also that they worked under active development. The sass conversion is also useless without its associated education effort: the brown bag lunch sessions, documentation and activity in chat. It was important to consider, if we were going to implement a lint, how could the lint be implemented in a manner that helped people get their work done and didn’t piss people off? We tend to focus in engineering on technologies, but engineering is not just about technology: engineering is people. It’s important to make sure the constructs you put in place when introducing technology include considerations for how those technologies will be used.
  107. Dan Na | @dxna Open source contributors = superheroes. 99

    Finally, infinite props to everyone who works on open source tooling, like Sass, and libsass, node- sass, scss-lint, and sass-lint, and the countless other pieces of software that help all of us across the industry do our jobs. We don’t say it enough but we are enormously appreciative of the work you do.
  108. codeascraft.com etsy.com/careers Thank you. http://talks.danielna.com/sassconf-2015 And that’s all I got.

    We’re hiring and here’s a link to my speaker notes. Thanks a lot.