Slide 1

Slide 1 text

REFACTORING CSS WITHOUT LOSING YOUR MIND Harry Roberts | CSSconf Argentina | August 2016

Slide 2

Slide 2 text

HOLA, ARGENTINA!

Slide 3

Slide 3 text

HI, I’M HARRY @csswizardry Consultant Front-end Architect CSS Architecture, Performance, Scale

Slide 4

Slide 4 text

HI, I’M HARRY @csswizardry Consultant Front-end Architect CSS Architecture, Performance, Scale And I really hate refactoring CSS

Slide 5

Slide 5 text

WHAT DO WE TALK ABOUT WHEN WE TALK ABOUT REFACTORING?

Slide 6

Slide 6 text

— Martin Fowler “…the process of changing a software system in such a way that it does not alter the external behaviour of the code, yet improves its internal structure.”

Slide 7

Slide 7 text

THIS ISN’T* FOR OUR USERS

Slide 8

Slide 8 text

THIS ISN’T (DIRECTLY) FOR OUR USERS

Slide 9

Slide 9 text

THREE KINDS OF REFACTORING 1. As-You-Go: I hard-coded it all to see if it would work; now I need to make it production-ready. 2. Technical Debt: We built what we could in the time we had; we want to tidy things up. 3. Rewrites and Overhauls: It’s become too difficult and expensive to maintain; we need to rewrite.

Slide 10

Slide 10 text

As-You-Go Technical Debt Rewrites and Overhauls Project Lifespan Cost/Scale of Refactor

Slide 11

Slide 11 text

TECHNICAL DEBT

Slide 12

Slide 12 text

— Maiz Lulkin, csswz.it/2adZH3M “A ‘debt’ means that you [acquired] something now for a long- term financial burden. This burden is not just about repaying what you got: there is ‘interest’. It means that, even if you pay your debt timely, you’ll pay more than you took, and if you don’t, your debt will keep increasing […] if you ignore a debt long enough, it will become unpayable and you’ll go ‘bankrupt’.”

Slide 13

Slide 13 text

TECHNICAL DEBT We’re going to incur some of it, fact. It’s vitally important that we keep up repayments. People forget that debt repayments incur interest. Schedule in bug-fixing and tech-debt cleanup every sprint. Make and prove the business case for refactoring.

Slide 14

Slide 14 text

WHEN (AND WHAT) TO REFACTOR

Slide 15

Slide 15 text

YOU SHOULD ALWAYS BE REFACTORING IN SOME FORM OR ANOTHER

Slide 16

Slide 16 text

WHEN TO REFACTOR If the (projected) cost of maintenance is higher than rewriting. If the current version is slowing you down. If the new version provides tangible benefit.

Slide 17

Slide 17 text

Existing Code Refactored Code Cost Now 2 7 Cost Next Time 2 0.25 Cost Next Time 2.5 0.25 Cost Next Time 2.5 0.25 Total Cost 9 7.75

Slide 18

Slide 18 text

INVESTMENT IS THE OPPOSITE OF DEBT

Slide 19

Slide 19 text

WHEN NOT TO REFACTOR If you’re not actually being slowed down by something. If it’s something that can be ignored or avoided. If it’s something that can be captured by a rewrite later on. If a rewrite is the better solution.

Slide 20

Slide 20 text

“We want to rewrite our CSS onto BEM.”

Slide 21

Slide 21 text

NEW THINGS Honestly, I love BEM as much as the next person… …but taking two weeks out to refactor your CSS onto it is not going to pay itself back very quickly. Is it actually worth it?

Slide 22

Slide 22 text

“We think the code for this nav is pretty ugly.”

Slide 23

Slide 23 text

UGLY CODE Is it causing actual problems for you right now? How often do you have to actually work with this code? Can we just leave it to keep working as it is?

Slide 24

Slide 24 text

REFACTORING TUNNELS

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

$ git reset --hard origin/master

Slide 27

Slide 27 text

AVOID LONG REFACTORING TUNNELS

Slide 28

Slide 28 text

REFACTORING TUNNELS Avoid refactoring anything that runs through the entire project. Takes too long, and leaves things messy. Huge deltas to merge (i.e. conflicts). Easy to (inadvertently) introduce more problems. Instead, pick off things with a limited and clear scope.

Slide 29

Slide 29 text

REFACTORING TUNNELS Find a short tunnel (e.g. refactoring just the nav). Get the work completed. Either get back onto features if needed… …or pick another short tunnel. Rinse and repeat. The site refactors itself.

Slide 30

Slide 30 text

REFACTOR IN ISOLATION

Slide 31

Slide 31 text

REFACTOR IN ISOLATION Don’t (re)build features into a dirty codebase. You’ll be relying on a stale environment. Fire open JSFiddle and build the new one there. Port it back into your project. Do any tidy-up work there.

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

SAFE TO ASSUME WE’RE GOING TO EXPERIENCE SOME BREAKAGES

Slide 35

Slide 35 text

ALL: INITIAL;

Slide 36

Slide 36 text

ALL: INITIAL; Effectively stops inheritance. Prevent legacy styles from leaking into fresh work. A very progressive way of defending against legacy.

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

.nav-primary { all: initial; font-size: 12px; font-family: sans-serif; } .nav-primary__link { all: initial; display: inline-block; } These rules will not get inherited from an ancestor anymore.

Slide 40

Slide 40 text

.nav-primary { all: initial; } .nav-primary__link { all: initial; display: inline-block; font-size: 12px; font-family: sans-serif; } So we have to define them on the leaf node.

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

DEFENCE.CSS

Slide 43

Slide 43 text

DEFENCE.CSS What happens when you need to run refactored code and legacy code side-by-side?

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

SKY UI TOOLKIT Had an existing/legacy toolkit. Modernised design and architecture required. New toolkit developed. Had to be rolled out very gradually (big project). Homepage was first candidate. Site now using old and new toolkits.

Slide 46

Slide 46 text

WE HAD TO RUN OLD AND NEW IN TANDEM

Slide 47

Slide 47 text

? old.css new.css

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

defence.css

Slide 50

Slide 50 text

DEFENCE.CSS A whole new project. Run OSS-style: other teams consume and contribute. Exists only to fix temporary/transient fallout and breakages. Stuffed full of crude fixes, !importants, hacks. Just type until it looks okay. This is the worst CSS you will ever write. And that’s okay. And what’s in this file…?

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

.RF-* CLASSES

Slide 53

Slide 53 text

.RF-* CLASSES Prefix any refactored classes with rf-. This means developers can see which classes are new… But it also means we can do this:

Slide 54

Slide 54 text

/** * If it’s a class containing the string rf-, * put a green border around it. */ [class*="rf-"] { outline: 5px solid green; }

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

THIS IS THE WORK WE’VE DONE

Slide 57

Slide 57 text

/** * If it’s a class, but isn’t a class containing * the string rf-, put a red border around it. */ [class]:not([class*="rf-"]) { outline: 5px solid red; }

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

THIS IS THE WORK LEFT TO DO

Slide 60

Slide 60 text

REF-HACK-TORING SPECIFICITY

Slide 61

Slide 61 text

HACKING SPECIFICITY Dealing with specificity on a legacy project. Newly refactored work being overridden by existing selectors. !important to manage it safely. We can hack specificity with minimal side effects.

Slide 62

Slide 62 text

...

Slide 63

Slide 63 text

#bar { color: blue; } .foo { color: green; } a { color: red; }

Slide 64

Slide 64 text

HACKING SPECIFICITY Three differently weighted selectors. All working against their source order. We could use !important to force different precedence. Or we can hack our specificity around.

Slide 65

Slide 65 text

html [id="bar"] { color: blue; } html .foo { color: green; } :root a { color: red; } Element & class equivalent Element & class Class equivalent & element

Slide 66

Slide 66 text

/** * Same specificity as one class. */ [id="bar"] {} /** * Same specificity as two classes. */ .foo.foo {} /** * Same specificity as a class and an element. */ :root a {}

Slide 67

Slide 67 text

THESE ARE HACKS

Slide 68

Slide 68 text

THESE ARE HACKS Ideally: Refactor until you don’t need the hacks. Realistically: Use one of these hacks to solve the problem. Never: Use !important.

Slide 69

Slide 69 text

SHAME.CSS

Slide 70

Slide 70 text

— Harry Roberts, csswz.it/113CPn2 “The idea of shame.css is that you have a totally new stylesheet reserved just for your hacky code. Code you have to write to get the release out on time, but code that makes you ashamed.”

Slide 71

Slide 71 text

A DUMPING GROUND FOR ALL THOSE BITS OF CSS YOU’RE A LITTLE BIT ASHAMED OF

Slide 72

Slide 72 text

ISOLATE HACKS Hacks are inevitable. Isolate and signpost them. Makes everyone else aware of them. Easy to find and fix things.

Slide 73

Slide 73 text

/** * The `.promo a {}` selector keeps overriding the * button’s styles. Increase its specificity here * until I get chance to refactor the promo boxes. * * Harry Roberts 2016-05-01 */ .btn.btn { text-decoration: none; }

Slide 74

Slide 74 text

SHAME.CSS Self-writing todo list. Keeps good code nice and clean (Broken Windows Theory). See which parts of the codebase are particularly problematic. $ git blame shame.css

Slide 75

Slide 75 text

$ git blame shame.css

Slide 76

Slide 76 text

A SECOND CHANCE

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

“I’m a civil engineer by trade… we don’t get to rework our architecture.”

Slide 79

Slide 79 text

REFACTORING IS A SECOND CHANCE THAT MOST INDUSTRIES DON’T GET

Slide 80

Slide 80 text

REMEMBER…

Slide 81

Slide 81 text

REMEMBER Prevention is cheaper than the cure. Technical Debt is fine, just make sure you keep up repayments. Only refactor once you can see tangible benefit. Avoid long Refactoring Tunnels. Isolate and highlight both hacks and refactored work.

Slide 82

Slide 82 text

— Robert Baden-Powell “Always leave the campground cleaner than you found it.”

Slide 83

Slide 83 text

THANK YOU Harry Roberts [email protected] speakerdeck.com/csswizardry csswizardry.com @csswizardry