Download as pdf or txt
Download as pdf or txt
You are on page 1of 118

01

10
010
1

01
1 10 01101
0 01
10
0

01101100
a field guide to writing code that lasts

011
00
0 01

0
010
0110

01
11
01

00 1 00
1

0 01 1
0110100
1

0
0 10
0111
01
10

by Jason McCreary
001
0
BaseCode
A .eld guide to writing lasting code
by Jason McCreary
“To those about to read, we salute you…”
Table of Contents

Boo
Boots
tstr
trap
ap 7

A tale of two quotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Why these practices matter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Why a 7eld guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

How to read BaseCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Accept the challenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Forma
ormatting
tting 13

Why formatting actually matters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

What to do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

Choosing a format. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Automate the format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Forget it . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Closing example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

Dead Code 21

The issue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

Dead code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

Commented Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

Unused code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

Unreachable code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

Abandoned code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Closing example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

Nes
Nested
ted Code 30

Getting back on top. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31


Empty blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

Conditional values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

Guard clauses. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

Switching to if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

Complex loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

Closing example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

Using Objects 39

Modern Objectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

Formalize. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

Couple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Encapsulate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

Closing Reminder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

Big Block
Blockss 45

The Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

Recognize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Regroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Refactor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Closing example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

Naming 65

Making naming things easy. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

Naming Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

Avoid abbreviations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

Follow conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

Leverage context. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Naming Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

Human Readable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

Express Domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Background processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

Closing Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

Remo
emoving
ving Commen
Comments
ts 76

Closing example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

Reasonable R
Reeturns 86

Avoiding bad returns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

Empty values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Expressive representations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

Null objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

Closing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Rule o
off T
Thr
hree
ee 93

Defer Until Necessary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

Closing example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

Symme
ymmetry
try 102

Seeking Symmetry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104


Syntactic Symmetry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Semantic Symmetry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Systemic Symmetry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Closing Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Exit 117
7 Bootstrap - Bootstrap

Bootstrap

A tale of two quotes


I was traveling to the lake with the family. Although I normally use this time to unplug, I
take a book to read during the drive - which is normally related to programming. What can
I say, I love what I do. I’m also a slave to e6ciency.

At the time I was reading Implementation Patterns by Kent Beck. It was one of the shorter
books on The Reading List recommended while interviewing with companies in Silicon
Valley. I had no idea it would contain one of the universal truths about programming. One
which would forever change the way I write code.

Programs are read more often than they are written.

All of my experience supported this. Every time I reviewed an answer on StackOver8ow or


browsed the source code of a project on GitHub I was reading code code. Even when I wrote
code, I read several lines of exiting code to determine where to insert the new code.

I often paraphrase the original quote by simply stating:

We read code more than we write code.


Bootstrap - A tale of two quotes 8

Given this universal truth, how could I improve the code I write? Well, I could prioritize
readability. Instead of playing code golf with fancy one-liners I could be more expressive.
After all, another programmer is going to read that code in the future and ask, “WTF does
this do?”

Readability has become my measure of code. It in8uences all the code I write. If I alter
code in any way it is for readability
eadability. Not how many abstractions or patterns are used. But
how readable the code is. When someone asks, “which code is better”, the deciding factor
is readability.

All through college I interned as a web programmer. I did all the grunt work like markup
web forms and save their data to a database. Although most of this was simple content it
was before the days of WordPress or modern full-stack frameworks.

So I did what any young programmer would do - I wrote my own. It was not just a CMS, it
was a CMS gener
genera ator
tor. I created a YAML-esque domain language which I parsed to create
all the things. It generated the HTML and PHP for all the cruddy web forms. It generated
9 Bootstrap - A tale of two quotes

the MySQL CREATE TABLE statements. It injected the forms into templates to provide all
the admin interfaces. I made it an active generator, so when a new 7eld was added all I
had to do was update the con7g 7le and I could regenerate entire sections of the CMS.

It was awesome. I was the best programmer alive.

I believe every programmer needs to experience this at least once. It’s really the only way
to realize how insane it is. Now I look back and ask myself:

• Why did I need my own domain language?

• Why did I need an active generator?

• Why did I need to support every HTML input type?

The truth is I didn’t. You rarely need complexity. In the moment, it’s easy to believe you
didn’t
do. But in retrospect I would’ve been far more productive with a simple script to generate
a majority of the web form 7elds and coded the rest manually.

Only through re8ection on your own similar experience can you realize the next universal
truth:

Most programs are too complicated - that is, more complex than they need to be to
solve their problems e6ciently.

Rob Pike said this in one of his essays in 1989. Things have only gotten more complicated
since. When I interned, it was common for web programmers to write an entire site in
a basic text editor using only HTML. Now web programmers need to know JavaScript
and CSS (really pre-processed, supersets of JavaScript and CSS), package managers and
build tools, and multiple frameworks.

Neither approach is right or wrong. But one is obviously more complex than the other.
Complexity is an easy sell to programmers, and for whatever the reason, we buy in bulk.
We pre-architect entire systems before writing a single line of code. We reach for using
the new shiny tool. We force design patterns or data structures into our programs.

We need to remind ourselves mos


mostt code is too comple
complexx.
Bootstrap - Why these practices matter 10

Start simple and keep it simple for as long as you possibly can. It’s likely you aren’t going
to need what you thought (YAGNI). In the rare cases you do, the solution is often self-
evident, without requiring any speculation.

When I started writing BaseCode I wanted to focus solely on readability. Ironically, to keep
it simple as a single motivation. Yet, when we put readability and complexity side by side,
we notice a subtle distinction - simple code is not necessarily readable and, conversely,
complex code is not necessarily unreadable.

In the end, while readability is the primary motivation, we need to balance complexity
as well. All of the practices within BaseCode aim to improve readability and reduce
complexity.

Why these practices matter


BaseCode takes a bottom up approach. The goal is to get back to the basics. Because of
this some might dismiss these practices as too trivial. They think they don’t matter. This
is a mistake.

From the same essay on complexity, Rob Pike goes on to say, “programs are often
complicated at the microscopic level”. He’s referring to the fundamental aspects of the
code.

All code consists of the same fundamental elements: variables, control structures, etc.
Complexity at a low level bubbles up to complexity at a higher level. By adopting practices
which focus on the elements of all code (the base code), we can write any code to last.

Why a .eld guide


Reading all of the books that went into BaseCode would take years. Robert C. Martin
wrote a great book on Clean Code that alone is 412 pages. I’ve read it as well as many
other books on The Reading List.

That’s not to say you shouldn’t read these books. More that most books contain 8u5. I
mean no disrespect. I just wanted BaseCode to be very focused. I intentionally kept it
short. As such, I didn’t feel right calling it a book. BaseCode is more like the last chapters
11 Bootstrap - How to read BaseCode

of Refactoring - a catalog of speci7c code practices. I landed on 7eld guide because this
feels more akin to an army manual or survival guide.

The practices in BaseCode are the result of the books I’ve read, the programmers I’ve
paired with, and the code I’ve written over 20 years. I spent a lot of time distilling each
practice to its purest form to avoid overlap. Each one is pragmatic so you can start
applying them immediately to your code.

How to read BaseCode


You can read this cover to cover like any other book. It’s short enough you may be able to
get through it in a day. You can start with the 7rst practice, apply it, then move on to the
next practice.

However, you don’t need to read it cover to cover. You are welcome to choose your own
adventure. If you have a codebase with a lot of comments, read Removing Comments.
Need inspiration for 7nding better names, read Naming Things. Been copy and pasting a
lot of code, reread Rule of Three.

The practices don’t necessarily build upon the previous. However, they are progressive.
The 7rst set of practices are easy - ones you can apply immediately. The next set take a
little more work. The 7nal few practices may take a lifetime to master.

No matter the order, I would suggest reading one practice at a time. Once you have, apply
it. Implement it into your codebase. Have your teammates review it. Discuss it. Challenge
it. If you get stuck reread the chapter. If you get really stuck, email me or message me
on Twitter (seriously). I believe each one of these practices improves code readability and
decreases code complexity. If it doesn’t, I want to know about it.

I also encourage you to read the entire 7eld guide. All of these practices work together in
harmony. Missing any one leaves opportunity for noise within your code. Over time that
may grow louder and louder, drowning out the others. Keeping your code readable and
avoiding complexity takes a lot of discipline. You must apply these practices consistently.
Bootstrap - Accept the challenge 12

Accept the challenge


These practices are opinions which deal with subjective matters. I don’t expect you to
agree with all of them. You may even 7nd some controversial. I know we’re passionate
programmers. As such, we can take things personal and become defensive of the code
we write.

I remember a discussion regarding separation of concerns with a respected peer. As the


new guy on team, I challenged his opinion. This was 7ne. What wasn’t 7ne was making
it a zero-sum game. There had to be a right and wrong. He recognized this and ended
the discussion by saying, “I was where you were a year ago. You’ll get there.” This, of
course, pissed me o5. Now it was personal - after all he was challenging my experience. A
year later, my view aligned almost exactly with his. It wasn’t personal. He was just a little
farther down the road than me.

We’re all on a journey. My goal is to share these practices as I believe they will improve the
code you write. They will challenge the way you write code. As such, they will challenge
you. It’s not personal. They’re so that you may take your journey a little farther.
13 Formatting - Formatting

Formatting

Let’s jump right in, shall we… When it comes to formatting there are a few kinds of
programmers. There are those who don’t care. They just write code. Aside from the chaos,
this might actually be better than the other two.

The next are those who make an e5ort to format their code. However, they may not apply
it consistently across the codebase. After all, we’re human.

The 7nal kind is those who really care. I mean they really, really care. I was this latter kind.
If the code wasn’t formatted, I’d spend time formatting it before I began writing new code.
If another programmer didn’t format the code, oh man, I’d let them know.

I remember being team lead and we had adopted a modi7ed version of PSR-2 as our PHP
standard. We were in a review meeting and I was, of course, criticizing the formatting. One
of my teammates replied with “why does it matter?”.

At the time, this pissed me o5. From my perspective, we had agreed upon a standard as
a team and they still didn’t follow it.

I’m less anal about it now. Nonetheless formatting remains a controversial topic. It leads
to holy wars such as tabs versus spaces and K&R versus Allman.

You have to let go. I know that can be hard. After all, formatting is one of those personal
marks we imprint on the code. But is this really the lasting mark you want to leave on the
code?
Formatting - Why formatting actually matters 14

Why formatting actually matters


My teammates question still lingers. It deserves an answer. At the time, I was so focused
on the action I never thought about the reason behind it. The reason is the exact
motivation behind BaseCode - improving readability.

Malformed code is incredibly hard to read. So, I’d take the time to format it in an e5ort
improve its readability. But this was misguided. I’d often spend more time reformatting
the code than I would writing new code.

The trolls will argue code format is subjective. A format one programmer 7nds readable
another programmer may not. This may appear true, but only on the surface.

The underlying process of reading is no nott subjective. We all learn how to read. We train our
brains to turn letters into words, words into sentences, and so on. Over time we rely on a
certain cues, such as formatting, to make the process of reading easier. If I were to say,
start m e s s i n g with the fo rm at ,it would bemuch harder to read.

Reading code is no di5erent. In fact, Douglas Crockford states this in JavaScript: The
Good Parts:

It turns out that style matters in programming for the same reason that it matters in
writing. It makes for better reading.

As programmers, we unconsciously rely on similar cues to make the process of reading


code easier. We recognize structures like assignment statement, if statements, and
method blocks. When the code is well formatted we can recognize these structures easily.
If the code format is malformed or the format constantly changes, it makes it harder for
us to recognize these structures.

Too often programmers focus on the syntax. When talking about formatting, we need
to focus on readability, not syntax. We need a way to represent the code as we see it
unconsciously. Kevlin Henney demonstrates this through a visual representation of code.

To do so, he replaces characters with X and eliminates punctuation (like line ending
semicolons). What we are left with is a visual representation of the code. Or how our minds
15 Formatting - Why formatting actually matters

see the code.

So if we have the following simple assignment statement:

variable = value;

Its visual representation would be:

XXXXXXXX X XXXXX

Let’s look at another visual representation:

XX XXXXXXX XX XXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXX


XXXX XXXXXX XXXXXX

What is this code? What does it do? How much do we know simply from the visual
representation? Maybe you can identify something, maybe you can’t…

Now let’s add some formatting with simple whitespace adjustments:

XX XXXXXXX XX XXXXXXXXXXXXXXXXXXXXX
XXXXXX XXXXX
XXXX
XXXXXX XXXXXX

Have we learned more about the code? It’s the same code, just formatted di5erently. Even
though we don’t see the actual syntax, as a programmer we pick up on certain cues. We
see paired blocks of code. We see indentation levels. We see the top levels start with two
characters and four characters. In a fraction of a second, we unconsciously identi7ed this
structure as an if/else block.

There’s no denying the second format relays more information than the 7rst. All we did
was format the code in a standard way. Formatting the code has the largest impact on
readability. Code that is properly and consistently formatted has what Kevlin Henney calls
“visual honesty”. That is the visual representation and the actual representation align.

We now realize format is not as much about what we like, but how we see. By letting go of
our own personal format and adopting a standard format, we can make it easier for every
Formatting - What to do 16

programmer to read the code.

What to do
Code formats can vary from person to person and language to language. They can
change over time. So what do we do?

First, understand programming is a team sport, even if the only members of the team are
you and future you. You have to let go of your individual formatting for the bene7t of the
team. Being anal bene7ts no one. You’ll go mad formatting all yourself or drive your team
crazy making them adhere to your format.

Second, the tiny nuances of the formatting don’t matter. Tabs versus spaces or K&R
versus Allman, they don’t really matter. What does matter is a format is applied
consistently so we can improve the readability by increasing the “visual honesty”.

So, here’s what you do about a formatting:

1. Choose it

2. Automate it

3. Forget it

Choosing a format
I will not dictate a speci7c code format. As noted, the format naturally varies between
programmers and languages. Therefore, it’s highly unlikely you’ll 7nd one to 7t all
programmers or all languages. You must come to terms with the fact that whatever the
format you choose may not be the one you want or the one you use in other languages.
That may be di6cult at 7rst. It was for me. But sooner or later you’ll realize your time is
more valuable than to waste formatting code.

I strongly recommend adopting a standar


tandardd format. Nearly all languages have a common
(popular) code format. Remember every detail of this format may not match your own.
That’s OK. You are welcome to attempt to adjust it, but as before I think you will 7nd that
to be a waste of time.

For example I often write PHP. As such I adopted the PSR-2 code style. For JavaScript I
17 Formatting - Automate the format

use StandardJS. For Objective-C and Swift I use the style applied by my IDE (Xcode).

Adopting a common standard not only relinquishes the governance of formatting, but
also means my code aligns with other code written in that language.

Automate the format


You have better things to do with your time than format code. Let me say that again
because it took me a long time to realize this. You have better things to do with your time
than format code.

Choosing a format is the hardest step. The next step is applying and maintaining that
format. First 7nd a tool to automate this process. Your IDE is good. Being able to run a
script or service is even better. For example, when writing PHP, I run PHPCodeSni5er to
verify I adhere to the PSR-2 code style. If the format you have chosen cannot be applied
in an automated way, you should reconsider the format you chose.

Once you have found a tool that can automate your format, run it across your entire
codebase. Check in all of those changes as a single commit (yes, you’re using version
control). Some programmers will not like this. Their common reason is that it messes up
the commit history, speci7cally when running commands like git blame. While having
formatted code far outweighs placing blame on a previous code change, this can be
remedied. If you are this programmer or have this programmer on your team, you can
pass a commit reference to git blame to run blame against the history before the mass
formatting.

Forget it
Now that you have chosen a format and applied it to your codebase everything is great,
right? Well not really. Over time your code will fall back into an unformatted mess. This is
inevitable. The result of laziness and new programmers.

Violations in format should be treated as a syntax error. Programmers should not be able
to merge code unless it is formatted to the chosen standard. Again, you can automate
this with a validator. Most of these tools can also correct any violations.

However, this should not be something a programmer has to think about. Once you’ve
done the two previous steps you should be able to forget about formatting. It’s something
Formatting - Closing example 18

that just happens.

Closing example
Let’s close with some real world code I encountered in my last project. We’ll continue to
clean up more of this code through other practices. For now, we’ll focus on formatting.

function check($scp, $uid){


if (Auth::user()->hasRole('admin')){
return true;
}
else {
switch ($scp) {
case 'public':
return true;
break;
case 'private':
if (Auth::user()->id === $uid)
return true;
break;
default: return false;
}
return false;
}
}

Of course, I can muddle through this code. But it’s not easy to read. The formatting is
nonexistent, or at least inconsistent. Even though it’s a simple switch statement, it’s
“visually dishonest”. We can see this through the visual representation:
19 Formatting - Closing example

XXXXXXXX XXXXXXXXXXX XXXXX


XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXX XXXX

XXXX
XXXXXX XXXXXX
XXXX XXXXXXXXX
XXXXXX XXXX
XXXXX
XXXX XXXXXXXXXX
XX XXXXXXXXXXXXXXXXX XXX XXXXX
XXXXXX XXXX
XXXXX
XXXXXXXX XXXXXX XXXXX

XXXXXX XXXXX

By automatically applying a standard format we can immediately improve the readability


with very little e5ort. Before looking at the code, we can see

XXXXXXXX XXXXXXXXXXX XXXXX

XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXX XXXX
XXXX
XXXXXX XXXXXX
XXXX XXXXXXXXX
XXXXXX XXXX
XXXXX
XXXX XXXXXXXXXX
XX XXXXXXXXXXXXXXXXX XXX XXXXX
XXXXXX XXXX

XXXXX
XXXXXXXX
XXXXXX XXXXX

XXXXXX XXXXX
Formatting - Closing example 20

From the visual representation it’s still a bit busy. However, it is nonetheless formatted.
This reveals other opportunities to clean up the code. By not wasting our time focusing
formatting, we are free to focus on other more important things, such as improving the
code through other practices.

For now, here’s the formatted code.

function check($scp, $uid)


{
if (Auth::user()->hasRole('admin')) {
return true;
} else {
switch ($scp) {
case 'public':
return true;
break;
case 'private':
if (Auth::user()->id === $uid) {
return true;
}
break;
default:
return false;
}
return false;
}
}
21 Dead Code - Dead Code

Dead Code

I had a great college teaching assistant for my introductory Computer Science course.
Although just a few years ahead, he was never afraid to bestow his wisdom upon us. One
of his recommendations was to read The Pragmatic Programmer. I purchased a copy, but
I didn’t read it until years later. I have since read it numerous times.

Back then it was a new book. Over the years some of its topics have become dated. Yet,
one of the timeless aspects of the book are its Tips - key points throughout the book
summarized into short statements. There are 70 in total. The one I want to focus on is the
tip Don’t Live with Broken Windows, which states:

Fix bad designs, wrong decisions, and poor code when you see them.

This tip is rooted in an academic theory which uses broken windows as a metaphor
for disorder within a community. Following the metaphor once a vacant house su5ers
a broken window it declines at a more rapid pace. The physical disorder leads to social
disorder as the community perceives decay.

Broken windows can exist in code as well. As noted in the tip, bad design and poor code
are a few examples. Yet the truest form of broken windows in code is dead code. Dead
code is the most visible form of decay. Left un7xed it will rot a codebase over time.
Dead Code - The issue 22

The issue
Dead code doesn’t happen intentionally. It’s the result of the way in which we program.
It’s rare, if not impossible, to write an entire program in one pass. Instead we divide and
conquer sections of the program at a time. While this is an excellent practice, its side
e5ect is disjointed code. Entire blocks of code may stay in this state for a long periods
time.

This divide and conquer practice is repeated as we continue to work on the codebase. We
add some code here and remove some code over there. Whether due to time constraints
or neglect, we rarely return to “clean up” the code.

These start as simple things: unused parameters, commented code, unreachable code.
All of which are forms of dead code. This code unnecessarily increases complexity and
decreases readability. From a complexity perspective it begs the question of how was this
code used. From a readability perspective it is extra reading without bene7t.

In the end, dead code makes the code appear clumsy or broken. Inline with the theory,
the e5ect is ultimately to not care about the code. This in turn results in more “bad code”
and the downward spiral begins, eventually sending the codebase into an unmanageable
state.

The issue with dead code can be summarized in three words:

• Apa
Apath
thy
y. Dead code makes it appear as though no one cares about the code. So
why should you…

• Noise
Noise. Not only is dead code unnecessary to read, it also yields false positives in
code searches, clutters version control, and generally weakens the signal.

• Obscurity
Obscurity. When dead code is spotted it begs origin questions. What was this
code used for before? The quest to 7nd such answers is often fruitless.

As programmers we must be vigilant and ruthless about removing dead code. I do this
as part of the process of Boy Scouting, which is to leave the code better than I found it.
Anytime I encountered dead code, I’ll make sure to clean it up when making my changes.
23 Dead Code - Dead code

Dead code
There are many di5erent forms of dead code. Some are easy to spot. Others are almost
impossible. The fastest way to spot dead code is to perform an automated analysis of
your codebase. Your IDE may o5er this or you can use a static analysis tool.

Regardless of which tool you use, it will group the di5erent forms of dead code into
categories. Let’s cover a few of the common forms of dead code with samples so you can
spot them more easily in your own codebase.

Commented Code
Commented code is the most common form of dead code. Far too often programmers
comment out large blocks of code.

function user_data() {
$data['name'] = 'JMac';
// $data['address'] = '123 Something Way';
// $data['city'] = 'Louisville';
// $data['state'] = 'KY';
$data['zip'] = '12345';

// return(json_encode($data, JSON_UNESCAPED_UNICODE));
return(json_encode($data));
}

As programmers it’s in our nature to be critical thinkers. When we come across large
blocks of code we naturally ask questions: What does this code do? Why was it
commented out? Do we still need it?

Without these answers, the code is often left alone to exhaust future readers and invite
others to practice commenting out code. This is lazy and irresponsible. When you
encounter commented out code, remo
emov ve it
it.

Some comment out code so it’s easier to uncomment when needed again. Nowadays
with projects are under version control, this isn’t as e6cient as it seems. It is very easy to
remove the commented code in an atomic commit and revert the commit at a later time.
This approach not only keeps the code clean, but scales better.
Dead Code - Unused code 24

For frequently toggled code, consider documenting a patch process or utilizing


feature 8ags.

Unused code
The next most common form of dead code is unused code. Often when we initially
write code we speculate on future needs. This results in extra parameters, methods, or
conditions which are never used.

While such code sounds easy enough to spot, even something as simple as unused
parameters can be harder than you think. A prime example is an options array parameter.
We’ll explore alternatives to such parameters in Using Objects. For now, the following code
demonstrates the dilemma of assessing if this in7nitely dynamic parameter is used.

public function auth(array $options = [])


{
// ...

if ($options['verify'] ?? false) {
$this->verification();
}

// ...
}

Obviously the verify option is being used. But what other options are being used? What
happens if we remove this condition? Even when the auth method doesn’t use options,
is the rest of the code passing in options? How do we go about auditing the codebase?

We start to see how di6cult it is to remove this type of dead code after it is originally
authored. Future readers may not have enough understanding to be con7dent in
removing the code themselves. So again, it is left for the next reader.

As the original author you must be thorough in reviewing your code to ensure it does
not contain unused code. This provides the best chance at removing the code before it
begins to decay.
25 Dead Code - Unreachable code

If you are new to the codebase you can use a static analysis tool to search for unused
code. Although you may be skeptical about removing dead code, I recommend doing so.
If necessary, remove unused code in small chunks and test them incrementally to ensure
the system does not break. But nevertheless work to remove unused code.

Unreachable code
Unreachable code is another form of dead code. In its simplest form you see these as
return statements preventing additional code from being executed.

A prevalent example of unreachable code are the break statements within a switch
block following return statements. We saw this in the code from the closing example in
Formatting:

switch ($scp) {
case 'public':
return true;
break;

// ...

default:
return false;
break;
}

When using return within switch blocks the break statement becomes unreachable.
Instead, we can simply write:

switch ($scp) {
case 'public':
return true;

// ...

default:
return false;
}
Dead Code - Abandoned code 26

This is a super simple cleanup, but can reduce the number of lines for the inherently
verbose switch statement and therefore reduce the amount of reading. In fact, the
break statement within the default case is always unnecessary.

Other forms of unreachable code include conditions which never evaluate to true, loops
which never run, or code below a top level return.

if (false) {
// never executed...
}

while (false) {
// never executed...
}

return;

// never executed...

While these seem like obvious examples, these become harder to spot when conditions
are dynamic, loops based on expressions, or longer code blocks which return from
multiple paths.

Again, the best thing to do is to remove this code. Do so in a separate commit so you can
easily revive the code if you determine it was indeed reached.

For high-demand systems, simply removing code may feel too extreme. Initially you
can add some tracer bullets to see if the target code is hit. These can be messages
output to a log which you audit after a few hours or days to gain more con7dence
before removing the code.

Abandoned code
One of the hardest forms of dead code to spot is abandoned code. Even with static
analysis tools it’s still di6cult to 7nd such code. This is similar to unreachable code and
often conditional code which has dynamic or temporal invocations.
27 Dead Code - Closing example

Consider the following code:

function send_notification($user)
{
// ...

if (config('sms.enabled') && $user->preferences()->has('notifications.sms')) {


send_sms();
}

// ...
}

What happens if we never enable SMS for any environment? What happens when we
decide not to send SMS noti7cations or the preference is never available to users?

In either scenario, this becomes dead code. Possibly all the code related to send_sms
dies as well. But with the compound condition based on dynamic values it’s unlikely to be
spotted and removed.

Not every codebase uses environmental con7guration or user preferences. But there are
other features which may lead to abandoned code. E-commerce systems have coupons
and promotions. Content providers have pay walls or time-sensitive publications. APIs
have rate-limiting and data thresholds. All of these features create conditional code paths
which may become abandoned.

Until code can self-destruct, we don’t really have an easy way to prevent abandoned
code. It is uncommon for us to go back after the fact and remove such code. In the end,
only good project management where follow-up stories are created can limit abandoned
code. Otherwise, do your best to remove abandoned code when you are Boy Scouting.

Closing example
The following code is a piece of middleware which redirects a web request to the proper
subdomain.
Dead Code - Closing example 28

public function handle($request, Closure $next)


{
$pieces = explode(".", $request->getHost());
$subdomain = $pieces[0];
$mainDomain = $pieces[1];
$intention = $request->path();

// If the subdomain is app, then force non SSL


// If the
if ($subdomain == 'app' || $subdomain == 'application') {
// die("need to force scheme to https");
URL::forceScheme('http');
} else {
// die("need to force scheme to https");
if ($request->server->get("HTTP_X_FORWARDED_PROTO") == 'http') {
//die($request, $intention, $request->headers->get('referer'));
return redirect('https://' . $subdomain . '.example.com/' . $intention);
}

// URL::forceScheme('https');
}

if (config('force_app_subdomain')) {
return redirect('https://app.example.com/' . $intention);
}

return $next($request);

if ($mainDomain != 'example') {
// return redirect('https://www.example.com');
$intention = $request->path();
redirect('https://' . $subdomain . '.example.com/' . $intention);
}

// if($subdomain == 'app' || $subdomain == 'application'){


// return $next($request);
// }

// if( $subdomain=='www' || $subdomain=='web'){


// return $next($request);
// }
29 Dead Code - Closing example

// abort(404);
}

It is rot with dead code containing commented code, unreachable code, and potentially
abandoned code. As readers of this code, it appears overly complex and noisy. It also feels
clumsy, leaving us with a lack of respect for the code. While there are other BaseCode
practices we can apply, by simply removing all the dead code we can boost the signal
making the code clearer to follow.

public function handle($request, Closure $next)


{
$pieces = explode(".", $request->getHost());
$subdomain = $pieces[0];
$intention = $request->path();

// If the subdomain is app, then force non SSL

if ($subdomain == 'app' || $subdomain == 'application') {


URL::forceScheme('http');
} else {
if ($request->server->get("HTTP_X_FORWARDED_PROTO") == 'http') {
return redirect('https://' . $subdomain . '.example.com/' . $intention);
}
}

return $next($request);
}
Nested Code - Nested Code 30

Nested Code

I don’t believe in hard rules for writing code. You hear them often. Things like methods
shouldn’t be more than 15 lines lines. There should only have one return statement.
Indentation must be 4 spaces
spaces. Such rules are too rigid.

In the real world code is much more 8uid. Adhering to these hard rules distracts us from
what really matters - readability. If my focus is strictly on the number of lines or return
statements, I prevent myself from writing more readable code simply because it is a few
lines “too long” or has more than one return statement.

Many of these hard rules attempt to address nested code. Even when properly formatted,
nested code is hard to follow. Physically there’s more visual scanning with the eyes.
Mentally, each level of nesting requires more overhead to track functionality. All of these
exhaust a reader.

Nested code is mostly the result of conditionals. Since conditionals are the basis for all
programming logic, we can’t very well remove them. We must recognize their e5ect on
readers and take steps to minimize this impact.

A few years ago, Rafael Dohms shared the “code katas” practiced on his team. One of
them was “avoid using else”. This was pretty interesting. I don’t believe this alone results
in more readable code. But the goal of avoiding else is a stepping stone to cleaning up
nested code.
31 Nested Code - Getting back on top

Getting back on top


To improve readability, we want to bring the code back to the top level. By nature, loops
and conditionals have a nested structure. There’s no way to avoid this. However, we can
avoid deeper nesting within these structures.

Let’s take a look at a few examples of nested code and practices for improving their
readability.

Empty blocks
You may not believe me, but I’ve seen the following code more than once:

public function handle($request, Closure $next)


{
if (env('APP_ENV') == 'development') {
// do nothing...
} else {
if ($request->getScheme() != 'https') {
URL::forceScheme('https');
return redirect('https://www.example.com/' . $request->getPath());
}
}

return $next($request);
}

That’s right, an empty if block. I’ve also seen the opposite - an empty else block. There
is no rule that an if must be paired with an else - at least not in any of the programming
languages I’ve used in the past 20 years. Empty blocks are dead code, remove them.

Conditional values
Nested code blocks often return a value. When these are boolean values, there’s an
opportunity to condense the code block and return the condition directly.

Consider the nested code within the isEmpty method of a Set class:
Nested Code - Guard clauses 32

public function isEmpty() {


if ($this->size === 0) {
return true;
} else {
return false;
}
}

While this method block is only 4 lines of code, it contains multiple sub-blocks. Even for
such a small number of lines this is hard to read, making the code appear more complex
than it really is.

By identifying the return of raw boolean values we have the rare opportunity to
completely remove the nested code by directly returning the condition.

public function isEmpty() {


return $this->size === 0;
}

Given the context of this aptly named method combined with a now one line block,
we decreased its perceived complexity. Although this line may appear dense, it is
nonetheless more readable than the original.

Condensing conditionals can work for more data types than raw booleans. For
example, you can return the condition as an integer with a simple type cast. However
this rapidly increases the complexity. Many programmers try to combat this by using
a ternary. But a ternary condenses the code without decreasing the complexity,
making the code less readable. In these cases, a guard clause is a better alternative.

Guard clauses
Nested code is often the result of logical progression. As programmers we write out each
condition until we reach a level where it’s safe to perform the action.

While this 8ow may be ideal for execution, it’s not ideal for reading. For each nested level
the reader has to maintain a growing mental model.
33 Nested Code - Guard clauses

Consider the following implementation of the add method of a Set class:

public function add($item) {


if ($item !== null) {
if (!$this->contains($item)) {
$this->items[] = $item;
}
}
}

Logically the progression: if the item is not null and if the Set does not contain the item,
then add it.

The problem is not only the perceived complexity of such a simple action, but that the
primary action of this code is buried at the deepest level.

Ideally the primary action of a block of code is at the top level. We can refactor the
conditional to a guard clause to unravel the nested code and expose the primary action.

A guard clause simply protects our method from exceptional paths. Although they
commonly appear at the top of a code block, they can appear anywhere. We can convert
any nested conditional into a guard clause by applying De Morgan’s Laws and
relinquishing control. In code, this means we negate the conditional and introduce a
return statement.

By applying this to the add method our implementation becomes:

public function add($item) {


if ($item === null || $this->contains($item)) {
return;
}

$this->items[] = $item;
}

In doing so we have not only drawn out the primary action, but emphasized the
exceptional paths for our method. It is now less complex for future readers to follow. It’s
also easier to test as the exceptional paths are clearly drawn out.
Nested Code - Switching to if 34

Switching to if
A switch statement is a very verbose code structure. switch inherently has 4 keywords
and 3 levels. It’s a lot to read even if the contained blocks of code are only a few lines.
While this is acceptable in certain cases, it’s not in others.

There are a few cases where using if statements instead of a switch statement may
produce more readable code.

• When there are just a few case blocks the inherent structure of the switch
statement produces more lines than the equivalent if statements.

• When case blocks contain nested code the complexity increases and readability
decreases to a critical level. Using guard clauses or adopting the practices within
Big Blocks can improve the code.
• When type casts or calculations are needed to coax the value into the constraints
of a switch. This doesn’t apply to languages (Swift, Go, etc) that support more
complex case comparisons.

switch statements are best when a 1:1 ratio exists between case statements and
the lines of code within their blocks. Whether these lines are assignments, return
statements, or method invocations the readability remains nearly the same provided the
ratio is roughly 1:1.

switch ($command) {
case 'action':
startRecording();
break;
case 'cut':
stopRecording();
break;
case 'lights':
adjustLighting();
break;
}

In such cases where switch statements are streamlined, many programmers use
35 Nested Code - Complex loops

a map, database table, or polymorphism instead. All of which are indeed additional
alternatives. Just remember every solution has trade-o5s (complexity). A switch
statement is often “good enough” for most code.

Complex loops
Another common form of nested code are loops. Loops by nature are complex. As
programmers, we’re cursed to be o5 by one and miss incremental logic. Again, we’re
humans not computers. So we’re unlikely to win the battle against loops. They will always
challenge us. The only way to combat this complexity is readability.

I won’t get into which data structures and algorithms may help improve your code.
That is much too specialized. In general though, most loops deal with accumulation or
invocation. If you 7nd your codebase contains lots of loops, see if higher order functions
like filter/map/reduce can be used. While this may not improve readability for all
readers, it will improve your individual skillset.

Closing example
Let’s revisit the code from the closing example in Formatting and apply these practices
to remove the nested code.
Nested Code - Closing example 36

function check($scp, $uid)


{
if (Auth::user()->hasRole('admin')) {
return true;
} else {
switch ($scp) {
case 'public':
return true;
break;
case 'private':
if (Auth::user()->id === $uid) {
return true;
}
break;
default:
return false;
}
return false;
}
}

First, we’ll remove the else as the initial conditional is already acting as a guard clause
with the return true.
37 Nested Code - Closing example

function check($scp, $uid)


{
if (Auth::user()->hasRole('admin')) {
return true;
}

switch ($scp) {
case 'public':
return true;
break;
case 'private':
if (Auth::user()->id === $uid) {
return true;
}
break;
default:
return false;
}
}

This decreases the nested code slightly and brings the switch statement back to the top
level. Now that it’s in focus, we see it’s perceived complexity. It’s verbose with just two
cases. One case contains nested code. The default case hides the primary action. In
addition, this block of code now mixes if and switch statements which lacks Symmetry.

Any one of these reasons might be enough to convert the switch to if statements. All
of them combined makes the decision easy.
Nested Code - Closing example 38

function check($scp, $uid)


{
if (Auth::user()->hasRole('admin')) {
return true;
}

if ($scp === 'public') {


return true;
}

if ($scp === 'private' && Auth::user()->id === $uid) {


return true;
}

return false;
}

In the end, we are left with a series of guard clauses. All are at the top level making the
conditions easy to read through. If none are satis7ed, we reach the primary action of
returning false.

Many will notice the raw boolean returns and try to condense all these conditionals
into one large return statement. Much like using a ternary, this can quickly increase
the perceived complexity and decrease readability. Again, there are no hard rules.
Sometimes 2 conditions are “too many”. Sometimes 4. Remember, the goal is to
improve readability, not decrease lines of code.
39 Using Objects - Using Objects

Using Objects

There’s nothing like writing a script to automate tedious tasks. I’ve written many over the
years to extract data from text, perform complex search and replace, or import data into
a system. These scripts are short by nature. As such writing procedural code using basic
data types never shows its downsides.

These downsides become more evident when the codebase grows. Over time we start
to see duplicate code. This appears through code with similar behavior or informal data
structures. Despite e5orts to adjust the code, it remains procedural.

The reason for this is primitive obsession. Primitive obsession is actually one of the
original code smells from Refactoring. Essentially we only use basic data types
(primitives) such as integers, strings, or arrays for everything. This limits the way in which
we structure our code and ultimately leads to unnecessary complexity.

We use primitives because they are easy. This is alright. In fact, stick with using simple
data types as long as you can. But when these primitives begin to make the code more
complex or harder to read, we may be missing an opportunity to use objects.

Modern Objectivity
Breaking primitive obsession can be hard. Especially for new programmers. We need to
remember that objects can be used not only to group data, but to house shared code. As
such, they are perfect for combatting duplication or formalizing a common data structure.
Using Objects - Formalize 40

Since the data varies from codebase to codebase, we’ll review a few code snippets to
demonstrate the respective bene7ts of using objects.

Formalize
At some point we start to push the limits of primitives. It’s the square peg in a round hole
debacle. While we often just make it work, it’s an awkward solution.

A good example of this is the con7guration array from the snippet we reviewed in Dead
Code.

public function auth(array $options = [])


{
// ...

if ($options['verify'] ?? false) {
$this->verification();
}

// ...
}

As discussed, this options array has an in7nite number of values. At any time, another
programmer can add any value to expand the behavior of the code. While some may view
this as a strength, it becomes impossible to maintain. Like the square peg, it just gets
stu5ed through so it works.

We can formalize the structure of options by using an object.

class AuthOptions
{
protected $verify;
protected $captcha;
// ...
}

Using an object instead of an array gives us more structure. In doing so, we make it easier
to manage by communicating all the available options and centralizing the control. The
41 Using Objects - Couple

object also allows for more growth than the primitive. We can continue to formalize the
options by specifying their type, set default values, and add helper methods.

Formalizing an associative array (or dictionary) into an object is not much of a leap. What
may be more of a leap is formalizing integers or strings into an object. In these cases,
using objects may help formalize magic values or encapsulate otherwise complex code.
We’ll demonstrate the latter in a future code snippet as well as other BaseCode practices.

Couple
Another way to 7nd opportunities to use objects is by spotting “data clumps”. This is a
group of primitives passed together or used at the same time.

Let’s review a code snippet from some banking software.

function transfer(Account $from, Account $to, $amount, $currency)


{
$from->debit($amount, $currency);
$to->credit($amount, $currency);
}

Notice the data clump of amount and currency. They are always passed around
together. If we think about how these values are used, we realize they are coupled. We
don’t use or change one without the other. For example, changing currency would
change amount.

We can better communicate this coupling by using an object to represent these values as
Money.
Using Objects - Couple 42

class Money
{
private $amount;
private $currency;

public function __construct($amount, $currency)


{
$this->amount = $amount;
$this->currency = $currency;
}

public function getAmount()


{
return $this->amount;
}

public function getCurrency()


{
return $this->currency;
}
}

Here we use the Money object to not only formalizes a set of data, but also to couple
the data as a single value. This is also known as a value object. Even though it holds
multiple values, once the object is created, the values are set and can not be changed.
Said another way, it’s immutable. Immutability is another good programming paradigm as
it helps eliminate state, thereby making code less complex.

If we update the original transfer method with our new Money object, we 7nd it easier
to read for a few reasons. Mainly because it’s a more natural representation of how we
think about the domain of banking. But also because it is more symmetrical. Which is
something we’ll talk about in Symmetry.

function transfer(Account $from, Account $to, Money $money) {


$from->debit($money);
$to->credit($money);
}
43 Using Objects - Encapsulate

Encapsulate
So far we’ve reviewed examples which demonstrate using objects to formalize and group
data. Now let’s demonstrate one of the primary bene7ts of object oriented programing -
encapsulation.

Let’s review a code snippet which 7lters some numbers between a minimum and
maximum value:

function limit($numbers, $min, $max)


{
$accumulator = [];

foreach ($numbers as $number) {


if ($number >= $min && $number <= $max) {
$accumulator[] = $number;
}
}

return $accumulator;
}

We have coupling between min and max. In this case, they are logically bound together.
We use them both to perform the limit behavior. As such, we can introduce a similar value
object (speci7cally a Range) to represent this coupling.

The resulting code becomes:

function limit($numbers, NumberRange $range)


{
$accumulator = [];

foreach ($numbers as $number) {


if ($number >= $range->getMin() && $number <= $range->getMax()) {
$accumulator[] = $number;
}
}

return $accumulator;
}
Using Objects - Closing Reminder 44

As before, using an object helps formalize the relationship between min and max.
However, we can take this further. Objects allow us to not only house data, but also
related logic. When using primitives, this logic is often duplicated throughout the
codebase. With an object, we can now put all that logic in one place.

In the case of our NumberRange, we still leak the logic for how we compare a number
using min and max. This comparison likely exists elsewhere in the codebase. Instead, we
can hide the logic relating to min and max within NumberRange.

After this next iteration, the NumberRange object houses both the data and logic relating
to min and max which yields more readable code:

function limit($numbers, NumberRange $range)


{
$accumulator = [];

foreach ($numbers as $number) {


if ($range->includes($number)) {
$accumulator[] = $number;
}
}

return $accumulator;
}

From here, we might apply some of the practices we learned in Nested Code to continue
to improve the readability of limit.

Closing Reminder
It’s important to remember our primary goal is to improve readability. Our goal is not
simply to use objects. If the code does not have informal data structures or replicated
data clumps and logic then using objects could actually decrease readability. Again, there
is nothing wrong with using primitives initially. It’s always better to keep the code as is,
until you identify a clear bene7t to change.
45 Big Blocks - Big Blocks

Big Blocks

Many programmers equate lines of code (length) with readability. In isolation, length
may seem like a good measure. But there are ready examples where short length is not
readable. On one end of the spectrum, consider a dense one-liner. Maybe the original
author understands, but the rest of us just scratch our heads. On the other end of the
spectrum, there are codebases where every method is just one line. This seems tidy on
the surface, but becomes hard to digest as you bounce around the codebase combining
the pieces back together.

While length does factor into readability it should not be our primary focus. With that said,
reducing lines seems to motivate us to refactor code. We can use this motivation as a
base and, over time, change it from how to make the code shorter to how to make the
code more readable.

The Process
When faced with a big block of code, how do we start reducing the length?

We do so with a simple, three step process:

1. Recognize the level

2. Regroup into sub-blocks

3. Refactor each sub-block


Big Blocks - Recognize 46

Most programmers jump straight to the refactor step. This step is inherently challenging.
But it can be easier if we take the time to perform the recognize and regroup steps.

Let’s work through this process by applying each step iteratively to reduce the following
(relatively long) code snippet for when a user successfully signs into an application:

protected function authenticated(Request $request, $user)


{
$ajax = $request->headers->get('X-Requested-With') == 'XMLHttpRequest';

if ((int) $user->role_id === 1) {


if ($ajax) {
return response()->json(['success' => true, 'url' => '/admin/dashboard']);
}
return redirect('/admin/dashboard');
}
if ((int) $user->role_id === 2) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/teacher/dashboard']);
}
return redirect('/teacher/dashboard');
}
if ((int) $user->role_id === 3) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/parent/dashboard']);
}
return redirect('/parent/dashboard');
}
if ((int) $user->role_id === 4) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/student/dashboard']);
}
return redirect('/student/dashboard');
}
}

Recognize
The 7rst thing we want to do is recognize the level. The level is determined by the context
of the code. Once we know this, we can make decisions on how to refactor the code for
47 Big Blocks - Regroup

the reader.

The context is set by the location and role of the code within the codebase. Is the
code within a function or class? Is it within of a controller or model? Is the code buried
deep within the codebase or at the surface? All of these form a context which helps us
recognize the level of the code.

We can combine the level with our reader to create a “reading level”. This is much like an
academic reading level. Given a student is in the 4th grade, we would expect them to read
at the 4th grade level. We don’t want a 4th grade student to read at the 2nd grade level.
Conversely, we don’t expect them to read at the 8th grade level.

This analogy also applies to code, we don’t want to read code pertaining to a higher or
lower level. Ideally, we are reading code pertaining to the current level. Anything else is
too much to read and likely resulting in extra lines of code contributing to our big block.

In the case of our code snippet, this code is located within an event callback. As such, its
level may seem arbitrary. In these scenarios, we can consider the level of the parent (what
calls the callback or 7res the event). For us, this is the controller. Within a Model, View,
Controller (MVC) application, the role of the controller is to handle requests and perform
interactions between our models and views. This all seems pretty high level.

As a reader, we don’t expect the controller to contain code dealing with data persistence
or output. Such code belongs in the model and view, respectively. In addition, since the
controller is high level, it serves as an entry point into our application. As such, we don’t
expect all our code to be at this level. Instead the controller may delegate to code at other
levels.

To summarize, we recognized the level through the speci7c context of the code
(controller). We can now use this to determine the expectations of the reader (entry point
for business logic) which will help us during the refactor step.

Regroup
The next step in evaluating a big block of code is to split the code into sub-blocks. The
goal is to group similar code together using line breaks, then label the action of each sub-
block with a temporary comment. This serves as outline of what the code does - making
it easier to understand, as well as spot duplication.
Big Blocks - Regroup 48

This step also gives us an opportunity to revisit the original intent of the code. Often
the existing code can act as a box - restricting us to the current implementation. By
regrouping the code into sub-blocks and adding comments we re-abstract the code
giving ourselves a better chance to think outside the box.

Focusing on what the code does parallels the Single Responsibility Principle (SRP). Some
programmers don’t 7nd SRP helpful in refactoring code. As programmers we are the
ultimate architects of the system. As such we control where code goes. One programmer
might put this code here while another might put it elsewhere. This gets subjective.

Focusing on the action of the code is less subjective. As programmers, we can read the
code and determine its action. Rarely do two programmers disagree about what the same
block of code does. So action gives us a more concrete way to 7gure out where the code
belongs by comparing it to the current level.

Let’s review our code snippet and regroup the code into sub-blocks. By simply adding
some line breaks and comments we outline what the code does.
49 Big Blocks - Regroup

protected function authenticated(Request $request, $user)


{
// determine if the request is ajax
$ajax = 'XMLHttpRequest' == $request->headers->get('X-Requested-With');

// redirect to admin dashboard


if ((int) $user->role_id === 1) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/admin/dashboard']);
}
return redirect('/admin/dashboard');
}

// redirect to teacher dashboard


if ((int) $user->role_id === 2) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/teacher/dashboard']);
}
return redirect('/teacher/dashboard');
}

// redirect to parent dashboard


if ((int) $user->role_id === 3) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/parent/dashboard']);
}
return redirect('/parent/dashboard');
}

// redirect to student dashboard


if ((int) $user->role_id === 4) {
if ($ajax) {
return response()->json(['success' => true, 'url' => '/student/dashboard']);
}
return redirect('/student/dashboard');
}
}

Regrouping the code not only draws out the action of each sub-block, but the primary
action of the code overall. We see the primary action of this code is redirecting a user to
their dashboard. We also see a lot of duplicate actions. We can focus on both when we
Big Blocks - Refactor 50

refactor the code.

Refactor
The 7nal step is to collapse the code. On its own, this may seem daunting. But armed with
the knowledge of the level of the code and the action of each sub-block, this refactor
becomes much easier.

We can now simply work through each sub-block and ask two questions:

1. Is there a more native way to handle the action?

2. Does this action 7t the current “level”?

We can think of this as a simple decision tree:

To answer the 7rst question, you can research if the codebase, framework, or language
already has code to perform the action. Using the labels from the temporary comments
as keywords, you can search the codebase or combine this with your language or
framework to perform a Google search.
51 Big Blocks - Refactor

In the case of our code snippet, a search within the codebase revealed ajax is actually
a helper method on the request object. If we use it directly, we can remove our initial
sub-block and reduce the code by a few lines. We also improve the readability since we
no longer have to track the temporary ajax variable.

protected function authenticated(Request $request, $user)


{
// redirect to admin dashboard
if ((int) $user->role_id === 1) {
if ($request->ajax()) {
return response()->json(['success' => true, 'url' => '/admin/dashboard']);
}
return redirect('/admin/dashboard');
}

// redirect to teacher dashboard


if ((int) $user->role_id === 2) {
if ($request->ajax()) {
return response()->json(['success' => true, 'url' => '/teacher/dashboard']);
}
return redirect('/teacher/dashboard');
}

// redirect to parent dashboard


if ((int) $user->role_id === 3) {
if ($request->ajax()) {
return response()->json(['success' => true, 'url' => '/parent/dashboard']);
}
return redirect('/parent/dashboard');
}

// redirect to student dashboard


if ((int) $user->role_id === 4) {
if ($request->ajax()) {
return response()->json(['success' => true, 'url' => '/student/dashboard']);
}
return redirect('/student/dashboard');
}
}
Big Blocks - Refactor 52

If we don’t 7nd a native way to handle the action, we should next ask ourselves if the code
7ts the current level. If so, we can leave it or collapse the code into a private method. If
not, we can move the code to another level.

The remaining code performs the primary action of redirecting the user. We can break the
code down further into four sub-blocks, each with two actions:

1. Determine the dashboard

2. Redirect the user

Let’s review the actions code for redirecting the user 7rst. As readers, we expect to see
code for managing the request and response within a web controller. So this action 7ts
the current level. As such, there’s no need to move it elsewhere. However, given the
duplication of this code, we can collapse this snippet in an e5ort to make our big block
more readable.

We’ll review the practice of removing duplication within Rule of Three. For now, we’ll
extract this code into a private helper:
53 Big Blocks - Refactor

protected function authenticated(Request $request, $user)


{
// redirect to admin dashboard
if ((int) $user->role_id === 1) {
return redirectToDashboard($request->ajax(), '/admin/dashboard');
}

// redirect to teacher dashboard


if ((int) $user->role_id === 2) {
return redirectToDashboard($request->ajax(), '/teacher/dashboard');
}

// redirect to parent dashboard


if ((int) $user->role_id === 3) {
return redirectToDashboard($request->ajax(), '/parent/dashboard');
}

// redirect to student dashboard


if ((int) $user->role_id === 4) {
return redirectToDashboard($request->ajax(), '/student/dashboard');
}
}

private function redirectToDashboard($isAjax, $path)


{
if ($isAjax) {
return response()->json(['success' => true, 'url' => $path));
}

return redirect($path);
}

By extracting this sub-block of code into a private method we dramatically reduced the
length of the authenticated method. Although a method in the same class, by splitting
out this action we prevent readers from repeatedly reading the same code. Instead, they
receive a summary through good naming and can review the method for more detail if
necessary. Even though duplication still exists, we have exposed it and can now focus on
collapsing it.

The other action of determining the dashboard, however, doesn’t 7t the current level. Do
Big Blocks - Refactor 54

we need to understand the various di5erent user roles within the controller? Likely not.
The low-level details exposed such as the user role ids and their mapping to dashboards
creates inappropriate intimacy between the controller and the user model. This code
belongs somewhere else. Somewhere such details are more natural to read and don’t feel
so low-level.

In this case, the user model seems the more appropriate level of the two. Since we are
dealing with the user object already, there’s no reason to introduce a new object. Let’s
move these duplicate sub-blocks dealing with determining the user dashboard to the
user class. We reviewed practices for dealing with such code in Nested Code. So, we’ll
collapse the action of determining the dashboard into a dashboard method on the user.

protected function authenticated(Request $request, $user)


{
return redirectToDashboard($request->ajax(), $user->dashboard());
}

private function redirectToDashboard($isAjax, $path)


{
if ($isAjax) {
return response()->json(['success' => true, 'url' => $path));
}

return redirect($path);
}

With that, we have reduced our big block to a single line. Things won’t always become
one line. Nor should they. We were able to do so mainly because of the duplicate code.
Nonetheless, the code is undeniably more readable and its complexity matches what
might expect as we read through each level of the code.

Unless we 7nd a native alternative, we never truly remove code. Code is a


mathematical equation. Any operations performed on one side have an equal
operation on the other side. If we remove 5 lines of code from one method, we need
to add them to another method. Keeping this in mind will lead to a higher success
rate when refactoring big blocks of code, and less anxiety when doing so.
55 Big Blocks - Closing example

Closing example
Let’s close with one more example to build some muscle memory. We’ll apply the three
steps to improve the readability by focusing on reducing the number of lines.

class EnrollmentController extends Controller


{
public function store(Request $request, $id)
{
Validator::make($request->all(), [
'first_name' => 'required|string',
'last_name' => 'required|string',
'email' => 'required|email',
'password' => 'required|confirmed|min:8',
])->validate();

$course = Course::findOrFail($id);

if (Auth::check()) {
$course->enrollUser($request->user());

return redirect($course->path());
}

$user = User::create([
'first_name' => request('first_name'),
'last_name' => request('last_name'),
'email' => request('email'),
'password' => bcrypt(request('password')),
]);

$course->organization->addUser($user);
$course->enrollUser($user);

Auth::login($user);

return redirect($course->path());
}
}
Big Blocks - Closing example 56

The 7rst step is to recognize the level. We 7nd ourselves within a method of a web
controller which contains the business logic for handling course enrollment. As such, we
expect the code to read at a pretty high-level.

Next, we’ll regroup into sub-blocks. The code is already well formatted with line-breaks
separating each sub-block. So we’ll just add temporary comments to draw out each
action.
57 Big Blocks - Closing example

class EnrollmentController extends Controller


{
public function store(Request $request, $id)
{
// ensure the request is valid
Validator::make($request->all(), [
'first_name' => 'required|string',
'last_name' => 'required|string',
'email' => 'required|email',
'password' => 'required|confirmed|min:8',
])->validate();

// get the course by id


$course = Course::findOrFail($id);

// determine if user is authenticated


if (Auth::check()) {
// enroll authenticated user
$course->enrollUser($request->user());

// redirect to course page


return redirect($course->path());
}

// create new user from request data


$user = User::create([
'first_name' => request('first_name'),
'last_name' => request('last_name'),
'email' => request('email'),
'password' => bcrypt(request('password')),
]);

// enroll new user


$course->organization->addUser($user);
$course->enrollUser($user);

// authenticate new user


Auth::login($user);

// redirect to course page


return redirect($course->path());
}
Big Blocks - Closing example 58

This step revealed a lot. The actions vary greatly between validation, creating users,
authentication, and redirection. The code also contains split paths each with the same
core actions: enroll the user and redirect to the course page. This hints at the primary
action. While relevant, everything else is secondary.

Now we’re ready to refactor each sub-block. Starting at the top we’ll go through each
sub-block and see if there is a native way to perform the action or if the action belongs
at the current level.

The 7rst sub-block performs some request validation. This code uses the Laravel
framework. A quick Google search for “Laravel request validation” reveals Form Requests.
These are “custom request classes that contain validation logic”. Perfect! We can move
the sub-block there to handle the request validation more natively.
59 Big Blocks - Closing example

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, $id)
{
// get the course by id
$course = Course::findOrFail($id);

// determine if user is authenticated


if (Auth::check()) {
// enroll authenticated user
$course->enrollUser($request->user());

// redirect to course page


return redirect($course->path());
}

// create new user from request data


$user = User::create([
'first_name' => request('first_name'),
'last_name' => request('last_name'),
'email' => request('email'),
'password' => bcrypt(request('password')),
]);

// enroll new user


$course->organization->addUser($user);
$course->enrollUser($user);

// authenticate new user


Auth::login($user);

// redirect to course page


return redirect($course->path());
}
}

Next we get the course by id. The results from a Google search for “Laravel controller 7nd
model from request data” hint at model binding. We can use this to inject the $course as
a parameter to the controller instead of our sub-block.
Big Blocks - Closing example 60

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, Course $course)
{
// determine if user is authenticated
if (Auth::check()) {
// enroll authenticated user
$course->enrollUser($request->user());

// redirect to course page


return redirect($course->path());
}

// create new user from request data


$user = User::create([
'first_name' => request('first_name'),
'last_name' => request('last_name'),
'email' => request('email'),
'password' => bcrypt(request('password')),
]);

// enroll new user


$course->organization->addUser($user);
$course->enrollUser($user);

// authenticate new user


Auth::login($user);

// redirect to course page


return redirect($course->path());
}
}

We now reach the sub-block where the code paths split. So we want to look at the actions
of their sub-blocks. These are the primary actions of enrolling the user and redirecting to
the course page. There won’t be a native way to perform these actions. Instead, let’s see
if they belong at this level.

Enrolling the user is nicely within the enrollUser method. Similarly, redirecting to the
course page is a readable one-liner. Furthermore, both these actions align with the
current high-level.
61 Big Blocks - Closing example

This leaves us with create new user from request data. This sub-block contains low-level
details. How to encrypt a user password does not belong at the controller level. We can
push this down to the User by creating a factory method.

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, Course $course)
{
// determine if user is authenticated
if (Auth::check()) {
// enroll authenticated user
$course->enrollUser($request->user());

// redirect to course page


return redirect($course->path());
}

$user = User::createFromEnrollmentRequest($request);

// enroll new user


$course->organization->addUser($user);
$course->enrollUser($user);

// authenticate new user


Auth::login($user);

// redirect to course page


return redirect($course->path());
}
}

The next sub-block to enroll the user contains duplicate code with the caveat of the call
to addUser. We could move this within enrollUser. They are already at the course
level. Given the details of the call chain and matching method signature, this is an easy
move.
Big Blocks - Closing example 62

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, Course $course)
{
// determine if user is authenticated
if (Auth::check()) {
// enroll authenticated user
$course->enrollUser($request->user());

// redirect to course page


return redirect($course->path());
}

$user = User::createFromEnrollmentRequest($request);

// enroll new user


$course->enrollUser($user);

// authenticate new user


Auth::login($user);

// redirect to course page


return redirect($course->path());
}
}

We’re down to the last unique action - authenticate new user. This relates back to our
split code paths. The other sub-blocks relate to our primary action which belongs at this
level. But it’d be nice to remove the duplication.

In these situations, it can help to pseudo code the ideal 8ow. Often this is simply restating
the primary actions. For example, ideally the store method would 8ow as:
63 Big Blocks - Closing example

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, Course $course)
{
// enroll authenticated user

// redirect to course page


}
}

Now we turn this pseudo code into real code where ever possible.

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, Course $course)
{
$course->enrollUser(/* pass the authenticated user or new user */);

return redirect($course->path());
}
}

This exposes the missing pieces. Although we already knew the remaining action, this
prevents us from getting stuck. It’s now simply a matter of 7lling in the blank. Here, we can
introduce a private helper method which handles the action to pass the authenticated
user or new user. We’ll talk more about the name of this method in Naming. For now, the
name relays the action.
Big Blocks - Closing example 64

class EnrollmentController extends Controller


{
public function store(EnrollmentRequest $request, Course $course)
{
$course->enrollUser($this->createUserIfUnauthenticated($request));

return redirect($course->path());
}

private function createUserIfUnauthenticated(EnrollmentRequest $request)


{
if ($request->user()) {
return $request->user();
}

$user = User::createFromEnrollmentRequest($request);
Auth::login($user);

return $user;
}
}

By following a simple, three step process we’ve collapsed a big block to just two lines.
During this process, we not only improved the readability, but learned more about the
codebase and framework.
65 Naming - Naming

Naming

I went back and forth on the title of this book. I originally considered Foundational Code
or Fundamental Code. I wanted to relay its focus on the elemental or underlying practices
of writing code. After a few iterations I reached Basic Code. I didn’t necessarily like it, but
from there, Base Code was an easy leap. The 7nal title, BaseCode was simply a design
choice to remove the space.

The title is also a bit of a play on words. I enjoy puns and plays on words, so I knew I found
the winner. It relayed that fundamental aspect, but also paired well with the book’s target
- the codebase. You can see the same play on words and continuation of this vocabulary
in the table of contents - Bootstrap and Exit, instead of Introduction and Conclusion.
Finally, I wanted BaseCode to be pragmatic - containing practices you could reference
as needed and apply immediately. That’s why I called it a Field Guide and not a book.

I also didn’t want to use Clean Code in the title. First, while the practices within BaseCode
achieve “clean” code, it’s not their exact purpose. Second, there’s already a 400 page
book on Clean Code. In fact, Meaningful Names is one of its better chapters and heavily
in8uenced the rules and guidelines found here.

Making naming things easy


A good name is everything. But getting there may not be easy. In fact, naming things
is hard. This, of course, comes from one of the oft-used quotes in programming by Phil
Karlton:
Naming - Naming Rules 66

There are only two hard things in Computer Science: cache invalidation and naming
things.

Looking back on naming BaseCode we see two aspects which made naming things
a little easier: Context and Time. We can use context to guide us towards a speci7c
vocabulary. Once we have this vocabulary, it’s only a matter of time until we land on a
natural, readable name. Context and time make naming things easy. Neither of these can
be forced. Forcing a context leads to overly technical names. Forcing time leads to stress
which makes naming things feel hard.

Naming Rules
I know I said I don’t believe in hard rules, yet there are a few we can use when it comes to
naming. Following these rules will avoid wasting time on simple naming and allow us to
focus on naming which truly needs our attention.

Avoid abbreviations
Early programs needed to conserve characters for various reasons. As such,
abbreviations were commonly used in naming or even language syntax. I remember
single character syntax in the 68k instruction set. More readily, I see this on the Unix
command line with mv, cp, cd. In fairness, this has more to do with usability (speed
of typing) than naming. However, with modern computing and powerful IDEs there’s no
reason to abbreviate names within code.

My 7rst programs were written in C and Perl. Nearly every name was an abbreviation.
My Perl programs were essentially line noise. Over the years I started to add more
characters to my variable and function names. It wasn’t until learning Objective-C that I
used complete names.

I’ll admit, at 7rst I didn’t like it. Such long variable and method names. Yet I noticed I
learned the language quickly. I could read my code more readily. Eventually I embraced
the expressiveness. Now I try to replicate such naming even in the code I write using
other languages.
67 Naming - Follow conventions

We have to 8ip the practice of terse, abbreviated naming on its head. Compare the code
in a language like PHP for inserting an item into a collection to the same in Objective-C:

array_splice($c, 5, 0, [$i]);

[collection insertObject:item atIndex:5]

Of course language keyword and function naming can’t be changed. But we can change
the names of the variables and methods in our own code. When I look at a line like this I
smile and just shake my head.

$perm = 0777;
mkdir($d, $perm);

While a reader can arguably 7ll in the missing pieces, why make them do so? Within a
larger codebase, this adds unnecessary mental processing. Avoiding abbreviations can
free up reader focus, giving them more attention towards improving the codebase.

Follow conventions
Programming has been around for decades. Over this time certain conventions have been
established. Much like the honorary Hello World program is found in multiple languages,
so are naming conventions. Honoring these conventions improves the readability across
codebases and languages by establishing a common vocabulary.

For example, consider the traditional code snippet for a loop:

for ($i = 0; $i < 10; $i++) {


// ...
}

We may be tempted to use a di5erent name for i. After all it may be an abbreviation for
index. But this code snippet is found everywhere. There is no reason to use x or lcv
or counter. Every programmer knows the intent of i within the context of a for loop.
Like it or not, that makes it a convention. When it comes to naming, we want to leverage
conventions.
Naming - Leverage context 68

Languages may contain other naming conventions as well. Maybe they pre7x an interface
with I (.NET) or namespace classes with two or three letters (Objective-C). However alien
these may seem, we should adopt them to help readers transition between codebases
and languages.

Be mindful not to carry these conventions too far. There are boundaries. For example,
naming nested loop variables i, then j, then k, then l goes past the convention. If
you 7nd yourself pushing beyond a convention, it’s likely an opportunity to apply other
practices - Nested Code or Big Blocks.

These naming conventions mus mustt be formaliz


ormalized
ed by the o6cial language
documentation or cross-language code snippets. While helpful, popular naming
practices or those found within a single codebase are not conventions.

Leverage context
Too often we inject context into names. We see this in everything from database design
to data structures to property names. This leads to names becoming too long. Now we’ve
gone from one extreme to another. We have to get the balance just right when naming.

For example, we pre7x column names with the table name in our database design:

+---------------+
| users |
+---------------+
| user_name |
| user_email |
| user_password |
+---------------+

We nest names in data structures or object. In this case, components.component:


69 Naming - Leverage context

{
"components": [
{
"component": "Controller",
"number_of_classes": 5
},
{
"component": "Model",
"number_of_classes": 7
}
]
}

We also inject data types (often abbreviated) in our names. Consider many of the native
array and string functions in PHP:

strtoupper($title);

Often this context can be inferred in other ways. We need to put a premium on names. If
each word comes at a cost, and we have a naming budget, then we need to get the most
value from each word. Repeating words or injecting type information wastes our budget.
Instead, we can allow readers to glean the same information from the surrounding
context. This frees up our budget to spend on better words which enrich the context.

Let’s revisit the naming within the di5erent contexts to leverage the surrounding
information. For the database, our columns live within the users table, so we can
leverage this context and not repeat it within our names. This may also a5ord us the
characters to enrich the generic name column, using full_name or legal_name.

+---------------+
| users |
+---------------+
| name |
| email |
| password |
+---------------+

Similarly, repeat naming within a data structure or method chain is an opportunity to


Naming - Naming Guidelines 70

leverage context. In our JSON example, we are within the context of a component.
As such, having a property named component within an object named component is
ridiculously redundant. Instead we can leverage the parent object name and choose a
name which adds to the context, such as name or type.

{
"components": [
{
"type": "Controller",
"number_of_classes": 5
},
{
"type": "Model",
"number_of_classes": 7
}
]
}

Finally, whenever data types are injected into names there’s an opportunity to leverage
context. The strtoupper function within PHP is just one of many examples. This
function converts a string to uppercase. If we consider the context of this function we
know we are passing a string argument and returns a string. So, is the str pre7x providing
value? No. It adds nothing that isn’t otherwise already known.

Instead, we could remove this pre7x to achieve a more contextually rich, readable name.
Maybe simply toupper or better yet uppercase:

uppercase($title);

Naming Guidelines
Now that we’ve covered the hard rules, let’s cover some of the softer suggestions for
improving our naming. While these are aimed to be more general, they may vary between
codebases and languages.
71 Naming - Human Readable

Human Readable
Naming is critical to readability as it provides the most human signal among the
surrounding syntax. As programmers, our brains are trained to parse language keywords
(e.g. if, case) as well as technical terms (e.g. client, service). All of these imply something
to us. They create expectations of how things might work.

However, these aren’t very descriptive, at least not in human context. Furthermore, as
we’ve seen, this information can often be inferred in other ways. We don’t need to
perpetuate the technical names. This gets back to readability. We want our names to be
readable by humans. Allow the reader (who is technical) to infer the technical aspects
from the code. Use names which relay the human aspects of the code to the reader (who
is also human).

Again, this may seem hard. But our rules have already put us on the right track. We can
start to pick up on this theme of writing for humans. In fact we see this increasingly in
modern languages. Their goal is to create a human readable grammar – like a sentence.
We can do the same thing with the code we write by using names which read as a
sentence.

Express Domain
The best names are not just human readable but relay information about the domain.
While it would be great if any human could read our code and form an understanding, we
should focus primarily on those who work with the codebase. This provides yet another
context for us to leverage. Those familiar with the code likely share a common vocabulary
around the features and requirements of the system.

We can bring these into our names to strengthen the relationship between the code and
the real world. Ideally our objects and methods, even our variables use the same names
we use when talking about the project in the real world. For example, consider writing
an application for managing zoos. Using names like Zookeeper and feed express the
domain better than their technical counterparts Employee and work.

Background processing
Don’t worry about 7nding a good name immediately. Give it time. Remember, naming
Naming - Closing Example 72

things is hard if you force yourself to 7nd the perfect name on the 7rst try. Use context
and time. The context of your application may change. Your code will always change.
When they do you may need new name. However, give it time. The best names might take
two or three iterations. They may require building more of your application so the context
becomes richer and your domain objects begin to appear in spoken language. When that
happens refactor those names to continue to tighten the relationship between the code
and the real world.

If you get stuck, use a temporary name. When in doubt, give it a long name. Use a full
sentence. While this may sound like a ridiculous practice, it will give you more context.
Your brain will continue to work on this name in a background process. Often you’ll return
to this temporary name and immediately have a better name.

If nothing else, this practice provides keywords. Much like when brainstorming the title for
my book.

Closing Example
Let’s close with a quick example of applying these naming rules and guidelines to the
following stereotypically named block of code:

class EventHandler
{
public function handle($e, $func) {
// ...
}

public function remove($e) {


// ...
}
}

First, let’s expand the abbreviations:


73 Naming - Closing Example

class EventHandler
{
public function handle($event, $function) {
// ...
}

public function remove($event) {


// ...
}
}

Next, we can follow conventions to rename function to better relay its intent of a
callback:

class EventHandler
{
public function handle($event, $callback) {
// ...
}

public function remove($event) {


// ...
}
}

We can also leverage context better to enrich the names rather than repeat handle
and event. We’ll improve this more when we apply the guidelines. For now let’s 7x the
redundant EventHandler.handle:

class EventHandler
{
public function listen($event, $callback) {
// ...
}

public function remove($event) {


// ...
}
}
Naming - Closing Example 74

This is better. In reality, many would stop here. However, there’s more we can do to
improve the readability. As it stands, it’s pretty technical. Although most programmers
could read it, there’s an opportunity to make it more human readable and express the
domain.

The code mostly reads like a sentence:

An EventHandler listens [for] an event and callback and an EventHandler removes


an event.

Again, not very human. Notice we had to inject some words to form the sentence. We
could rename listen to register or bind to improve this:

class EventHandler
{
public function bind($event, $callback) {
// ...
}

public function unbind($event) {


// ...
}
}

Yet this is only one piece. We’d still have a very technical vocabulary. These names don’t
express what an event or callback is.

We can examine the domain to answer these questions. In doing so we may 7nd a more
expressive vocabulary. For example, if this code were part of a telephony application we
can express the handler and event in a better way.
75 Naming - Closing Example

class Operator
{
public function bind($call, $callback) {
// ...
}

public function unbind($call) {


// ...
}
}

Using this as a base, we can do some research on operator to leverage this context more.
Using a Dictionary or Thesaurus are great ways to add words to our vocabulary to better
express the domain. For example:

class Operator
{
public function connect($call, $endpoint) {
// ...
}

public function disconnect($call) {


// ...
}
}

This is just a 7rst pass. Nonetheless we improved the readability dramatically. We’ve
enriched the context by using names which relay not more technical information, but
more human information. As the codebase evolves, so too can we evolve these names.
Remember, take your time. Use these rules and guidelines to make naming things easy.
Removing Comments - Removing Comments 76

Removing Comments

About a decade ago I stagnated in how I wrote code. I was coding comfortable. There’s
nothing wrong with this. But personally, I wanted to push myself a bit more. So I decided
to switch jobs. In fact, I decided to relocate. At the time, the place to go was Silicon Valley.

I met Je5 Moore at a conference. The company he worked for was hiring and he gave
me a preliminary interview. We reviewed some code I had written and he immediately
asked, “What do you think about these comments?” I explained how they documented
the code. He challenged me to rewrite the code without comments and send it back to
him to review.

Although I interviewed with their company, I ultimately took a job at The New York Times.
However, I continued to challenge Je5 sending him code snippets with what I believed
to be useful comments. What about this? How about in this case? But this is where you
need them! He stood fast.

It took about two years to accept on my own there is rarely a need for code comments.
Although such a simple practice, removing comments was the initial push that put me on
the path to writing more readable code.

I wrote a post on Removing Comments. At the time, this post received the most tra6c
and for a brief moment was #1 on r/programming.

I won’t spend much time on the post as a commenter led me to yet another quote from
Rob Pike which explains the reasons behind removing comments perfectly:
77 Removing Comments - Removing Comments

[comments are] a delicate matter, requiring taste and judgment. I tend to err on the
side of eliminating comments, for several reasons. First, if the code is clear, and uses
good type names and variable names, it should explain itself. Second, comments
aren’t checked by the compiler, so there is no guarantee they’re right, especially
after the code is modi7ed. A misleading comment can be very confusion. Third, the
issue of typography: comments clutter code.

Let’s go through each of these points.

First, if the code is clear, and uses good type names and variable names, it should
explain itself.

So many comments describe what the code is doing. They simply explain the code.
However, we’re programmers. Code is our primary method of communication. Anything
else is secondary. Paradoxically, if we can’t explain the code with code, how can we
explain it better without code?

Rob Pike suggests good naming can combat the need for comments. We found this
is true in Naming. He goes on to say code “should explain itself”. This is not the same
as self-documenting code. Comments and documentation are separate things. While
comments may serve as documentation, the comments we want to focus on are inline,
code comments.

Second, comments aren’t checked by the compiler, so there is no guarantee they’re


right, especially after the code is modi7ed.

How many times have you read a comment that states the complete opposite of what
the code does. Further proof of the secondary nature of comments. Even if the comment
were right, it doesn’t matter, as the code reigns supreme. Whatever the code does is
what’s actually happening.
Removing Comments - Closing example 78

The reality is, even the best of comments immediately rot after being authored.
Comments are Dead Code. They are never executed, checked, or maintained. It is more
often the case a comment will mislead or confuse you than save the day.

Third, the issue of typography: comments clutter code.

This 7nal reason brings us full circle back to readability. Comments require additional
reading. They lower the signal. They’re noise. They’re cost without bene7t. Our time should
be spent improving the readability of the code rather than authoring a comment.

As the author of the code when writing a comment, ask yourself what would you do to
change the code so it doesn’t need the comment? Don’t write comments, write code.

The goal is not to remove all comments. Again, some comments serve as
documentation (docblocks, shared libraries, bug tracking). The goal is to challenge a
comment’s existence to see if the code can be written in a way that the intent . If it
can not, that code may be worth a comment.

Closing example
The practice of removing comments doesn’t have a lot of steps. It is more of a feeder
practice, which drives us to apply the other practices found within BaseCode. As such,
let’s get right into removing comments from the following real world code snippet.
79 Removing Comments - Closing example

class TwitterClient
{

// ...

// get tweets since yesterday for handle


public function recentTweets($handle)
{
// date for yesterday
$date = date('Y-m-d', strtotime('yesterday'));

// array of recent tweets to return


$retval = [];

// user_timeline endpoint gives move


// control over response data
$tweets = $this->client->get(
'statuses/user_timeline',
['screen_name' => $handle, 'exclude_replies' => true, 'trim_user' => true]
)

// loop over tweets to filter yesterday


// by comparing the date of tweet
foreach ($tweets as $tweet) {
if (date('Y-m-d', strtotime($tweet->created_at) >= $date)) {
$retval[] = $tweet;
}
}

// return recent tweets


return $retval;
}

// ...
}

First, a review of the code shows most comments describe what or how. We can
immediately remove any comment which simply explains what the next line of code does.
In this case, we 7nd this 1:1 ratio in a few spots. Most obviously for the return statement:
Removing Comments - Closing example 80

// return recent tweets


return $retval;

This comment provides no additional information. It comes at a cost to the reader


without any bene7t. Frankly, it’s almost condescending - presuming the reader (who
is a programmer) might not know that return $retval returns recent tweets. While
there is indeed room to improve the readability of this line, we can start by removing the
comment.

The other two 1:1 comments appear at the beginning of the method. However, the
comments do contain additional information. In this case, they describe the values the
variables hold. To Rob Pike’s point, these indicate an opportunity for better naming. We’ll
use the information in the comments to create a better name so we can remove the
comments.

We can actually do the same for the method comment as it too simply describes what the
following code does.

The resulting code becomes:


81 Removing Comments - Closing example

class TwitterClient
{

// ...

public function tweetsSinceYesterday($handle)


{
$yesterday = date('Y-m-d', strtotime('yesterday'));

$recent_tweets = [];

// user_timeline endpoint gives move


// control over response data
$tweets = $this->client->get(
'statuses/user_timeline',
['screen_name' => $handle, 'exclude_replies' => true, 'trim_user' => true]
)

// loop over tweets to filter yesterday


// by comparing the date of tweet
foreach ($tweets as $tweet) {
if (date('Y-m-d', strtotime($tweet->created_at) >= $yesterday)) {
$recent_tweets[] = $tweet;
}
}

return $recent_tweets;
}

// ...
}

This leaves us with just two comments. Before moving on though, our changes have left
$yesterday and $recent_tweets rather free-8oating. Although the new names have
helped, the comments hinted at how the variables have used. For a larger block of code
we might have lost some context. To mitigate this, we can use the proximity. We’ll simply
move these variables closer to where they are used.
Removing Comments - Closing example 82

class TwitterClient
{

// ...

public function tweetsSinceYesterday($handle)


{
// user_timeline endpoint gives move
// control over response data
$tweets = $this->client->get(
'statuses/user_timeline',
['screen_name' => $handle, 'exclude_replies' => true, 'trim_user' => true]
)

// loop over tweets to filter yesterday


// by comparing the date of tweet

$yesterday = date('Y-m-d', strtotime('yesterday'));


$recent_tweets = [];

foreach ($tweets as $tweet) {


if (date('Y-m-d', strtotime($tweet->created_at) >= $yesterday)) {
$recent_tweets[] = $tweet;
}
}

return $recent_tweets;
}

// ...
}

This allows us to focus on this block of code which now accounts for a majority of the
method. Even though the comment simply explains how the code works, it actually does
so more succinctly than the code. As such, it currently has value.

If we push ourselves to remove comments, how might we do this without the comment?
We want the same succinctness, but in code. We could treat this code as a little Big Block.
This would drive us to extract this logic elsewhere and give us an opportunity to use a
better name.
83 Removing Comments - Closing example

The resulting code becomes:

class TwitterClient
{

// ...

public function tweetsSinceYesterday($handle)


{
// user_timeline endpoint gives move
// control over response data
$tweets = $this->client->get(
'statuses/user_timeline',
['screen_name' => $username, 'exclude_replies' => true, 'trim_user' => true]
)

$yesterday = date('Y-m-d', strtotime('yesterday'));

$recent_tweets = $this->filterTweetsByDay($tweets, $yesterday);

return $recent_tweets;
}

// ...
}

Based on the proximity and the immediately following return statement, we can remove
the need for the temporary recent_tweets variable:
Removing Comments - Closing example 84

class TwitterClient
{

// ...

public function tweetsSinceYesterday($handle)


{
// user_timeline endpoint gives move
// control over response data
$tweets = $this->client->get(
'statuses/user_timeline',
['screen_name' => $username, 'exclude_replies' => true, 'trim_user' => true]
)

$yesterday = date('Y-m-d', strtotime('yesterday'));

return $this->filterTweetsByDay($tweets, $yesterday);


}

// ...
}

Some may also want to inline yesterday. This becomes a slippery slope as the only
remaining variable is tweets. So why not inline it as well? The result would be a
rather nested one-liner and the comment would pair with filterTweetsByDay.
Remember, the goal is readability, not density. In cases like these, it’s better to leave
(well named) temporary variables for the added context.

We are down to one comment. This comment actually relays why the code is written
a certain way. As such, it provides additional information. We would be hard-pressed
to relay this same information in code. We could extract this into a private method
called userTweets. But the comment would likely remain. As such it has withstood our
challenge. Although we will correct the grammatical errors and improve its readability by
leveraging its primary language of communication - English.
85 Removing Comments - Closing example

class TwitterClient
{

// ...

public function tweetsSinceYesterday($handle)


{
// user_timeline endpoint offers more parameters to limit
// size of the response which improves performance
$tweets = $this->client->get(
'statuses/user_timeline',
['screen_name' => $handle, 'exclude_replies' => true, 'trim_user' => true]
)

$yesterday = date('Y-m-d', strtotime('yesterday'));

return $this->filterTweetsByDay($tweets, $yesterday);


}

// ...
}
Reasonable Returns - Reasonable Returns 86

Reasonable Returns

One of my other passions is investing. There’s a simple concept when making


investments known as a reasonable rate of return. For the investment to be worthwhile,
it must generate a positive return. This leads to other investment principles like
compounding.

We can parallel these principles to return values in programming. Inline with the monetary
analogy, the creator of null called it a billion dollar mistake. His reasoning was the cost
of handling null. This means null actually has a negative rate of return as it leads to
more and more code written.

Avoiding bad returns


Really our focus is avoiding null. It’s just not a value that communicates anything.
It literally means no value. While our programmer brains jump to this technical
representation of absence, it is foreign to our human brains. If you don’t believe me, try
explaining null to a non-technical person. They might get it, but it will likely take a fair
amount of explanation.

Therein lies the poor return on investment. It’s not so much that null is a bad return
value in and of itself. It’s more that the amount of code required to handle a null value
often outweighs its use. Furthermore, all the handling of null interrupts a reader, making
it hard to follow the intent of the underlying code.
87 Reasonable Returns - Empty values

Many languages have null. When null is built into the language we will need to handle
it at some point. But for the code we write, it is possible to avoid null and other bad
return values with a few simple practices.

Empty values
Too often null is used to represent an empty value. However, within di5erent contexts
an empty value may be represented in di5erent ways. If we 7nd a di5erent value to better
represent this emptiness we can communicate more e5ectively and potentially reduce
code.

Going back to primitive obsession we 7nd null being used instead of a corresponding
primitive value. Consider the following, albeit hopefully 7ctitious, example:

function sum($a, $b)


{
$result = $a + $b;

if ($result === 0) {
return null;
}

return $result;
}

Obviously this is silly. By returning null we greatly compound any code calling sum. Why
return null when we could return zero? We wouldn’t. But this emphasizes the point -
there are better representations than null which also have less implications on the code.

All primitive types have representations for emptiness - zero, 0.00, an empty string,
an empty array. Objects are really where we use null more to represent emptiness.
We’ll review that shortly. For pretty much everything else though, null should be the
exception for representing empty values, not the norm.

Let’s look at a more complete example of compounded code when returning null.
Consider a few simple functions to determine the vowel count in a particular string.
Reasonable Returns - Empty values 88

function vowels($string)
{
$vowels = preg_replace('/[^aeiou]/', '', $string);

if (empty($vowels)) {
return null;
}

return str_split($vowels);
}

function vowel_histogram($string)
{
$vowels = vowels($string);

if ($vowels === null) {


return null;
}

$histogram = array_count_values($vowels);

ksort($histogram);

return http_build_query($histogram, '', ', ');


}

If we go to the lowest level we 7nd the vowels function returns null when no vowels
are found in the string. This in turn causing any calling function, in this case
vowel_histogram, to handle null.

Instead of returning null we can return a more natural empty value. To determine the
best return type we can review the primary action. For vowels it’s an array and for
vowel_histogram it’s a string.

Let’s return an empty array from vowels instead of null. The resulting code becomes:
89 Reasonable Returns - Expressive representations

function vowels($string)
{
$vowels = preg_replace('/[^aeiou]/', '', $string);

if (empty($vowels)) {
return [];
}

return str_split($vowels);
}

function vowel_histogram($string)
{
$histogram = array_count_values(vowels($string));

ksort($histogram);

return http_build_query($histogram, '', ', ');


}

This code is much less awkward. The null checks can be removed. The return type from
vowels is no longer mixed between array and null. All of which keep the code 8uid.
While this is not necessarily the same as 8uent (call chaining), avoiding null return values
can promote such code.

We could continue to refactor vowel_histogram to ensure we return an empty string


(http_build_query may return null). However, the largest return on investment will
be avoiding null at the lowest levels of our codebase as this prevents the handling code
from bubbling all the way up to the top level.

Exceptions are another way to represent truly exceptional behavior in code instead
of null. Unfortunately, they come with the same costs as null. When using
exceptions, determining the appropriate levels to throw and catch them will help
with readability.

Expressive representations
There are a handful of functions which return odd values. In JavaScript, indexOf returns
Reasonable Returns - Null objects 90

-1 when a substring is not found within a string. In C-based languages, strcmp returns
-1, 0, 1 when a string is less than, equal, or greater than another string, respectively.
Some languages even have functions which return 0 as well as false, leading to bugs
when not handled strictly.

As programmers, we are forced to memorize such return values. We commit these magic
numbers to memory and carry on. But there is a better way. Much like magic numbers are
bad, so too are these return values.

It took writing Objective-C before I noticed a better way. Within its string class, instead of
returning -1 when searching for a substring, they return a representation for not found.
Let’s look at a code snippet:

if ([string rangeOfString:@"foo"].location == NSNotFound) {


// string does not contain "foo"
} else {
// string contains "foo"
}

NSNotFound is far more expressive than -1. Even without knowing Objective-C, we
could read and understand this code.

Using more expressive representations through values, constants, or simple objects can
limit our use of null and greatly improve code readability.

Null objects
As we mentioned, null is best paired with an object. After all, what do you return to
represent the absence of an object? The original issue remains though - we have to
handle null. Most languages throw some kind of exception when attempting to call or
use null as an object. For example, Java and its ubiquitous Null Pointer Exception.

Among others, Objective-C is yet again a ready example for handling null more
gracefully. Any message sent (method call) to nil (null) returns nil. While this doesn’t
prevent the need to handle null entirely, it does greatly reduce it.

Unfortunately most languages don’t handle null gracefully. In addition, we can take this
a step further by creating our own, custom Null Object. These objects mimic the behavior
91 Reasonable Returns - Null objects

of its real counterpart but return appropriate empty values. This allows you to return this
object instead of null and keeps your code free of handling null. These objects also
provide a way to encapsulate this exceptional behavior in one place (as we saw in Using
Objects).

Let’s consider the following real world snippet to greet a user of our application:

function greeting()
{
$user = Session::user();

if ($user !== null) {


return 'Welcome ' . $user->getPreferredName();
} else {
return 'Welcome Guest!';
}
}

This code retrieves a User object from the current Session and uses their preferred name
to personalize their greeting. However, if a user is not logged in, we give them a generic
greeting.

If we avoided all the null handling, greeting could simply be written as:

function greeting()
{
return 'Welcome ' . Session::user()->getPreferredName();
}

In order to implement this code Session::user() needs to return an object which


responds to getPreferredName() (as well as other methods on the User class). A
User object meets this criteria, but null doesn’t. We could create a new object to
represent the state of not being logged in which mimics the behavior of a User object.
This becomes our null object or, in this case, our Guest object.
Reasonable Returns - Closing 92

class Guest
{
// ...

public function getPreferredName()


{
return `Guest`;
}

// ...
}

Such objects are straightforward to implement. Since these objects often represent the
data being passed around your application at a low level, they yield a large return on
investment just as we saw with Empty Values.

Closing
The practices in these 7nal chapters are increasingly conceptual. As such, 7nding a
comprehensive, real-world example would not be di5erent than the code snippets we
already covered. For now, I will simply close by outlining the two key takeaways for
Reasonable Returns.

1. Instead of null, return an empty value which removes code for handling null.
This empty value can be a simple primitive or object. Focus on the data structures
and functions at the lowest level of the codebase 7rst and work up to the highest.

2. Ensure all values used within a codebase are expressive. Using constants, enums,
translation objects, or value objects all provide more context around the values.
Leverage these to make the code as human readable as possible.
93 Rule of Three - Rule of Three

Rule of Three

I hate repeating myself. It makes me feel as though I lost all progress. So when I came
across the DRY principle (Don’t Repeat Yourself) I adopted it immediately. In fact, I wrote
an article entitled “Staying DRY” in which I shared the active generator I mentioned in the
Introduction. At the time, I went to great lengths to ensure my code was as dry as an arid
desert.

The DRY principle is so appealing because it gives code a sense of organization, tidiness,
even completeness. In the early stages of programming, this helps combat the natural
disorder of incomplete code. It may even help to combat imposter syndrome.

However, repetition is an integral part of growth. Other common advice supports this, for
example “practice makes perfect”. We can’t expect to learn a new skill or complete a large
task without some amount of repetition or rework.

So too it is for writing code. Our code is constantly changing. We must expect some
amount of duplication, some amount of repetition. To 7ght this repetition would be to 7ght
growth.

Defer Until Necessary


It wasn’t until years later I came across the Rule of Three. It was a few more years
until I actually started practicing it. The Rule of Three is a principle found within many
disciplines. I found it in programming from Martin Fowler’s Refactoring who found it from
Don Roberts. Roberts’ guideline states:
Rule of Three - Defer Until Necessary 94

The 7rst time you do something, you just do it. The second time you do something
similar, you wince at the duplication, but you do the duplication thing anyway. The
third time you do something similar, you refactor.

Martin Fowler sums this up simply as:

Three strikes and you refactor.

The key take away is that we don’t refactor immediately. In fact we keep the duplication
for a bit. To Roberts’ point we should wince in pain before addressing the duplication. To
align with our analogy, the code should experience growing pains.

So what’s the harm in avoiding duplication? Shouldn’t we want to prevent pain? It’s hard
to answer with “No”. After all, it’s a good thing to prevent pain. But as with most things in
programming, it’s a trade o5. This trade o5 is what makes the answer not so simple.

There’s another side to avoiding duplication. Sandi Metz expresses it well in her talk all the
little things:

Duplication is far cheaper than the wrong abstraction.

So, in reality, there’s pain on both sides. By allowing duplication we have the pain of
maintaining this duplication throughout the codebase. By avoiding duplication we have
the pain of writing code which may be unnecessary and hinder future growth.

The catch is time. We won’t immediately know if the code we wrote to avoid duplication
will be correct. Maybe it will, maybe it won’t. Only time will tell. So we’re taking a risk.

The interesting thing is that the other approach mitigates this risk. By allowing the
duplication we also allow ourselves time. Time for the code to grow naturally and
determine which pieces are truly duplicated.
95 Rule of Three - Defer Until Necessary

If the duplication remains, we may then make a more informed decision on how to
refactor the code because we allowed ourselves time. In doing so, we mitigate the risk of
writing the wrong abstraction.

So both approaches indeed have the potential for pain. Yet only allo allowing
wing duplica
duplication
tion
mitigates this risk by allowing time. Instead of following the DRY principle, follow the DUN
principle or Defer Until Necessary. Don’t try to clean up duplication immediately. Defer it
until it becomes absolutely necessary to address.

In the beginning the Rule of Three helps with this deferral. To demonstrate the Rule of
Three I often present a numerical series to an audience. I like this better than code for
two reasons. First, it’s succinct. Second, it avoids distractions by implementations details
within a code snippet.

Think of a test where you’re given a series of numbers and asked to provide the next
number. Suppose I provided you with the sequence 2 and ask, “What number comes
next?”

Audiences typically respond with 2 or 3. There’s de7nitely not a consensus. It’s split with
many abstaining from answering. After all, it could be a decimal series having 2.1 next. It
could be a descending series with the next number being 1. It really could be anything.

Then I provide another numbers in the sequence: 2, 4 and again I ask, “What’s next?”

There’s more audience participation now. Although still pretty split. Some say 6. Some say
8. Some say 16. Again, it could be any of these.

I then provide the third number in the series: 2, 4, 16 and I ask 7nally, “What’s next?”

There’s a pretty strong consensus now among the programmer audience who agree the
next number is 256. While mathematically their might be other numbers which satisfy
another series, in this case, it is indeed a squared series.

This demonstrates the Rule of Three. It took multiple data points before accurately
recognizing a pattern. Technically it was the fourth iteration before being able to
con7dently determine the pattern.

And so it goes for code. It is unlikely that from a single instance of code we will accurately
be able to determine a usage pattern and create the correct abstraction. We need to allow
the code to be duplicated multiple times before understanding it well enough to abstract
Rule of Three - Closing example 96

it.

In the end, it’s not about three times. Remember, there are no hard rules. The real rule is
that you will be smarter in the future. As such, it is in your best interest to defer decisions
until absolutely necessary.

Closing example
Code snippets demonstrating duplication require a time-lapse. In order to continue to
provide real world examples I needed to draw upon one of my own codebases. The
perfect example was Laravel Shift. While this codebase has changed dramatically over
the last few years, it has ne
nevver been rewritten.

What’s more important than the implementation is the progression. I want to look at how
the design changed at three di5erent points in the codebase’s history.

As some background, Shift is a service that upgrades your Laravel (A PHP framework)
projects between major versions. It clones your project, analyzes your code, and attempts
to detect and automate any changes required by the new version.

It’s a pretty ambitious service. In the beginning I had pretty ambitious design ideas. I
envisioned a Linux like system where I had many tiny commands which together could do
big things. I thought about creating and traversing the abstract syntax tree to statically
analyze the code.

This was the code for the initial release:


97 Rule of Three - Closing example

// ...

$shift_version = $argv[1];
$project = $argv[2];
$src_branch_name = $argv[3];
$service = $argv[4];
$clone_url = $argv[5];

list($vendor, $package) = explode('/', $project);

$workspace_path = BASE_PATH . '/tmp/' . date('YmdHis');


$vendor_path = $workspace_path . '/' . $vendor;
$project_path = $vendor_path . '/' . $package;

$shift_branch_name = 'laravel-shift-' . $shift_version;

exec('mkdir -p ' . $vendor_path);


chdir($vendor_path);
exec('git clone ' . $clone_url);
chdir($project_path);
exec('git fetch origin');
exec('git checkout -b ' . $shift_branch_name . ' origin/' . $src_branch_name);

shift_psr2();
commit('psr2.md');
shift_compiled_path();
shift_create_bootstrap_cache();
commit('cache.md');
shift_add_provider();
shift_auth_controller();
shift_validation();
shift_eloquent();
shift_commands();
shift_handlers();
shift_blade();
shift_tests();
commit('Add $baseURL property to TestCase');
shift_bindings();
commit('bindings.md');
add_comment('This update is incomplete until you review your dependencies and run `compose

// ...
Rule of Three - Closing example 98

That’s right, just a single, procedural PHP script. We don’t have to look far to 7nd
duplication. We see it immediately in just the commit() calls. I assure you it was far worse
within the function bodies. Pretty gross, right? To Roberts’ point, it makes you wince.

Why didn’t I code what I had envisioned? Honestly, it was timing. I met Taylor Otwell (the
creator of Laravel) at a conference and he thought the idea was cool. Laravel 5.1 was soon
to be released. So I started working on it right there during the conference. I had to make
hard decisions in order beat the release of the new version.

Although I wasn’t happy with the code, I was able to launch the initial service. When it
came time for Shift to support other versions, I didn’t redesign much. After all, it worked.
I just duplicated several of the functions and copied them to multiple other PHP scripts
which I loaded based on the version.

// ...

$shift_version = $argv[1];
$project = $argv[2];
$src_branch_name = $argv[3];
$service = $argv[4];
$clone_url = $argv[5];

list($vendor, $package) = explode('/', $project);

$workspace_path = BASE_PATH . '/tmp/' . date('YmdHis');


$vendor_path = $workspace_path . '/' . $vendor;
$project_path = $vendor_path . '/' . $package;

$shift_branch_name = 'laravel-shift-' . $shift_version;

exec('mkdir -p ' . $vendor_path);


chdir($vendor_path);
exec('git clone ' . $clone_url);
chdir($project_path);
exec('git fetch origin');
exec('git checkout -b ' . $shift_branch_name . ' origin/' . $src_branch_name);

require 'src/' . $shift_version . '.php';

// ...
99 Rule of Three - Closing example

In regards to duplication, this code is just as bad if not worse. Now I had duplication not
just within one 7le, but across many 7les. However, I still didn’t have a clear direction for
the code. I hadn’t abandoned my visions of small, shared components. But I didn’t want
to force it either. I was willing to take a little more pain until the right abstraction emerged
naturally.

This version of the code ran for almost two years. It wasn’t until I began writing Shifts for
other types of codebases, beyond Laravel, that I addressed the duplication. At that time,
I had a far better understanding of how the upgrade process ran.

To this day the primary actions are still within this original simple script. It accepts the
various input, processes the upgrade, and sends the output. But the duplication has been
abstracted into objects follow the facade and factory pattern.
Rule of Three - Closing example 100

// ...

$shift_number = $argv[1];
$shift_code = $argv[2];
$package = $argv[3];
$source_branch = $argv[4];
$service = $argv[5];
$access_token = $argv[6];

$shift_branch = 'shift-' . $shift_number;

list($vendor, $project) = explode('/', $package);

$workspace_path = BASE_PATH . '/tmp/' . TIMESTAMP;


$vendor_path = $workspace_path . '/' . $vendor;
$project_path = $vendor_path . '/' . $project;

Shift::run_system_command('mkdir -p ' . $vendor_path);


$clone_url = clone_url($service, $package, $shift->getType(), $access_token);
Shift::run_system_command('git clone ' . $clone_url);
chdir($project_path);
Shift::run_system_command('git fetch origin');

Shift::run_system_command('git checkout -b ' . $shift_branch . ' origin/' . $source_branch

$shift = \Shift\Factories\ShiftFactory::createShiftForSku($shift_code);
$tasks = $shift->getTasks();

$comments = [];

foreach ($tasks as $class) {


$comments = array_merge($comments, $task->perform());
}

$result = Shift::run_system_command('git push origin ' . $shift_branch);

$gitClient = \Shift\Factories\GitClientFactory::createGitClient($service, $package, $acces

$message = file_get_contents(BASE_PATH . '/resources/views/git/' . $shift->getCode() . '/p


$message = str_replace('', $shift_branch, $message);

$pullRequest = $gitClient->createPullRequest($source_branch, $shift_branch, $shift->getNam


101 Rule of Three - Closing example

foreach ($comments as $comment) {


$gitClient->addPullRequestComment($pullRequest, $$gitClient->getCommentPresenter()->pr
}

// ...

There’s still more to be done. For example, the pipe and 7lter design of the Tasks has
limitations. It’s not always so easy to pass data. But this is a new problem, not a result of
the initial duplication. As such, the counter resets back to zero and I’ll wait until I feel the
pain before refactoring this code.

If I had started removing duplication from the beginning I would not have launched
the service in time. I fully expect I would have coded something grand, but it unlikely
would have been what was necessary. The result of allowing the duplication and, instead,
deferring until necessary ultimately brought me to point where where my initial vision
met the demands of reality. Nothing more, nothing less. This, in turn, gave me more
con7dence to continue practicing this deferral.

I want to close with one more quote. It’s from an interview with Kent Beck on Full Stack
Radio. They were talking about the subject of abstracting code and emergent design.
Kent Beck describes an experiment he tried which I found inspiring and is the eventuality
of the Rule of Three.

I did a little experiment. What if I deliberately stopped trying to predict the future and
limit my design horizon to six months? Things went better for me. I was less over
engineering. I was making progress sooner. I was less anxious. Things were cleaner,
easier to understand. So what about three months? One month? I never reached a
limit with that experiment…
Symmetry - Symmetry 102

Symmetry

We touched upon symmetry multiple times throughout BaseCode. It is one of those


programming principles that can take a long time to master. Symmetry may vary from
codebase to codebase, time to time, and programmer to programmer. As such, it remains
elusive. For those reasons, I have saved it for last.

I also saved the best for last. Symmetry underlies all code readability. We 7nd symmetry in
format, naming, and structure. Symmetry communicates the human side of code. When
code has symmetry, it 8ows more naturally.

I always sought symmetry. Even before programming, I liked knowing what I considered
the polar opposite of things. Words are the ready example, north and south, summer and
winter, push and pull. This last example makes me think of array_push and array_pop
in PHP. Something about these functions lacks symmetry. My programmer brain
understands the data structure and remembers pop o5 the stack. But it’s not called
stack_push or stack_pop. So my human brain always wonders.

I 7rst came across the principle of symmetry in Implementation Patterns where Kent Beck
de7nes this as:

Symmetry in code is where the same idea is expressed the same way everywhere it
appears.
103 Symmetry - Symmetry

He provided the following code snippet:

void process() {
input();
count++;
output();
}

While this code is indeed easy to read, it lacks symmetry. First, as discussed in Big Blocks,
the levels vary. input() and output() are high-level abstractions, but count++ is a
low-level implementation.

He initially rewrites this to be more symmetrical as:

void process() {
input();
incrementCount();
output();
}

This achieves symmetry in regards to the abstraction levels. However, it still lacks
symmetry in naming. Although technical in name, input and output read well within
the context. For example, process input and process output. But incrementCount
is a bit too technical, in addition to being a compound name. As such, it still relays
implementation detail, and is not symmetrical with the other names.

Beck renames this method in his 7nal, symmetrical version:

void process() {
input();
tally();
output();
}

There’s something undeniably satisfying about this code. It’s simple, even generic. It has
excellent 8ow. As a reader of this code we’d 7nd it approachable, even natural. Without
knowing more, we might even start to intuit what to expect next.
Symmetry - Seeking Symmetry 104

Seeking Symmetry
It’s pretty easy to read symmetrical code. We recognize it as being more consistent and
more intuitive. Writing symmetrical code, on the other hand, is not so easy. It takes time
to form symmetrical code. In reality, we should always keep symmetry in mind, but apply
it last, after all other practices.

The good thing is once we begin to apply symmetry, it becomes easier to mold and shape
the code into its pure form. To help start this process, let’s look more closely at applying
symmetry progressively to code.

Syntactic Symmetry
Just as with the BaseCode practices, the 7rst place to start with symmetry is syntax. We
don’t want curly braces on the same line for one half of the code and on a new line for
the other half. The code should be formatted in a consistent way.

If there is a symmetry format, we can move on to the structure of the code. Examples
include placing declarations at the top or grouping methods by visibility. We could even
be more granular, such as order of parameters within a set of code.

PHP is notorious for lacking symmetry in its order of parameters. For example, the
signature for array_filter($input, $callback) has callback as the second
parameter, whereas array_map($callback, $input) has callback as the 7rst
despite both being array functions.

We can continue to even the lowest level of syntax, focusing on symmetry with operators.
For example consistently using && instead of textual and or vice versa.

These choices will depend on the codebase as well as the team. But their application
remains the same. To Kent Beck’s point we must apply these in the same way everywhere
it appears.

Semantic Symmetry
The next level of symmetry deals with semantics. In this case, symmetry will come
from names. We covered several practices in Naming, now we can practice symmetry in
105 Symmetry - Systemic Symmetry

naming.

Going back to polar opposite, we want to ensure we properly pair names. If we use on we
should use off, not disable, stop, or end. If we inject certain words or patterns into
our names, we must inject them universally within the current scope and ideally across
the codebase.

A common example are getters and setters. Tradition favors pre7xing these with set and
get, while modern languages may not. Similarly, some prefer to pre7x methods which
return boolean values with is, can, or has. Again, the choice is our own. But we must
remain consistent.

Another point on semantics to consider is how the code reads. For example, statements
which read in a positive tone contrasted with by statements which read negative. This
requires readers to invert the logical 8ow adding unnecessary overhead. Instead, we can
improve symmetry by maintaining a positive tone or negative tone. While the compiler
may not care, humans may 7nd a positive tone nicer to read.

Systemic Symmetry
The 7nal level of symmetry is systemic. Within our codebase a reader should expect a
consistent use of abstractions. This deals not only with our abstraction levels, but also
the patterns which are used.

Too often we play design pattern bingo within a codebase - using too many di5erent
patterns. This prevents the reader from getting a feel of the application. Given the choice
between adopting a new pattern or using one already found with in the codebase,
symmetry favors reuse.

When abstractions lack symmetry this often indicates an opportunity to apply another
practice such as those found Using Objects or Big Blocks. The goal of systemic symmetry,
as well as symmetry in general, is to help readers gain a feel for the application from
anywhere in the code so they may ultimately understand it everywhere.

Closing Example
The closing code sample comes from the Oh Dear! codebase. Oh Dear! is a service which
Symmetry - Closing Example 106

monitors websites. This particular snippet comes from the Check class and determines if
a check needs to run.

class Check
{
// ...

public function needsToRun(): bool


{
if (!$this->site->team->hasActiveSubscriptionOrIsOnGenericTrial()) {
return false;
}

if (!$this->enabled) {
return false;
}

if ($this->type === CheckType::UPTIME && optional($this->latestRun())->hasFailed()


return true;
}

if (is_null($this->latestRun())) {
return true;
}

if ($this->previousRunHasCrashed()) {
return true;
}

if ($this->checkHasAlreadyBeenScheduledOrIsRunning()) {
return false;
}

return $this->latestRun()->hasEndedMoreThanMinutesAgo($this->minutesBetweenRuns())
}

// ...

}
107 Symmetry - Closing Example

As evident from the description, this code already reads well. It applies many of the
practices within BaseCode - good naming, guard clauses, abstractions, and bite-sized
blocks. While it’s good as it stands, we could improve its readability even further with
symmetry.

Let’s take a 7rst pass to check for syntactic symmetry. The code is well formatted and the
syntax, for the most part, is consistent. The code utilizes guard clauses heavily. Focusing
on them more closely reveals they return true and false. By the nature of a guard
clause, we’d expect all the negative paths to be handled. Technically the rest of the if
statements are a form of early return, likely optimizations for the primary action.

We can improve symmetry by 7rst having all checks which return false, then all those
which return true.
Symmetry - Closing Example 108

class Check
{
// ...

public function needsToRun(): bool


{
if (!$this->site->team->hasActiveSubscriptionOrIsOnGenericTrial()) {
return false;
}

if (!$this->enabled) {
return false;
}

if ($this->checkHasAlreadyBeenScheduledOrIsRunning()) {
return false;
}

if ($this->type === CheckType::UPTIME && optional($this->latestRun())->hasFailed()


return true;
}

if (is_null($this->latestRun())) {
return true;
}

if ($this->previousRunHasCrashed()) {
return true;
}

return $this->latestRun()->hasEndedMoreThanMinutesAgo($this->minutesBetweenRuns())
}

// ...

This 8ow give the reader a clear division between the guard clauses and early returns.

Continuing to focus on the syntax, most of these checks are simple expressions making
method calls. Except for one. Similar to Kent Beck’s example, we have implementation
109 Symmetry - Closing Example

details among higher abstractions. In this case, the $this->type ===


CheckType::UPTIME. We’re going to defer this one for now.

Moving on to semantic symmetry, let’s start with the naming. The initial code contains
rich, readable names. Some may be a little too rich. As discussed in Naming, we want to
balance human readability with context. This code leans slightly to the human readability
side. While better than the alternative, we can pare them down slightly. For example,
checkHasAlreadyBeenScheduledOrIsRunning could simply become
alreadyScheduledOrIsRunning by removing some 7ller words and leveraging the
current context of Check.

In addition, we can improve symmetry by better name pairing. For example, currently
we’re interchanging last, latest, and previous. While it’s possible they di5er within the
domain, these are all within the Check class and therefore can be uni7ed.

Applying symmetry in naming yields the following code:


Symmetry - Closing Example 110

class Check
{
// ...

public function needsToRun(): bool


{
if (!$this->site->team->subscribedOrUsingTrial()) {
return false;
}

if (!$this->enabled) {
return false;
}

if ($this->alreadyScheduledOrIsRunning()) {
return false;
}

if ($this->type === CheckType::UPTIME && optional($this->lastRun())->failed()) {


return true;
}

if (is_null($this->lastRun())) {
return true;
}

if ($this->lastRunCrashed()) {
return true;
}

return $this->lastRun()->endedMoreThanMinutesAgo($this->minutesBetweenRuns());
}

// ...

This pass tightens up the naming by removing easily inferred context from the name
and using consistent terms and tense. Before moving on to systemic symmetry, there’s
one element of semantics we could focus on. Again, much of this code has simple
expressions. Yet a few negate the expression. This adds a bit of complexity which is easily
111 Symmetry - Closing Example

avoided by applying symmetry.

By inverting the logic in these few places, we can keep all expressions positive.

class Check
{
// ...

public function needsToRun(): bool


{
if ($this->site->team->inactiveSubscription()) {
return false;
}

if ($this->disabled()) {
return false;
}

if ($this->alreadyScheduledOrIsRunning()) {
return false;
}

if ($this->type === CheckType::UPTIME && optional($this->lastRun())->failed()) {


return true;
}

if ($this->firstRun()) {
return true;
}

if ($this->lastRunCrashed()) {
return true;
}

return $this->lastRun()->endedMoreThanMinutesAgo($this->minutesBetweenRuns());
}

// ...

}
Symmetry - Closing Example 112

Removing the negations creates a consistent 8ow. Readers can now move through
each condition more naturally, without having to apply additional logic. This pass also
removed one of the remaining implementation details by replacing !$this->enabled
with $this->disabled().

Let’s move on to the 7nal set of symmetries and analyze the systemic nature of the code.
We can revisit the one remaining implementation detail now. Taking a closer look, not only
is it missing an abstraction, but it contains a relatively bit of nested code wrapped in
optional(). This function provides inline handling of null. While useful, it’s really only
protecting against cases when there is not a last run.

We can alter the order of the code to avoid the need for using optional(). Focusing just
on this intermediary step, the resulting code might be:

// ...

if ($this->firstRun()) {
return true;
}

if ($this->type === CheckType::UPTIME && $this->lastRun()->failed()) {


return true;
}

// ...

From here, we have two possible paths. One which abstracts the $this->type ===
CheckType::UPTIME and one which abstracts the entire condition. The one which
is most symmetrical should win. In this case, the latter not only removes the
implementation details, but also removes the compound conditional aligning it better with
the surrounding code.

Now the order doesn’t matter, preventing the code from containing a hidden temporal
dependency. In addition, the abstraction allows us to explain the intent of this otherwise
dense code.
113 Symmetry - Closing Example

// ...

if ($this->retryUptimeCheck()) {
return true;
}

// ...

It’s worth noting this positive feedback loop. As we continue to improve the symmetry of
this code, we more easily spot the bits lacking symmetry. A purer form of this code may
have been unknown before, but now its really taking shape.

Let’s review the full code for any 7nal improvements.


Symmetry - Closing Example 114

class Check
{
// ...

public function needsToRun(): bool


{
if ($this->site->team->inactiveSubscription()) {
return false;
}

if ($this->disabled()) {
return false;
}

if ($this->alreadyScheduledOrIsRunning()) {
return false;
}

if ($this->firstRun()) {
return true;
}

if ($this->retryUptimeCheck()) {
return true;
}

if ($this->lastRunCrashed()) {
return true;
}

return $this->lastRun()->endedMoreThanMinutesAgo($this->minutesBetweenRuns());
}

// ...

This looks pretty symmetrical. There’s just one last aspect that’s di5erent than the rest.
We’ve simpli7ed everything to simple conditions by improving our abstractions with the
exception of two.
115 Symmetry - Closing Example

First, there’s some inappropriate intimacy happening in the initial check. The class
reached through the site relationship which reaches through its team relationship. As
practiced in Big Blocks this should likely be moved to a higher level than needsToRun.

Second, while readable the primary action is not only chained, it calls a method with
an argument. None of the other methods require parameters. This could be abstracted
further to again encapsulate some of these implementation details as well as better
communicate intent.

We’ll stop with these changes as we have achieved a su6cient level of symmetry for the
reader to easily 8ow through this method.

Here’s the 7nal code:


Symmetry - Closing Example 116

class Check
{
// ...

public function needsToRun(): bool


{
if ($this->disabled()) {
return false;
}

if ($this->alreadyScheduledOrIsRunning()) {
return false;
}

if ($this->firstRun()) {
return true;
}

if ($this->retryUptimeCheck()) {
return true;
}

if ($this->lastRunCrashed()) {
return true;
}

return $this->readyToRerun();
}

// ...

You can watch a video of this refactor, as well as additional refactors, on the
BaseCode YouTube Channel.
117 Exit - Exit

Exit

You’ve reached the end. Before you exit I want to leave you just as we started – on a
pragmatic note. I 7rmly believe the 10 practices within BaseCode work together to yield
the most readable code. Said another way I don’t believe you can 7nd readable code that
doesn’t exhibit some or all of the practices within BaseCode. So while you were welcome
to read these practices in any order, I do hope you read all together.

I know many programmers will dismiss this 7eld guide as too fundamental. Some readers
may even skip over entire practices. Admittedly they are fundamental. This is intentional.
Too often programmers get caught up in “best practices”. The reality is these vary greatly
between codebases and languages. Nonetheless, people will still push these as how you
must write your code.

As I said in the Introduction maybe we’re not there yet. Maybe they’re not there yet.
Although code is ultimately binary, it doesn’t mean we have to be. It doesn’t have
to be right or wrong. It doesn’t have to be so technical. Code should be simple and
approachable. It should be human. Never be afraid to do the simple thing.

The truth is when you strip away all the design patterns and algorithms and what remains
are the basic elements of programming. These are the focus of the practices within
BaseCode. Building upon them will give you con7dence and allow you to be agile and
nimble when writing code. This means you can change code easily. No more big rewrites,
no more exclaiming “this code sucks”. Instead, you’ll be able to identify which elements
are missing from code and improve the code by applying them. In doing so you write code
that is approachable by others, easy to maintain, and honestly fun to work with.
Exit - Exit 118

Since you’ve read this 7eld guide I assume you believe (as I do) that programming is a
craft. There is no hard set of rules we can follow to write the best code. We have to
practice, be self-aware, and push boundaries in order to improve our craft. It’s important
to remember this takes time. While some of the practices in this book can be applied
immediately, like Formatting or Dead Code, others, like the Rule of Three and Symmetry
will take time. That’s okay. Take your time. Continue applying the practices you’re
comfortable with and the others will follow. You’ll get faster. You’ll discover certain objects
that you’ll use in other codebases. You’ll develop a vocabulary for naming things. And,
while it might take the longest, you’ll 7nd symmetry. For me that’s peak programming.
But the ascent is challenging and the terrain is ever-changing. That’s what makes
programming great. These practices will help you along your journey. Just keep writing
readable code!

You might also like