“Clean code reads like well-written prose.” - Grady Booch, from Clean Code**
The book “Clean Code” (Robert C Martin) came out in 2008, and since then, it has become a classic in software development, one of the first books recommended by employers and mentors. In it, Martin argues that it’s not enough for code to just work - for a code base to be maintainable and resilient, the code should be accessible and readable for future developers (or current developers who might end up not touching the code for so long they forget what they were thinking when they wrote it!). Unclean code can cost an organization hours of developer time in trying to decipher cryptic code, having extended onboarding for new developers who have trouble understanding the code base, or chasing down bugs that result from inconsistencies or misunderstandings within the codebase.
At its core, writing clean code is writing intuitively understandable code - and the most intuitively understandable code is code that is as close to written English prose as possible. Every moment your brain has to stop and figure something out - whether it’s what an abbreviation stands for, what a function is doing, or what exactly is happening in that ternary - is an avoidable increase in cognitive load. And higher cognitive loads result in developers having less energy to focus on problem-solving, as well as increase the likelihood mistakes are made and bugs are introduced.
The book “Clean Code” focuses on backend development; however, these principles can be applied to front-end coding, as well. Here are (number) ways to apply these principles to front-end engineering and start writing cleaner front-end code today.
Use descriptive naming. When deciding how to name variables and functions, use longer and more descriptive names - we want our code to be as similar to written prose as possible. Avoid abbreviations. Especially avoid abbreviations if the intuitive pronunciation of the abbreviation is different from what it’s short for. For example, I see “char” as a variable name used frequently. Our brain pronounces this “char”, whereas the first syllable of “character” is pronounced “care”, so every time a developer reads this, they have to stop and think about what this means. Additionally, longer names are usually more searchable (and easier to remember) if you have to search within a repo.
Some examples are below. Notice how, in the rightmost column, even without seeing any of the code, we have a much clearer idea of what the function does. If we’re working through a new code file, we don’t have to remember what the handleClick() in this file does (often if it’s used once in a repo, the same function name is used in other components, as well).
|
|
|
---|---|---|
|
|
|
|
|
|
|
---|
|
|
---|---|
|
|
|
|
Write prose-like conditionals. Breaking conditional statements into separate functions make conditionals much more intuitive to read. Many developers prefer ternaries to if/else statements; however, ternaries are less intuitive (especially to younger developers), and more prone to misinterpretations.
Nested ternaries are very difficult to decipher and should always be avoided. I know a few senior front-end developers who said they could read nested ternaries easily, but that came from years of practice. Writing readable code means writing for future readers, not for yourself, so if there’s a chance junior developers are going to be reading the code eventually, stick with code that doesn’t need years of experience to digest easily.
|
|
---|---|
|
|
|
|
|
|
**Use white space effectively.**This is a great tip for both writing code and writing in general. Adding blank lines between paragraphs of prose as well as between functions helps make things much less overwhelming to read. Additionally, using indentation easily and intuitively communicates relationships between functions to readers.
Have defined and easy-to-find coding conventions. Many large organizations have published their style guides on the internet, which is a great place to start. Decide the format for names (are all variables camelCase? Are all files with underscores (file_upload_component.js), etc), as well as any other style conventions. Consider using a linter like ESlint or prettier to help enforce these standards so that developers and code reviewers who are focused on other parts of the code don’t miss those small mistakes.
Use a CSS extender like Less or Sass to write cleaner CSS. One of the core aspects of complexity in software is amplification: when making a change in the code requires changes in multiple places (increasing the likelihood you miss a place and a bug is introduced). CSS variables allow you to, for instance, set brand colors to variables, so if your brand palette changes, you only need to change the hex code in one place, rather than going through multiple CSS files and changing the code in many places. These libraries include other features, like nesting and mixins, that help clean up and organize CSS.
Aim for DRY code. The DRY principle in programming is: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. The less verbose version is: Don’t Repeat Yourself (DRY). Don’t have your brand colors set in multiple places in your CSS; use a variable to set it once. Don’t have the same utility function in multiple files; create a central function and use it. Don’t do the same computations on your active user’s data in multiple components; do the necessary computations in one variable that’s in the application store and available to all components. The level of complexity increases for a codebase (and a task) the more places changes need to be made and it also makes errors more likely. If you miss one of the places the old color was, there will be some areas of your site still in the old color. If you miss updating one of the component’s methods to calculate data, your user will have the wrong data on one page. Reducing instances of duplicate code makes the code base simpler, more resilient, and easier to maintain.
Organize components and project files in an intuitive, non-overwhelming way. Getting to know a new repository can be a daunting task - especially if it’s large with many files. Being mindful about component and file organization can make a big difference in how challenging onboarding for a repo is. One project I worked on started out using the atomic component structure (atoms, molecules, and organisms for simple, medium, and complex components), but after 6 months, the project had grown so large that there were 30+ components in each of those folders. Many of those components were similar to each other, so it wasn’t obvious from looking at the page which component in the “molecules” file was the one they were looking at. Ultimately, we reorganized the app to have a shared component folder for simple, shared components, and then components were organized based on what page they belonged to (this was an SPA with only about 5 pages/views total). It was a lot easier to figure out which component you need out of 6 page-specific components than a folder of 30 covering a wide range. Of course, repos can grow somewhat unpredictably over time, so committing to an organized, intuitive file structure means being willing to commit to revisiting and potentially refactoring the app over time.
In a similar vein, knowing when and when not to break components into subcomponents is essential. If a piece of a component will be used elsewhere, that’s clearly an instance to have a subcomponent, as well as if there’s a clear difference in purpose (ie, a nav bar probably won’t be reused, but has a clear and separate purpose from the rest of an app’s header), or if a component is very long or complex. Additionally, if that piece has complex functionality that doesn’t overlap with other sections of the component (for instance, a map from an external library is used in a component, and involves several functions to initiate and render it correctly), separating that into its own component reduces cognitive load so developers are only engaging with that complexity when it’s necessary. However, subcomponents for the sake of subcomponents adds unnecessary complexity and creates a more overwhelming and confusing repository. As in the above example, it’s not a good use of a developer’s time to sort through a folder with a bunch of subcomponents
Writing clean code doesn’t require more work; it just requires a paradigm shift: write code for your future readers who might not know as much as you, and make it as descriptive and close to written prose as possible. A repo written with clean code is more resilient and maintainable over time. It minimizes potential future bugs, it makes bug solving easier, it supports junior and new developers by reducing cognitive load, and overall it makes for a smoother development experience.
Further Resources:
Clean Code by Robert C Martin
Photo byJantine Doornbos on Unsplash