As a fan of
, known for his influential books like Test-Driven Development by Examples and Implementation Patterns, I was eager to get my hands on his latest book, Tidy First?, as soon as it was released. Surprisingly, the book is quite concise, with fewer than 100 pages, much shorter than I initially anticipated.Kent mentioned that Tidy First? is the first in a series aimed at individual developers, focusing on clean code practices, tidying techniques, and refactoring. Future volumes will delve into relationships between contributors and expand to interactions with other team roles, such as product or business partners.
Despite its brevity, I found the content very relatable and engaging. The concise material makes it easy to consume, allowing me to read a chapter in just a few minutes—perfect for those short intervals between tasks. Here are some key points I'd like to share.
Tidyings
Martin Fowler defined refactoring as a noun in his book Refactoring: Improving the Design of Existing Code.
Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.
These are all small steps you can talk about, refer to, and remember. So it’s easy to say, apply Extract Function here or Replace Conditional with Polymorphism there, and it provides a great shared language for developers. Also, many IDEs already support such named refactorings so you can do that automatically.
In my opinion, refactorings are already considered small steps. However, in Kent's book, he introduces even smaller steps known as tidyings, which can be easily applied when making changes to the code. For instance, in a React component, you could write:
export const SpotlightModal = ({isActive = true}: Props) => {
const {formatMessage} = useIntl();
return isActive &&
<SpotlightModalWrapper title={formatMessage('title')}/>
}
If the render body is too long or if there are multiple conditions in isActive
, returning early with null
could be a better option:
export const SpotlightModal = ({isActive = true}: Props) => {
const {formatMessage} = useIntl();
if (!isActive) {
return null;
}
return <SpotlightModalWrapper title={formatMessage('title')}>
{/* hundreds of other JSX elements here */}
</SpotlightModalWrapper>
};
Helper Functions
Another example I’ve recently done is a series of simple refactorings in a dropdown list component. The code was pretty old and recently migrated from a legacy codebase, I was fixing a defect there and found this:
data.operations.reduce(
(result, { name, url: givenUrl, internal_legacy_field }) => {
let url = givenUrl;
if (Object.hasOwn(URL_OVERRIDE_FIELDS, internal_legacy_field)) {
url = URL_OVERRIDE_FIELDS[internal_legacy_field];
}
//... other logic
return result;
},
[] as React.ReactNode[]
)
What I have omitted in the push
is a long JSX expression that creates a menu item for each operation. I wanted to get rid of the if-statement in the reduce, and hopefully simplify the logic. So I extracted a helper function that can preprocess the operations
.
const mapOperationToActionItem =
({ name, url: givenUrl, internal_legacy_field: field }: Operation) => {
const url = Object.hasOwn(URL_OVERRIDE_FIELDS, field)
? URL_OVERRIDE_FIELDS[field] : givenUrl;
return { name, url, field };
};
It’s definitely a simple change, and with unit tests, I can ensure it won’t break the current behavior. So I can safely make the adjustment in the original code:
data?.operations?
.map(mapOperationToActionItem)
.map(({ name, url, field }) => {
// mapping to menu items
})
A few other tidyings, such as removing dead code and deleting redundant comments, are also practical. You may already be following these practices in your personal process. However, it's beneficial to have specific names for these techniques so that we can discuss them, reference them, and implement them when necessary.
Keep reading with a 7-day free trial
Subscribe to The Pragmatic Developer to keep reading this post and get 7 days of free access to the full post archives.