Konmari Your Code: Finding Joy in Refactoring

A presentation at Reactathon in December 2020 in by Becca Bailey

Slide 1

Slide 1

Slide 2

Slide 2

Slide 3

Slide 3

For those of you who are not closely following American and/or Japanese popular culture, Marie Kondo is an author, Manga character, Net ix star, and the world-renowned patron saint of tidying.

Slide 4

Slide 4

A couple years ago, I decided to read her book and attempt to put her principles to practice in my messy 600 square-foot Chicago apartment. I picked up her book expecting to hate it, but actually, I didn’t.

Slide 5

Slide 5

In addition to learning some new folding skills, I learned about how reducing the number of things I own helps me to better appreciate the things I have. And when we know and appreciate the things we have, we’re less likely to leave them lying around on the floor.

This got me thinking about how to connect these practices to the code I write. There are many days when none of my code brings me joy, but unfortunately just throwing it away is not a viable option. This brought me back to the concept of refactoring.

Slide 6

Slide 6

What is refactoring?

Slide 7

Slide 7

According to book ~Refactoring~, “Code refactoring is the process of restructuring existing computer code—changing the factoring— without changing its external behavior.” –Martin Fowler

Slide 8

Slide 8

Factoring refers to the internal parts of a program or the implementation details. Note that making breaking changes to the public-facing API of a library or top-level component, while often necessary, is not by-definition Refactoring. Refactoring means changing the inside without changing the outside.

Slide 9

Slide 9

This brings me to my personal definition of refactoring. To me, refactoring is an opportunity to change your mind to ask yourself, what decisions would I make differently if I could write this code again?

Slide 10

Slide 10

Sometimes when we talk about refactoring, we are referring to the process of modernizing our syntax.

Slide 11

Slide 11

In React, this might involve refactoring from class to function components, replacing deprecated lifecycle methods, or converting state and lifecycle methods to hooks.

Slide 12

Slide 12

I think this is a separate concern from refactoring for code style and readability. Modernizing your syntax is a part of refactoring, but it’s not the entire reason you refactor.

Slide 13

Slide 13

You might discover that using hooks, context, suspense, or other modern React APIs might aid you in your pursuit of human-readable code, but in my view, modern syntax is a tool, and not an end-goal when refactoring.

Slide 14

Slide 14

Refactoring isn’t just about code quality either. In my experience, the pursuit of artfully crafted software can lead to a lot of pride about your work, but unless you’re deliberate about respecting deadlines, embracing alternate viewpoints and and collaborating with your team, it really just makes software engineering more exclusionary to people who don’t subscribe to a specific set of engineering principles.

Slide 15

Slide 15

Now one of the things I really loved about Marie Kondo’s philosophy is that it’s not just about buying stuff from the Container Store and re-decorating your house to make it look like a Martha Stewart magazine cover. Likewise, the end goal of refactoring isn’t to have perfect or beautiful code for its own sake.

Slide 16

Slide 16

When do I refactor?

Slide 17

Slide 17

When I reorganized my apartment a couple years ago, my goal was to feel less stressed every time I came home from work. After dealing with this anxiety for a few months, I knew it was time to reorganize. Likewise, there are some things that tell me it’s time to refactor my React code.

Slide 18

Slide 18

My general rules are to refactor when your code is: 1. Hard to test 2. Hard to understand 3. Hard to change

If you find yourself mocking out a lot of external libraries, walking on eggshells around your codebase, or scratching your head a lot when you try to read your code, it’s might be time to refactor.

Slide 19

Slide 19

Out-of-control props Nested conditionals Confusing variable names Components doing too many things

But more specifically, here are some things I see in React projects that tell me it’s time to refactor.

Slide 20

Slide 20

These are general best-practices as I know them. But if you do your job for a while, do regular code reviews, and practice refactoring, I promise you will develop your own sense for knowing what to refactor.

Slide 21

Slide 21

A couple more things to note as you go on your refactoring journey… First of all, It’s really common to think about refactoring as a negative thing- as something that happens only when we write “bad” code.

Slide 22

Slide 22

I call this the “git blame refactoring”.

Slide 23

Slide 23

However, refactoring doesn’t mean that you (or someone else) did something wrong. Maybe you made the best choice you could with the knowledge you had in the moment, but now you have more information or more experience and you have changed your mind. It’s okay to change your mind. There’s no shame in refactoring, or having someone else refactor your code.

Slide 24

Slide 24

A key tenet of Marie Kondo’s philosophy is to start with discarding. While I know most of us have some amount of legacy code to maintain and we don’t have the ability to throw out everything that doesn’t bring us joy, it’s also not unusual for us to hold onto code that we don’t need anymore.

Slide 25

Slide 25

Maybe it’s a class that your coworker wrote a year ago that you don’t quite know what to do with, or some utility functions that are no longer useful, or some old comments that don’t apply anymore. Perhaps we’re afraid to get rid of code because we didn’t write it, we don’t understand it, or we’re uncertain about whether we’re going to need it someday.

Slide 26

Slide 26

git checkout .

Discard it. Worst case scenario, we can use three magic words to bring it all back again. This is one of my favorite things about programming—no matter how messy things get it’s easy to reset everything and try again.

Slide 27

Slide 27

I don’t know about you, but the practice of refactoring isn’t always a joyful experience. Making changes to the structure of our components can result in confusing errors, test failures, and unintentional changes to the behavior of our components. Have you ever introduced a bug by refactoring? I know I have.

Slide 28

Slide 28

This is why I love writing tests. Testing sounds stressful, especially if you’re not used to doing it. But believe me when I tell you that this is the number one thing you can do to transform your refactoring work into a more relaxing experience.

Slide 29

Slide 29

In order to do this, I need to write tests that tell me about the behavior of my component. Testing is important, but not all tests are made alike. Here’s an example.

Slide 30

Slide 30

Let’s say I have a simple class component. I’ll call this a drawer, since Marie Kondo loves putting things in drawers.

Slide 31

Slide 31

I have some state

Slide 32

Slide 32

And a render function with some conditional logic. I don’t know about you, but this logic isn’t super easy for me to understand, so this is why I’m refactoring this component.

Slide 33

Slide 33

I can test that this component works.

Slide 34

Slide 34

And let’s say this component has some existing tests. This is a little less common now, but if this code has been around for a few years there’s a good chance I’m using a test renderer like Enzyme to test my code.

Enzyme allows me to access the props and state of my component in order to make test assertions against them.

Slide 35

Slide 35

I run my tests, and everything works great so far.

Slide 36

Slide 36

Okay now let’s start refactoring. I know I said that we shouldn’t refactor just to modernize syntax, but hooks are cool so I’ll start by switching this class component out for a function with a useState hook.

Slide 37

Slide 37

Slide 38

Slide 38

Woah, hold on now. When I run my tests again something is broken, even though I know the component works the same way it did before.

Slide 39

Slide 39

This tells me something about my tests - the things I was testing were implementation details. If I can break my tests by changing the name of my state variable or changing an internal function name, this makes refactoring difficult. The thing that I care about when refactoring is the way this component behaves.

Slide 40

Slide 40

If I look at this component from the perspective of a user, I can see that when I click the open button, the contents are visible. When I click the close button, the contents are hidden again.

Slide 41

Slide 41

While I can still use Enzyme to make these kinds of assertions as well, I’m going to switch my next test to use React Testing Library, which is a test renderer built for these kinds of behavior-driven integration tests. What’s the difference? Well, now I’m not using any internal component APIs.

Slide 42

Slide 42

Now I’m rendering my component

Slide 43

Slide 43

Clicking the button with the text “Open”

Slide 44

Slide 44

And expecting my content to appear in the HTML. I no longer care about my inner props or state.

Slide 45

Slide 45

This test passes for both versions of my component. Once I add a couple more tests, I should be good to start making more changes to this component without fear of breaking things.

Slide 46

Slide 46

Here’s the part that makes refactoring really satisfying for me: having the ability to make small, incremental changes while relying on my tests as the source of truth.

Slide 47

Slide 47

Now, I can gradually untangle these nested conditionals.

Slide 48

Slide 48

Slide 49

Slide 49

Slide 50

Slide 50

Slide 51

Slide 51

The final result might tell you a little about the kind of code I really appreciate - I haven’t made it any shorter, but instead of three conditionals there is just one. There might be a little bit more repetition, but it’s easier for me to tell what is going on.

Slide 52

Slide 52

Now this isn’t strictly related to refactoring, but along the way you might discover another pain point, which is file structure and code organization.

Slide 53

Slide 53

Marie Kondo would say that the goal of re-organizing isn’t just to make it easy to find your belongings, but also to put them away. For example, the shoes you wear every day probably shouldn’t be stored in a bin under your bed, because you’re never going to remember to put them there.

Slide 54

Slide 54

Likewise, the goal of file organization isn’t just to make it easier to find things, but to make it easier to know where to put new code.

There are lots of different philosophies on this, but my basic rule of thumb is to group things that are used together, and to avoid deep nesting. If your imports are a mile long, it might be time to re-think your component organization.

Slide 55

Slide 55

Every time we refactor, we have an opportunity to leave something a little better than we found it.

Slide 56

Slide 56

Now I haven’t sent out an official survey, but I’m willing to bet that the number one reason why we don’t refactor all our code all the time is because we don’t have time. When there are looming deadlines, and any number of things going on in the world right now, the struggle is real.

Slide 57

Slide 57

Rather than refactoring all at once, the more we can make refactoring a part of our daily routine, the easier it will be to find the time and to get buy-in from our team. It doesn’t have to be a prioritized item on your backlog—it can just be something you take an extra few minutes to do while you’re working on a feature. I don’t want to get anyone in trouble here, but I’m going to say that maybe you don’t even need to mention to your product manager that you’re doing it.

Slide 58

Slide 58

And speaking of product managers, how do I get other people on board, especially when we have bigger refactoring work to do? Because sometimes this is really where it gets tricky.

Slide 59

Slide 59

If we don’t have a team that values thoughtful code reviews and collaboration, chances are there will be someone organizing their one little drawer while the rest of your house stays messy. Or worse, you will all be organizing your drawers, but there will be a different system for each one.

Slide 60

Slide 60

One of the best things you can do for your team is to be consistent in code quality and style so the goals when refactoring are clear.

Slide 61

Slide 61

Sometimes it helps to document our style and testing practices. Even if there are inconsistent parts of the codebase, it helps to have a shared idea of what we’re working towards when refactoring.

Slide 62

Slide 62

Like the end goal of tidying up is to better appreciate the things you own, the end goal of refactoring is to better understand your own code and build systems that other people can also use and understand.

Slide 63

Slide 63

Now, this might be a little out there for you, but I think Marie Kondo would say that the key to refactoring well is gratitude for the work you have already done.

Slide 64

Slide 64

Gratitude is the antidote to the scarcity mindset that tells us we need to stockpile twenty-three pairs of jeans just in case the apocalypse happens and we can never go to the Gap again (which admittedly sounded a lot crazier when I was writing this talk nine months ago)

Slide 65

Slide 65

We can have gratitude, even for the things we are getting rid of.

“You’ll be surprised at how many of the things you possess have already ful lled their role. By acknowledging their contribution and letting them go with gratitude, you will be able to truly put the things you own, and your life, in order.” –Marie Kondo

When I approach my refactoring process with gratitude, I can acknowledge that I learned something from this code, even if the thing I learned is that I don’t want to write code like that anymore.

Slide 66

Slide 66