Skip to main content

Writing Quality Code

11-16-15 Divya Sasidharan

Divya explores how we can ensure that our code lives up to best practices—even with all those pesky project constraints.

Like most specialized skills, there is neither a metric nor a clearly defined and standardized checklist we can use to measure how good our code actually is. Good code is DRY (Don’t Repeat Yourself), test-driven, behavior-driven, maintainable, and consistent among many other things. These principles are a great standard to hold our code against. However, reality often presents us with less-than-ideal situations that can influence how we write and consequently judge our code. Some factors that influence this include project timelines, budget, team size/structure, and average experience of your development team. With these constraints, how can we ensure that our code lives up to these (somewhat arbitrary) standards?

Why does good code even matter?

The concern we have for writing good code is an important one. Chances are high that we will be tasked with maintaining and progressively adding new features to the codebases, post-project completion. Completely disregarding the task of keeping code in check indubitably leads to issues down the line. We risk breaking things when trying to integrate a new feature, or worse, we may struggle to understand what we wrote in the first place. Even if we’re lucky enough to escape the muck of maintenance work, we merely pass on the grief to the next developer. All this comes at a huge cost to developer productivity and more importantly to the overall cost of the project. The last thing we want as developers is to create a monolithic codebase that is a nightmare to maintain, let alone refactor.

How do we start writing good code?

XKCD Comic about

In the seminal book on writing, The Elements of Style, usage and composition are brought up as the most foundational principles of good writing. Though the two fail to define good writing in and of themselves, they provide a solid foundation to build upon. Similar to the “intro, body, conclusion” mantra that is widely prescribed in essay writing class, usage and composition form the guard rails of good prose. With the more formal aspects of writing in place, there is room for a writer to be creative and inject his or her own style. Parallels can be drawn between the principles of writing prose and writing software. By providing structure to our code and by following certain strict rules and guidelines, we can strive for better quality code as we do in our prose.

Style Guides

When we think of style guides, most of us think of long form, well thought out, and researched documentation that covers general code architecture and patterns, as well as the nitty gritty details of how syntax should be formalized. While these style guides are great to have for our individual projects and teams, making sure our code follows a style guide doesn’t have to be so complicated. After all, the purpose of a style guide is to set a standard to ensure uniformity in your code. Start small. Sit with your team and draft up the minimum requirements that would standardize code across the board, and make sure you review each other’s code before merging or deploying. A simple stylistic rule we use in projects at Sparkbox is making sure there is consistent spacing in function declarations.

Before:

function foo(bar){
 //do something
}

After:

function foo(bar) {
 //do something
}

With one simple rule, we ensure consistency across the codebase and significantly improve code readability. To make our jobs even simpler, we can automate this process by integrating code quality tools such as JSHint into the build process. The build process will automatically throw an error when you fail to follow an agreed-upon style. This keeps your code in check as you craft it and relieves the person reviewing code from the task of checking syntax. For ideas on starting your own style guide, check out AirBnB’s style guide, Idiomatic.js, and jQuery Core style guide. By following a pattern and making sure you stick to those patterns by doing code reviews before code is merged or deployed, you are well on your way to writing better quality code.

Naming Conventions

“There are only 2 hard things in CS: cache invalidation and naming things”

To paraphrase a past foundry post that became wildly popular, naming things is hard. We risk making premature assumptions about the code when we do, and we can’t accurately name things since we don’t always know how our code will scale over time. Defining a universal system for naming functions and classes is a mean feat. There is no hard and fast formula for naming things. However, key things to keep in mind when naming are readability and how you foresee the function being used in the future. It goes without saying that the main goal of naming a function is to succinctly indicate that function’s purpose. If you find yourself struggling with naming a function, then the concept you’re trying to wrap is not clear enough.

A while back, I was working on a feature request from a client and found myself in a naming conundrum. The client wanted a modal overlay on small screens that past a certain breakpoint would disappear and move the modal content into the page flow. The client also wanted us to consider the possibility of having more than one modal on a page, like having a modal within a modal. They called this overarching story `multipleModal`. This sparked 2 weeks worth of discussions and multiple white board sessions where we tried to grasp all the specifications and craft a concise solution. All things about how to effectively run meetings and demanding more precise specifications from clients aside, poor naming caused significant misunderstandings of what the client really wanted that in effect caused a huge delay in development. Eventually, we split the problem up into modal behavior and content management with the corresponding function names: `moveModalContent` and `toggleModal`. This was clearer in terms of the primary specification where modal content was moving from the modal to the page, and we were able to crank out code (with tests) within a day or two.

Naming functions also comes with the added benefit of helping with debugging. Kangax has a well explained example of this in his blog post, Named Function Expressions Demystified. He explains that using anonymous functions in your code can lead to confusion when debugging an error.

Take for instance the function below.

setTimeout(function(){
 throw new Error(‘Some Error’)
}, 0)

When run, the call stack trace returns: “Error: Unnamed unreferenced function at null.js:2:9″

If we were to name the function like so:

setTimeout(function handleTimeout(){
 throw new Error(‘Some Error’)
}, 0)

the call stack is slightly more descriptive: “Error: Unnamed unreferenced function at handleTimeout (null.js:2:9)”

Naming functions eases the job of debugging code since the call stack is more readable and descriptive.

Testing

“Good Fences Make Good Neighbors; Good Tests Make Good Code”

The most common reason for not writing tests is that there just isn’t enough time or room in the budget to do so. While this is a valid concern, tests are beneficial in the long run. They are good measures to ensure we write code systematically and keeps us aware of functions that are doing too much. Tests also serve as self-updating documentation. In addition to alerting us of code that needs to be refactored as the codebase scales, they help make code more readable and in my opinion should be baked into the budget and project timeline at the get go. However, if you find yourself having to turn around code in a short period of time, regression tests are still valuable in checking and validating assumptions.

With testing frameworks like QUnit, Jasmine, and Mocha, tests can easily be integrated into your existing workflow. They come prepackaged and work seamlessly with existing build systems like Gulp, Grunt, Express, and npm. Furthermore, you can easily hook up your tests to a headless browser such as phantomJS to simulate user interactions. If you want to be extra diligent about making sure your code is tested, you can additionally use tools like Coveralls and Code Climate to keep a check on how much of your code is actually tested. But be aware that Coveralls are not the holy grail of well-tested code; a high GPA does not make for a well-tested code base since the checks are only on whether the function was called—indirectly through another function for instance—and not whether those calls are explicit.

Documentation

There is a popular adage in computer science that claims that good code is its own best documentation. Regardless, explaining your code distills your thoughts. Similar to the practice of rubber-ducking code, where you attempt to explain your code line by line to yourself or to an inanimate object (such as a rubber duck), writing documentation forces you to evaluate your own thought process in a formalized manner. Forcing yourself to walk through your own design decisions improves the design of your code. Creating a README for your codebase is a great way to explain the overarching purpose and structure of your codebase. This has become such an essential part of a codebase that GitHub recommends it each time you create a repository, and build tools like Grunt create an empty README for you upon initialization. JSDoc is another great tool to use for documenting individual functions, and it provides a very specific guideline and syntax for documenting code. There are plugins for your favorite text editor that you can install that make using JSDocs incredibly simple.

Conclusion

Our ability to perceive good quality code ultimately boils down to experience. As we write, read, and review code, we grow our own personal senses of what good code looks and smells like. Writing more readable and well-documented code sets us on the right track. It allows us to experiment with various code concepts, paradigms, and strategies while making sure that our code delivers and can withstand the test of time. Moreover, instead of writing code for the sake of writing code and getting the job done as quickly as possible, it makes us self reflective as programmers and motivates us to evaluate code as we write it.

Related Content

User-Centered Thinking: 7 Things to Consider and a Free Guide

Want the benefits of UX but not sure where to start? Grab our guide to evaluate your needs, earn buy-in, and get hiring tips.

More Details

See Everything In

Want to talk about how we can work together?

Katie can help

A portrait of Vice President of Business Development, Katie Jennings.

Katie Jennings

Vice President of Business Development