Writing Clean Front End Code

Written by danielleford04 | Published 2023/03/31
Tech Story Tags: programming | front-end-development | frontend | frontend-development | front-end | website-frontend | frontend-principles | clean-code

TLDRThe book “Clean Code” (Robert C Martin) came out in 2008. 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. The principles can be applied to front end coding, as well.via the TL;DR App

“Clean code reads like well-written prose.” - Grady Booch, from Clean Code**

What is 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.

How to write clean front-end code

  1. 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).

let char = ‘a’

let character = ‘a

let selectedCharacter = ‘a’

handleClick(){}


onLogoutClick(){}

onSubmit(data)


onNewCustomerSubmit(customer_data)

  1. Use short, named functions. Look for opportunities to break code out into smaller functions. That way, you reduce long, complex functions and instead have parent functions that are very easy to read through because they have descriptively named functions, and the complexity is broken into smaller functions, which makes it a lot easier to understand what is going on.

function createCustomer(data) {
data.creationDate = getFormattedCurrentDate()
data.membershipCost = calculateProRatedMembershipCost()

let validatedData = validateFormData(data)
submitForm(validatedData)}

function calculateProRatedMembershipCost() {...}
function getFormattedCurrentDate() {...}
function validateFormData(data) {...}
function submitForm(data) {...}

  1. Keep parameters to a minimum, and write self-documenting function names. If you have more than two or three parameters, pass in an object with the parameters instead. You can also use destructuring to enforce correctly named parameters within that object. If you do have parameters, include them in a function name (in order), so that users don’t have to go check the source code or risk accidentally putting the parameters in the wrong order.

getInstructions(user_id, group_id)

getInstructionsByUserIdAndGroupId(user_id, group_id)

getInstructions(user_id, group_id, date, user_role)

getInstructions({user_id: user_id, group_id: group_id, date: date, user_role: user_role})

getInstructions({ user_id, group_id })

const active_user = { user_id: ‘john123,’ group_id: ‘234’, first_name: ‘John’, age: 23
getInstructions(active_user)

  1. 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.

  2. 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.

if (user !== null && user_role === ‘admin’ && user.active === true) {....}

if (isUserValidated) {...}

function isUserValidated(user) {
user !== null && user_role === ‘admin’ && user.active === true
}

{selectedObject === this.id ? ‘selected’ : ‘hidden’}

{isSelected ? ‘selected’ : ‘hidden’ }

{this.props.attendees.length ? this.renderTable() : this.renderNoAttendeesMessage() }

if (areThereAttendees) {
this.renderAttendeesTable } else {this.renderNoAttendeesMessage}

const className = (isSelected && isAvailable) ? ‘highlighted’ : (isAvailable) ? ‘default’ : ‘hidden’;

let className = ‘hidden’;
if (isSelected && isAvailable) {
className= ‘highlighted’
} else if (isAvailable) {
className=’default’ }

  1. Use comments judiciously. Theoretically, descriptive code should be clear enough that it doesn’t need comments. Feeling like you need to write a comment can be a sign to take a second look and try to write the code in a clearer way. However, there are some use cases where comments are necessary and can’t be described through the code: if you are explaining the intent behind the code (maybe you decided between two strategies and it would be helpful for future developers to know why one was chosen), warning about specific consequences (sometimes we are coding to avoid consequences that are too specific to include in function names), clarifying confusing code you have no control over (ie, RegEx functions always look overwhelming, but there’s nothing we as users of RegEx can do to change that).

  1. **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.

  2. 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.

  3. 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.

  4. 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.

  5. 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

The DRY Principle



Photo byJantine Doornbos on Unsplash


Written by danielleford04 | Front end engineer. Meditator. Soccer fan.
Published by HackerNoon on 2023/03/31