I was still studying software engineering when I got in contact with the Broken Window Theory. Although I can’t remember when I heard about it for the first time, it made a lasting impression on me. The theory comes from an experiment that was executed in the late 60s, of which you will find all about in a great article that NPR put together.
In short, researchers placed two cars, stripped from their license plates, in two different areas: the first one in a problematic neighborhood of New York, and the second one in a rich neighborhood of the state of California. The first car was vandalized within a few minutes after being abandoned, while the second one was left alone for a few days. The researchers then decided to break a window on the second car. A few minutes later, someone started stealing the car’s parts, and it quickly became as destroyed as the first one.
Recently, I shared this story with a colleague of mine during a coffee break. A few days later, he told me that he’s now applying this philosophy to his coding and code review style. This was music to my ears, as this is something that I’m always trying to pass to the teams that I work with.
But how does it work exactly?
Let's take a look at the following piece of code, found in a codebase that I've picked up and currently am working with.
def month_work_days_count(month = DateTime.now.month) count = 0 end_date = month >= DateTime.now.month ? (DateTime.now - 1) : DateTime.new(DateTime.now.year, month, -1) beginning_of_today = DateTime.new(end_date.year, end_date.month, end_date.day, 0, 0, 0, end_date.zone) beginning_of_month = beginning_of_today - beginning_of_today.mday + 1 (beginning_of_month..beginning_of_today).each do |day| next if BankHoliday.holiday_on?(day) count += 1 if (1..5).cover?(day.wday) end count end
The goal is not to look into the reasons of how and why we ended up with such overcomplicated solution, but rather to put ourselves in the shoes of the developer that was asked to change something in it.
Now, ask yourself the following questions:
- Will it be easy to change this code?
- If someone develops a new function somewhere else on the project, is it likely that they follow the programming language’s code conventions?
An honest answer will point to no on both questions. Overcomplex code is something that we, developers, are used to find. We know by heart that it takes time to understand and change the code.
Now let's look at a refactored version of the previous code, and ask the same two questions.
# Calculates and returns working days on a month, up and including a given date # Params: # +date+:: +Date+ object with the month to calulate and the date to stop def count_working_days_including(date) (date.at_beginning_of_month..date).count do |day| (1..5).cover?(day.wday) && !BankHoliday.holiday_on?(day) end end
Changing this piece of code is, by far, easier than changing the previous one. And contrarily to what usually runs through a developer's mind, when deadlines are tight, keeping the codebase small and well-organized helps.
We often don't think about how keeping the code in good shape sets the bar for others working on the project. But a project with well written code will motivate everyone to mimic the behavior, follow the conventions and deliver code with similar quality. If there are no broken windows on the project, people will be less tempted to break one.
The good thing about nurturing an unbroken window behavior is that, ultimately, it will propagate through the entire development team, and impact all projects in the company. Hopefully, this will also work as a sign to other departments, and the philosophy gets adopted outside the realm of the company’s codebase.
The broken window experience comes to my mind every now and then, as it exemplifies the importance that minor behaviors have on avoiding problems from scaling and ultimately systems from falling into chaos. It is one of the rules that it's worth to live by.