Originally published on the BigCommerce Developer Blog.
The idea for this post started over a beer.
I was catching up with Dan Murrell, Software Engineering Team Lead at BigCommerce, at a Friday happy hour when the conversation turned to a recent project Dan was working on. He told me how he had gotten well into development on a project, only to turn around and start over. He had realized that the code had become indecipherable to anyone but himself, and because of that, a course correction was needed. Not just because he didn’t feel like the quality met his standards, but also for the sake of any future engineers that were assigned to the project. He was currently in the process of reworking the project, organizing the code according to a set of guidelines designed to make it easy for other developers understand what the code was doing with minimal ramp up time.
This idea interested me, because it’s something that developers find themselves on both sides of. At different times, we might be the one who inherits a spaghetti code base — or, whether we want to admit it or not — the one creating it. Dan pointed out that over its lifetime, a project changes hands many times. And each owner has the opportunity to leave things better than they found them, or to create a giant mess that makes future developers curse their name.
I wanted to find out more about how Dan thinks about writing clean and readable code, so I met up with him again (no beer this time) to dig deeper into what it means to write code with empathy for future programmers.
A Life of Its Own
The first thing to understand is that most software has a life far beyond the original developer who created it.
“The reality is, if you’re doing any kind of software for a company, it’s going to almost certainly last far longer than you expected,” Dan says. But knowing that your code will live on and be passed to multiple owners changes your mindset about the code you write. “Your tendency is to make the code that you’re writing personal, and you put all your experience into it. But you’re not going to be the only person who ever works on this thing.”
No matter where you work writing software, it’s rare that you have the luxury of designing a solution from the start. Much more often, you’re working with a code base that you’ve inherited: either a legacy code base within your own company or a client whose website has passed through the hands of multiple developers before you. Being on the receiving end of a train wreck of a code base gives you first hand exposure to the consequences of ignoring clean code best practices.
Dan recalls, “I worked at an agency for five years, and my first project that I worked on was one that had started from scratch at the company, but the original developer had left. He hadn’t been using the greatest practices and then the project passed through three more developers before it came to me. We started adding new feature requests to it, and we had to understand what this thing does, because now we have to add new features that work with what’s already there. And inevitably, we had to rewrite a lot of code, because maybe it was out of date, or didn’t work, or it never worked in the first place. Probably the first year that I was at that company, it was really just orienting myself in this massive code base.”
Dan spent the next half year refactoring as he added the new features that were needed. And as the code base was cleaned up, new features that had once seemed impossible to build suddenly seemed straightforward.
“I had to take a step back and really consider what had happened. The refactoring work we did made that feature now possible. It wasn’t that it was easy to do — it was only easy to do after all the work we put into it.”
Why Good Developers Write Bad Code
If we assume that developers mean well, why is there so much illegible code out there? Are there just that many…bad developers?
Not at all. The truth is there are a number of factors working against even the most experienced developer. You might be under a tight deadline or building out a proof of concept — that later becomes a permanent solution. And in those cases, writing the most elegant and well-organized code possible might take a back seat to just making it work and getting the project out the door.
Plus, the idea that you’ll clean up a project later is often wishful thinking.
“Even an experienced developer has said 1000 times that they will get back to it, but you never get back to it. Sometimes you do, but more often you won’t. So taking care of things on the front end is the important consideration. Once you have self awareness of the amount of time that’s available to work on a thing, or the fact that you’re not going to be always the one working on the thing, you can make better decisions around how you structure your code,” Dan says.
Cleaning Up Your Act
We can all agree that it’s better to write legible code the first time around than waiting for a future refactor that may never come. But what do we actually mean when we say legible code — and how do you get there? Next, Dan shares a few tips for writing code that will make your project’s future owner thank you.
1. Practice.
Dan recommends building clean coding skills through bite-sized trainings. “There are practices that you can do, little exercises called Code Katas, that present you a simple problem. It’s something you can do on a lunch break or in 15 minutes, and it gives you a chance to work through a very short, brief problem in a way that you can practice clean code development.”
Consider it like building a muscle. These practice sessions can help you cultivate the ability to distill your code to its simplest form, and when best practices become second nature, it’s easier to maintain them.
2. Extract, extract, extract.
One of the biggest wins for code readability is extracting code out into single-use functions. Dan gives an example:
“Let’s say you write a 20 line function that does four different things. You can extract those four different things into their own separate functions. And when you do that, you’ve accomplished two things: you’ve isolated the individual things into their own containers and you’ve simplified the structure.”
Multi-purpose functions will always carry more of a cognitive load because the developer trying to decipher the code has to work out that the code is doing four different things. When you extract that out into separate functions, it becomes much easier to read what each piece of code is doing.
“Maybe your first function stores a setting on the server. And then maybe the second function you extracted uses that setting to make an API call. And then the next function does some kind of reaction based on the results, and so on,” Dan continues. “Those four functions basically read like a book that tells you what I’m doing.” Dan recommends extracting code out until each function has a single responsibility — and that’s it.
Once you extract everything out into single purpose functions, you can make your project even more readable by pushing those single use functions down to the bottom of your file. “Put them in private functions so they’re not being exposed to the global namespace, and then what you have as your global function is very simple, very clean: a function that calls those four other functions. That makes it very clean and linear at the top of the file, where somebody’s actually going to be reading, especially if the private function names are very descriptive,” Dan says.
“If you see a file that’s written that way, then you can almost certainly assume somebody has used clean code practices to extract, extract, extract and leave behind the very deliberate, very specific, easy-to-read things at the top.”
3. Use descriptive names.
Naming functions and variables descriptively is a practice that goes a long way toward making your code readable to others. “When you name things well, it self documents what you’re doing. Nobody wants to write documentation — and nobody wants to read it either. But if you if you name the functions and the variables descriptively, that makes it self documenting, and then you can take that whole documentation step out.”
At one point in time, it was best practice to be as concise as possible with naming conventions to conserve memory. But that’s no longer the case with today’s resource-rich computing.
“Back in the old days, you would use function names that were like two letters, or variable names like i and j and k. Nowadays, the compiler is going to compile code down, and storage is essentially free. So there’s really no reason to be brief with your function names and your variables when you don’t have to.”
Dan also prefers the self-documenting approach to comments in the code. “You do need to document any kind of information that somebody who’s going to consume a framework needs to know. If there’s a default to a Boolean, you should document what that’s going to be. But as far as multi line comments that explain just exactly what the code does — I never see that anymore.”
4. Leverage your tools
And when all else fails — automate it. Set yourself up for success by leaning on tools that take the guesswork out of clean code.
Dan says, “I use Xcode, and there are actually tools inside of Xcode that let you refactor things. It can extract methods out of a bigger block of code, put that put it in a new function, and figure out the inputs that need to go into it.”
Consistency is another factor that contributes to readable code, and linters are a great tool for enforcing style and formatting conventions. But linters are really only helpful if their usage is established early in the project. “Linters are one of the things you want to put in right at the beginning, because it’s going to flag everything that you’re doing that’s not following the standards that you set for that project. If you’re six months into a project and you’ve written 20,000 lines of code, you add a linter at that point…well just set aside your next month, because you’re just going to be fixing compliance issues,” Dan says.
If you’re inheriting an older project, a linter won’t help you much at that point. But if you’re fortunate enough to be starting a project from day 1, pulling in a linter can do a lot to make your code readable to the next developer assigned to the project.
Coding with Empathy
What really interested me about my conversation with Dan is the idea that this is not just an academic pursuit, writing clean code for clean code’s sake. There’s a human element to making your code simple and readable. Software is written by teams — not by lone geniuses. Coding with empathy means recognizing that other developers will contribute to your code down the line and making your code approachable to others.
And when you do that, you can begin to create work that goes beyond the individual and begins serving others. Everyone likes recognition, and it’s satisfying to solve a problem in a particularly clever way. But at the end of the day, it’s not just about you — it’s about the quality of the project and the success of all of the other team members, past, present, and future, who also have a stake.
Dan sums it up:
“Don’t write your code to be clever. Don’t try to impress anybody. If you use empathy driven development, where you’re thinking about the next person working on a thing, you’re going to make decisions keeping them in mind. And what it will do is keep your ego in check, and take your ego out of the equation.”
Conclusion
Most developers don’t set out to write code that makes sense only to them, but the reality is that there are factors that work against those good intentions. You might be building out a proof of concept or an MVP that you mean to come back and refine later — but later never comes. Or, you might start out as a project’s sole owner, only to move on or expand your team.
Once you recognize that software has a life far beyond its original creator, you can start to employ clean code practices by:
- Training up with problem solving exercises
- Extracting functions into single-purpose blocks
- Naming functions and variables descriptively
- Using built-in tools and linters to automate consistency
…and you’ll produce code that doesn’t make life difficult for future developers working on your project. That’s not just coding with empathy, that’s good karma.
Thanks to Dan Murrell for the insights and the conversation that sparked this post. You can find Dan on Twitter @danimal99 or tweet us @BigCommerceDevs. Cheers! 🍻
Top comments (10)
❤️ I can't say enough great things about this post. You've put so much that I just feel into words. Getting to the point where your code is written to benefit your current users as well as your future self/team is pretty close to dev nirvana.
The trick, I think, is finding the balance of what's good enough / acceptable to put out into the world right now, and that is incredibly sensitive to the team, the complexity of the features being developed, and the realities of building on a budget and a timeline.
This is so good. I want to get this post tattooed on the inside of my eyelids.
Thank you! It feels good to know this resonated with you 😊
Absolutely agree with your post, Karen. I know this from my own experience at my first tech company. I started working there straight after college and wrote code for an entire project as most freshers do; not following any clean code principles.
Fast forward a year, I was struggling to add new features or solve bugs in the existing code because it was so messed up.
Now, I try to keep my code as clean as possible right from the beginning. Refactoring is not a good excuse because people hardly take the time or the effort to refactor later.
On a side note, I support this idea 100%:
While abstractions are good and keep your code modular but too much abstraction can actually do more harm than good for you and for anyone coming in later.
Awesome, thanks for sharing your experience!
Thank you for this nice and "on spot" article.
It made me realise I did a lot of katas and dojos, however, readibility was never such a thing. We were more interested by TDD and finding nice algorithms. However, readability was not such a focused objective.
I think I should go back to wearing the white-belt and practice more explicitely the readability of the code.
Actually adding a linter later on a project is not a bad idea. Most of those tools offer a
-fix
option that works really well. At least as a starting point.Nice--that's a good tip :)
Great post. When we only focus on making code work we give future developers (or our future selves) a debt of maintainability and understandability.
Thanks, well put!
This Dan sounds like a smart guy, must be the name 🤣🤣. Thanks for the write-up Karen, good read 👍👍