Back to blog
Sep 04, 2024
8 min read

Tidying code

Key takeaways from the book Tidy First by Kent Beck

Introduction

How to tidy first before making changes to a piece of code. Think of code as human relationships when it comes to readability.

1. Tidying

Guard Clauses

Use early returns inside if statements to avoid nested if statements and apply other checks. DO NOT overdo guard clauses, more than 4 is not readable, so you can extract some of them to a helper function.

Dead Code

Delete dead and unused parts of code. Line by line, clause by clause, function by function.

Normalize Symmetries

There are different ways to write the same code, introduce certain consistency and pattern so that as a reader you can easily jump to the conclusion of what is going on. Separate the different parts from the identical parts so that the different parts are easy to spot.

New Interface Old Implementation

If there is a function that you need to call but the parameters are making it tedious or complicated or confusing. Create a new interface that you wished it looked like, and call the old function inside it, re-use the new interface everywhere you want to.

Reading Order

Arrange the code in order of how the user would prefer it to be read. Important things come first.

Cohesion Order

When we need to make a behaviour change, all dependent pieces of code should be adjacent/close by, this may mean that the functions are nearby, or the files are in the same folder, or the same repository(frontend, backend), it is even best if you have the time to reduce this coupling

Initialization and Declaration

Move them together, prevent them drifting apart. By default JS you already follow this.

Explaining Variables

When an expression grows, you need to extract it into a smaller sub-expression. Always separate the tidying commit from the behavior change commit.

Explaining Constants

Instead of hardcoding constant values, put them into variables and name them explicitly. Do not use same variable for two different purposes just because the value is the same.

Explicit Parameters

When data is not passed explicitly to a function, you could divide the function in such a way that the upper part gathers the parameters and leads them or passed them explicitly to the second part. In JS, you could say the de-structuring that belongs on the top.

Chunk Statements

Put a blank line between different parts of code that do a certain “step” of the process.

Extract Helper

When a block code has an obvious purpose and limited interaction with the rest of the code, extract it has a helper function. Name the function after the purpose. Another case of extracting a helper is expressing temporal coupling (a() needs to be called before b() so ab())

One Pile

When there are too many small pieces of a code that is making it harder to understand, put it all together as a pile and start the tidying process from there.

Explaining Comments

Write down what wasn’t obvious from the code. Sometimes writing a header comment to a file also helps.

Delete Redundant Comments

Delete comments that provide no benefit, the code should mostly be self documenting. If it is redundant, which means the variable is self-explanatory then delete the comment.

2. Managing

Separate Tidying

Tidyings go into their own PRs, with as few tidyings per PR as possible. How you lump or split your PRs is a trade-off.

Chaining

One type of tidying will usually set up for the next logical tidying. Be wary of changing too much, too fast.

Batch Sizes

This is a trade-off, known as Goldilocks Dilemma.

Collisions - Merge Conflicts Interactions - A batch accidentally changing behavior. Speculation - More prone to more tidying.

Reduce review effort.

Rhythm

Set a defined rhythm of refactorings. How many hours need to be dedicated to make the tidying a profit. Initial hours invested into tidying will later result in possibly no tidying at all when making behavioral changes.

Getting untangled

Do no have a mess of tidyings and changes all tangled together. Untanlge tidyings and changes into separate PRs.

First, After, Later, Never

Don’t do “Never” Later is a myth, it never comes around. Tidy after if it is cheaper to tidy now, going to change the same area again, soon, or the cost of tidying is roughly in proportion to the cost of behavior changes. First? If the tidying does not make it any easier, do not tidy first. If the tidying helps with comprehension, then tidy first. If the code is not gonna change for a while then do no tidy a lot. Bias away from speculation, how sure are you of your tidying? only do it then.

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. • There’s 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.

3. Theory

Understanding of theory optimizes application.

Beneficially Relating Elements

There are elements, they have relationships, and you can derive benefits from those relationships. For example, box.width() & box.height() can be turned into box.area() , so a single function call is taking advantage of the relationship that methods are called on the same method.

Structure and Behavior

Structure in the code, creates options, and options create money. The structure could make it easy to add new features, or make it hard.

Economics, Time Value and Optionality

Earn sooner spend later, options are better than things so create options in face of uncertainty. If a behavior change makes us money and now and we can tidy after, we make it the change first and tidy later.

Options

The more volatile the value of a potential behavior change, the better. The longer I could developer, the better The cheaper I could develop in future, the better. The less design work I could do to create an option, the better.

Reversible Structure Changes

Tidying first will give us plenty of chances to reverse changes. But if there are irreversible decisions, the pace should be slow and deliberate. You can make irreversible changes a bit reversible for a while using Tidying. For example feature flags. There is value in reversibility.

Coupling

When we say two changes are coupled, we need to say they are coupled with respect to which changes. If two elements are coupled with respect to a change that never happens, then they aren’t couple in a way that should concern us. For analysing coupling we need to know about the changes that have happened, and that MAY happen.

Cascading changes are the bigger issue. Use software design to reduce the probability and magnitude of cascading changes.

Constantine’s Equivalence

The sooner we get feedback from real usage, the less time/money/opportunity we spend on behavior that doesn’t matter.

The cost of software is approximately equal to the cost of changing it. Cost of total change = Cost (big changes) Cost of big changes = Cost of Coupling

Decoupling is not free and is subject to trade-offs.

Coupling vs Decoupling

You can pay the cost of coupling or pay the cost of decoupling.

Cohesion

Make no sudden moves. Do not dramatically re-arrange everything. Leave it better than you found it.

Conclusion

To answer the question of “tidy first”? Cost - Will tidying make cost smaller, later or less likely? Revenue - Will tidying make revenue larger, sooner or more likely? Coupling - Will tidying make it so I need to change fewer elements? Cohesion - Will tidying make it so the elements I need to change are in a smaller, more concentrated scope?

You cannot be your best self if you’re always rushing, if you’re always changing code that’s painful to change. Only you and your teammates are the audience for your tidying, and you’re very likely to be satisfied.