Tidy when?

An excerpt from Tidy First

Summary

Tidy never when:

  • You’re never changing this code again.
  • There’s nothing to learn by improving the design.

Tidy later when:

  • You have a big batch of tidying to do without immediate payoff.
  • Theres eventual payoff for completing the tidying.
  • You can tidy in little batches.

Tidy after when:

  • Waiting until next time to tidy first will be more expensive.
  • You won’t feel a sense of completion if you don’t tidy after.

Tidy first when:

  • It will pay off immediately, either in improved comprehension or in cheaper behavior changes.
  • You know what to tidy and how.

Explaining Variables

When?

Some expressions grow to the point where understanding them quickly at a glance becomes very hard. You may see something that looks like this:

return new Point(
	...big long expression,
  	...another big long expression
)
 

Tidy to what?

Before making behavioural changes to either of these expressions, consider tidying first to:

int x = ...big long expression...
int y = ...another big long expression...
return new Point(x, y);
 

You might even find that the expressions mean something more specific, like width and height, or top and left.

Why?

Making these kinds of changes means that the time spent deciphering meaning from those lengthy expressions is done once, instead of every time that piece of code is read.

Move Declaration and Initialisation Together

When?

If variables are declared first, and then initialised later. It might look something like this:

function myFunction() {
  var myString;
  // ...some code that doesn't use myString...
  myString = "The thing";
  // ...some code that uses myString
}
 

Tidy to what?

function myFunction() {
  // ...some code
  var myString = "The thing";
  // ...some code that uses myString
}
 

Prefer to declare variables immediately before they are used as much as possible.

Why?

It is easier to read, less bug-prone (the alternative is initialising to null/undefined or not initialising at all)

Cohesion Order

When?

Upon reading the code, you realise that making a particular behaviour change will need other changes spread widely across the codebase.

Tidy to what?

Reorder the code so the elements that need to change are adjacent. This works for routines in a file: if two routines are coupled somehow, put them next to each other. Same for files in directories: if two files are coupled, put them in the same directory.

We may wish to eliminate the coupling entirely, if we can. Cohesion order is related to Reading Order, trumping it in terms of importance. That is, we prefer higher cohesion, at the (potential) cost of compromised reading order.

Why?

High cohesion is a desirable trait in a system, and helps reduce cognitive load associated with working in that system.

Reading Order

When?

When reading code in a file and I am finding myself frustrated at how much I need to jump around to see what I want/need to see.

Tidy to what?

Reorder the code in the file in the order in which a reader (and there will be many readers) would prefer to encounter it.

Why?

Reading order makes a difference to the overall coherence of a piece of code. There is no objective correct order, however, in my experience I tend to prefer working in code that applies the “reads like a newspaper” approach. That is, headlines (public interfaces, high-level algorithms) at the top, and scrolling below reveals detail in the order it is introduced.

REMEMBER

There is an idea here to apply during code review > when receiving feedback that code you have written is unclear, or are given a suggestion for improved clarity - defer to the reviewer - between the two of you, they have a better perspective on what it’s like to read this code fresh.

New Interface, Old Implementation

When?

If I need to call a routine, and the interface makes it difficult/complicated/confusing/tedious.

Tidy to what?

Implement the interface you wish you could call, and call it. Implement the new interface by calling the old one (we can inline the implementation later, after migrating all other callers).

Why?

This would be a pass-through interface, which normally I would be against, if this was where the story ended. Instead, here we are identifying that some behaviour change would be easy, if only the design were like this instead. So we make the design like that.

See also

Coding backwards - start with the last line of a routine, as if you already had all the stuff that got you there.

  • Coding tests-first - start with the test that needs to pass.
    • Designing helpers - If only I had a routine/object/service that did X, then the rest of this would be easy.

Normalise Symmetries

When?

As code grows, we often start to see the same kinds of problems solved in different ways by different people.

Tidy to what?

Pick one way, and converge all other variants to the chosen way.

Why?

As a reader, we want to be able to see variance in code as meaningful variance - something to pay attention to. To much variance makes distinguishing the meaningful variance harder.

Dead Code

Delete it.

Guard Clauses

When?

If a method begins with something like:

if (condition) {
	...some code...
}

or even something like this:

if (condition) {
	if (not other condition) {
  		...some code...
	}
}
 

Tidy to what?

if (not condition) return;
if (other condition) return;
 

Why?

Nesting reduces visual clarity.

When not?

If the code looks more like this:

if (condition) {
  ...some code...
}
...some other code...
 

This is no longer a guard clause, but a decision point with two valid options. A different refactor would be required.