Skip to content

Instantly share code, notes, and snippets.

@wataruoguchi
Created January 22, 2021 17:46
Show Gist options
  • Save wataruoguchi/8e9ab579d3fa23a310a5e3e9132f9682 to your computer and use it in GitHub Desktop.
Save wataruoguchi/8e9ab579d3fa23a310a5e3e9132f9682 to your computer and use it in GitHub Desktop.
Refactoring - Chapter 1: Refactoring: A First Example

Refactoring

Chapter 1: Refactoring: A First Example

The Starting Point

Comments on the Starting Program

... when I change the system, there is a human involved, and humans do care.

.

When you have to add a feature to a program but the code is not structured in a convenient way, first refactor the program to make it easy to add the feature, then add the feature.

The First Step in Refactoring

Whenever I do refactoring, the first step is always the same. I need to ensure I have a solid sets of tests for the section of code.

Ask myself:

  • Do you have tests?
  • Do you have code that’s testable?
  • Do you have skills to make our code testable?

Decomposing the statement Function

  • Refactoring technique : Extract Function

Refactoring changes the problems in small steps, so if you make a mistake, it is easy to find where the big is.

Push, merge, ship, and iterate with small changes. The author does commit after each successful refactoring. -> we should all learn git-squash

I take a look at what I’ve extracted to see if there are any quick and easy things I can do clarify the extracted function. The first thing I do is rename some of the variables...

Name convention. Generally in JavaScript, using camel case for variables and functions. Constants are in upper case, pascal case for class names. Also, it should describe what it is. It’s ok to be super long, if that help other developers read the code. To prevent making it long, use class so you can add context.

For example,

const authorizedUserFullName = “Luke Skywalker”

It can be:

class AuthorizedUser {
  constructor(id, firstName, lastName) {
    this.id = id;
    this.fullName = `${firstName} ${lastName}`;
  }
...
}

Even more:

class AuthorizedUser {
  constructor(id, firstName, lastName) {
    this.id = id;
    this.fullName = new FullName(firstName, lastName);
  }
}

// this is called "value object"
class FullName {
  constructor(firstName, lastName) {
    this.value = `${firstName} ${lastName}`;
  }
}

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

We need empathy. Empathy to your coworkers. Craftsmanship and professionalism come together.

Removing the play Variable

Removing / adding a parameter to a function is a big deal. Each parameter may need to interact with others. The number of combinations will be N * (N-1). (N is the number of parameters).

The great benefit of removing local variables is that it makes it much easier to do extractions, since there is less local scope to deal with.

The other benefit I can think of is it keeps the parameter “immutable”. With a variable, it’s possible that someone will rewrite, or update the variable anytime. This is not immutable (but “mutable”). Mutable variables are hard to track, thus hard to change its implementation.

  • Refactoring technique : Inline Variable

Extracting Volume Credits

It reminds me of this tweet.

https://twitter.com/stemmlerjs/status/1245785484233707523?s=21

Boring code:

  • is consistent
  • is not clever
  • is easily understandable
  • makes locating features easy
  • adheres to Principle of Least Surprise (things do what I assume they would)
  • takes a minimal amount of effort to change
  • uses the right tool (paradigm, language) for the job

"It makes locating features easy."

Removing the format Variable

  • Refactoring technique : Change Function Declaration

In the book, he says “formatAsUSD would be a bit too long-winded” - remember, we’re coding for developers. If it describes great, that’s improving developer experience better. I’d use long names.

Removing Total Volume Credits

  • Refactoring technique : Split Loop
  • Refactoring technique : Slide Statements
  • Refactoring technique : Replace Temp with Query
  • Refactoring technique : Extract Function
  • Refactoring technique : Inline Variable

Additionally, for loop is not a great pattern. It introduce a variable that you need to pay attention to. I would:

function totalVolumeCredits() {
  return invoice.performances.reduce(function (acc, perf) {
    return acc + volumeCreditsFor(perf);
  }, 0);
}

“reduce” is more familiar for many developers. It has a context of “Making something out of an array.”

Status: Lots of Nested Functions

Splitting the Phases of Calculation and Formatting

The problem: broken-out functions are nested.

  • Refactoring technique : Split Phase
  • Refactoring technique : Extract Function

Status: Separated into Two Files (and Phases)

When programming, follow the camping rule: leave the code base healthier than when you found it.

Reorganizing the Calculations by Type

The amountFor function highlighted the central role the type of play has in the choice of calculations - but conditional logic like this tends to decay as further modifications are made unless it’s reinforced by more structural elements of the programming language.

  • Refactoring technique : Replace Conditional with Polymorphism

Creating a Performance Calculator

  • Refactoring technique : Change Function Declaration

Moving Functions into the Calculator

  • Refactoring technique : Move Function
  • Refactoring technique : Inline Function

Note: class is handy, however, we can freely change class members. This will confuse people, because it’s so hard to tell the state of class members. When the class is huge, class members tend to be used like global variables. In my opinion, they should not be changed (immutable). There’s a good way to add the constraint.

Object.freeze();

For example,

class Person {
  constructor(name) {
    this.name = name;
    Object.freeze(this);
  }
  updateName(newName) {
    this.name = newName; // error
  }
}

const person = new Person(“Wataru”);
person.name = “Watura”; // error

Making the Performance Calculator Polymorphic

  • Refactoring technique : Replace Type Code with Subclasses
  • Refactoring technique : Replace Constructor with Factory Function
  • Refactoring technique : Replace Conditional with Polymorphism

Status: Creating the Data with the Polymorphic Calculator

Final Thoughts

The true test of good code is how easy it is to change it.

.

A common sequence is: Read the code, gain some insight, and use refactoring to money that insight from your head back into the code.

.

Code should be obvious: When someone needs to make a change, they should be able to find the code to be changed easily and to make the change quickly without introducing any errors. A healthy code base maximizes our productivity,allowing us to build more features for our users both faster and more cheaply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment