DEV Community

Marco
Marco

Posted on • Originally published at blog.disane.dev

Best practices in programming: Clean code for you and your team ๐Ÿš€

Discover coding best practices! Learn how to write readable, maintainable and clean code that is not only understandable for you, but also for your team. ๐Ÿš€


In software development, it is crucial to write code that not only works, but is also well-structured, readable and maintainable. This applies not only to team collaboration, but also in the event that you come back to your own code months later. In this article, I will introduce you to the best practices and principles you should follow when programming.

Using JavaScript examples, I'll show you how to turn bad code into readable code and the benefits of functional programming.

I also go into the most important soft skills that are essential for a developer. Programming is a craft and just as much love should be put into the code โค๏ธ

There is a very good book on this topic by Robert C. Martin:

Preview image

Why good code is important ๐Ÿค”

Readability

Readable code allows you and other developers to quickly understand and use the code. When you look at your own code again after a few months, you don't want to have to think for hours about what this or that part of the code does. Readable code saves time and nerves. Readable code also helps to find and fix bugs faster, as the logic and structure are clearly visible.

Maintainability

Maintainable code is crucial for fixing bugs and adding new features. Unstructured and unclear code makes maintenance more difficult and increases the likelihood of bugs being introduced when new features are added. Maintainable code should be modular so that changes in one module do not have unforeseen effects on other parts of the code.

Confused Cbs GIF by Wolf Entertainment

Collaboration

In most projects, you are not working alone. Clear and well-structured code facilitates team collaboration. Other developers can read, understand and further develop your code. A uniform code base with consistent naming conventions and formatting makes it easier to familiarize new team members and promotes a productive working environment.

Reusability

Well-written code is often reusable. By applying principles such as DRY (Don't Repeat Yourself) and KISS (Keep It Simple, Stupid), you can ensure that your code blocks can be reused in different parts of your project or even in other projects.

Code for less experienced developers

A good developer writes code in such a way that it is also understandable for developers with a lower level of knowledge. This means that the code is well documented, clearly structured and free of unnecessary complexity.

Principles of good code ๐Ÿ› ๏ธ

Clarity before cleverness

It is tempting to write clever and complicated code that looks impressive at first glance. But clarity should always take precedence. Simple and clear code is often the better way to go. Code that is easy to understand makes debugging and further development easier.

// Clever, but possibly difficult to understand
const isValid = (str) => !/[^\w]/.test(str);

// Clear and understandable
const isValid = (str) => {
  const regex = /[^\w]/;
  return !regex.test(str);
};

Enter fullscreen mode Exit fullscreen mode

Always put yourself in the shoes of another programmer and ask yourself three questions:

  • Would a beginner understand my code?
  • Will I still understand my code in 6 months?
  • Can I train someone in the code without having to train them?

If you can answer one of these questions with no, then your code is probably too complicated.

Coffee Wow GIF by Starbucks

Consistency

A consistent style makes the code easier to read. Use consistent naming conventions, indentation and formatting. This makes it easier to understand and maintain the code. Tools such as ESLint or Prettier can help to format the code automatically and keep it consistent.

// Inconsistent
const userName = "John";
const UserAge = 30;

// Consistent
const userName = "John";
const userAge = 30;

Enter fullscreen mode Exit fullscreen mode

Work Demanding GIF by HannahWitton

DRY (Don't Repeat Yourself) ๐Ÿ”‚

Avoid redundancies in code. Repeated code should be outsourced to functions or classes. This reduces errors and makes changes easier. If there is a change in the code, it only needs to be made in one place.

// Redundant code
function getUserName(user) {
  return user.firstName + ' ' + user.lastName;
}

function getUserAddress(user) {
  return user.street + ', ' + user.city;
}

// DRY principle applied
function getFullName(user) {
  return `${user.firstName} ${user.lastName}`;
}

function getAddress(user) {
  return `${user.street}, ${user.city}`;
}

Enter fullscreen mode Exit fullscreen mode

Say It Again Jason Sudeikis GIF by Saturday Night Live

KISS (Keep It Simple, Stupid) ๐Ÿคฏ

Keep your code as simple as possible. Complexity increases the susceptibility to errors and makes it more difficult to understand. Simple code is often more robust and efficient code. Use simple and clear logic instead of complex and nested structures.

// Complex and difficult to understand
function getDiscount(price, isMember) {
  return isMember ? (price > 100 ? price * 0.9 : price * 0.95) : price;
}

// Simple and clear
function getDiscount(price, isMember) {
  if (isMember) {
    if (price > 100) {
      return price * 0.9;
    } else {
      return price * 0.95;
    }
  }
  return price;
}

Enter fullscreen mode Exit fullscreen mode

TV gif. Mario Lopez as Slater on Saved by the Bell runs down his high school steps with football in hand, points at us, and says,

YAGNI (You Aren't Gonna Need It) โŒ

Implement only the functionalities that you currently need. Superfluous features increase complexity and maintenance costs. This principle helps to keep the code lean and focused.

// Overengineering
function calculatePrice(price, tax, discount, isMember) {
  let finalPrice = price + (price * tax);
  if (isMember) {
    finalPrice -= discount;
  }
  return finalPrice;
}

// YAGNI principle applied
function calculatePrice(price, tax) {
  return price + (price * tax);
}

Enter fullscreen mode Exit fullscreen mode

Season 7 No GIF by Chicago Fire

Code is like a good book ๐Ÿ“š

The correct naming of variables, functions and classes is essential for the readability and comprehensibility of the code, similar to a well-written book. Clear and precise names reflect the intention of the code block and facilitate maintenance and team collaboration. For example, a function that calculates the square of a number should be called calculateSquare, not doSomething.

A class for calculating squares could be called SquareCalculator. Well-named and structured code saves time and nerves and makes working in a team more pleasant and productive. By writing your code in such a way that it is understandable even for less experienced developers, you avoid technical debt and improve the quality of your project in the long term.

Names often follow the rules of language, similar to adjectives, nouns and verbs, which makes them more intuitive and easier to understand.

Unimpressed Sea GIF by SpongeBob SquarePants

Adjectives

Adjectives describe properties or states. In programming, they are often used to name variables that store certain states or properties.

let isActive = true;
let userName = "JohnDoe";
Enter fullscreen mode Exit fullscreen mode

In this example, isActive describes the state of an object (whether it is active or not) and userName stores a property of a user.

Verbs

Verbs describe actions or processes. In programming, verbs are often used to name functions that perform certain actions.

function calculateArea(width, height) {
  return width * height;
}

function fetchData(url) {
  // Retrieve data from a URL
}

Enter fullscreen mode Exit fullscreen mode

Here, calculateArea and fetchData describe the actions that the respective functions perform (calculating an area and retrieving data respectively).

Nouns

Nouns denote people, places or things. In programming, nouns are often used to name classes and objects that represent certain entities or concepts.

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

class Product {
  constructor(id, price) {
    this.id = id;
    this.price = price;
  }
}

Enter fullscreen mode Exit fullscreen mode

In this example, User and Product denote concrete entities in a system, similar to nouns in language.

Adverbs

Adverbs modify verbs and describe how an action is performed. In programming, adverbs can help to specify function names and clarify how an action is performed.

function calculateAreaQuickly(width, height) {
  // Quickly calculate the area
  return width * height;
}

function fetchDataSafely(url) {
  // Fetch data safely from a URL
}

Enter fullscreen mode Exit fullscreen mode

Here, quickly and safely modify the actions and provide additional information about how the actions should be performed.

Bad vs. good code in JavaScript ๐Ÿ’ป

Example of bad code

function processData(input) {
  let output = [];
  for (let i = 0; i < input.length; i++) {
    let processedData = input[i] * 2;
    output.push(processedData);
  }
  console.log(output);
  return output;
}

Enter fullscreen mode Exit fullscreen mode

Example of good code

function processData(input) {
  return input.map(item => item * 2);
}

const input = [1, 2, 3, 4, 5];
const output = processData(input);
console.log(output); // [2, 4, 6, 8, 10]

Enter fullscreen mode Exit fullscreen mode

In this example, the code has been simplified by using Array.prototype.map, which increases readability and maintainability.

Technical debt ๐Ÿฆ

Technical debt occurs when short-term solutions or compromises are made in programming to achieve quick results instead of implementing long-term, sustainable solutions. This can be caused by time pressure, lack of resources or lack of planning. Like financial debt, technical debt must also be "paid back", which takes the form of additional work for maintenance and refactoring. Technical debt can affect code quality, slow down development and make it difficult to introduce new features. Therefore, it is important to make conscious decisions and, where possible, favor long-term and clean solutions to avoid accumulating technical debt.

season 7 episode 10 GIF

Hardcoded values

Bad solution

function calculateTotalPrice(quantity) {
  return quantity * 9.99;
}

Enter fullscreen mode Exit fullscreen mode

In this example, the price of the product is hardcoded in the code. This is a quick solution, but it leads to technical debt because any change to the price requires a change in the code.

Sustainable solution

const PRODUCT_PRICE = 9.99;

function calculateTotalPrice(quantity) {
  return quantity * PRODUCT_PRICE;
}

Enter fullscreen mode Exit fullscreen mode

By using a constant for the product price, the code becomes more flexible and easier to maintain. Changes to the price only need to be made in one place, which reduces technical debt.

Duplicated code

Technical debt

function calculateRectangleArea(width, height) {
  return width * height;
}

function calculateTriangleArea(base, height) {
  return (base * height) / 2;
}

Enter fullscreen mode Exit fullscreen mode

There is duplicated code here that contains the calculation of the area. This repetition leads to technical debt, as changes to the logic have to be made in several places.

function calculateArea(shape, ...dimensions) {
  switch (shape) {
    case 'rectangle':
      return dimensions[0] * dimensions[1];
    case 'triangle':
      return (dimensions[0] * dimensions[1]) / 2;
    default:
      throw new Error('Unknown shape');
  }
}

Enter fullscreen mode Exit fullscreen mode

By merging the calculation logic into a single function, the code becomes DRY (Don't Repeat Yourself). This reduces technical debt and makes the code easier to maintain and extend.

Disadvantages of kilometer-long one-liners ๐Ÿšซ

Kilometer-long one-liners can be difficult to read and debug. They tend to become complex and opaque, making maintenance difficult.

Season 1 Bug GIF by Nanalan'

Example of a bad one-liner

const result = array.map(x => x * 2).filter(x => x > 10).reduce((acc, x) => acc + x, 0);

Enter fullscreen mode Exit fullscreen mode

Split-and-readable-code

const doubled = array.map(x => x * 2);
const filtered = doubled.filter(x => x > 10);
const result = filtered.reduce((acc, x) => acc + x, 0);

Enter fullscreen mode Exit fullscreen mode

By splitting the code into multiple lines, it becomes much more readable and easier to debug.

Functional programming ๐Ÿง‘โ€๐Ÿ’ป

Functional programming can help to write clean and efficient code. It encourages the use of immutable data and pure functions.

Functional programming in JavaScript

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(num => num * 2);
const even = doubled.filter(num => num % 2 === 0);

console.log(even); // [4, 8]

Enter fullscreen mode Exit fullscreen mode

In this example, the original data is not changed and the operations are clear and comprehensible.

Advantages of functional programming

Invariability

Invariability means that data is no longer changed after it has been created. Instead, new data structures are created. This reduces errors and makes the code more predictable.

Pure functions

Pure functions are functions that have no side effects and always return the same result for the same input. This makes the code easier to test and debug. The expected mode of operation is deterministic.

Negative example โŒ

Here the value of the global variable counter is changed, which does not make the function pure, as the state outside the function is affected and the result differs for the same calls.

let counter = 0;

function incrementCounter() {
    // This function increases the value of counter and returns the new value
    counter++;
    return counter;
}

// Testing the function with side effect
console.log(incrementCounter()); // Output: 1
console.log(incrementCounter()); // Output: 2 (not the same result with the same calls)

Enter fullscreen mode Exit fullscreen mode

Positive example โœ…

function add(a, b) {
    // This function returns the sum of a and b
    return a + b;
}

// Testing the pure function
console.log(add(2, 3)); // Output: 5
console.log(add(2, 3)); // Output: 5 (always the same result)

Enter fullscreen mode Exit fullscreen mode

Higher-order functions

Higher-order functions are functions that take other functions as arguments or return functions. This enables flexible and reusable code blocks.

const add = (a) => (b) => a + b;

const add5 = add(5);
console.log(add5(10)); // 15

Enter fullscreen mode Exit fullscreen mode

Soft skills for developers ๐Ÿค

Communication

Good communication is essential. You need to be able to convey your ideas and solutions clearly and precisely. This applies to working with other developers as well as communicating with non-technical people.

Chicago Pd Nbc GIF by One Chicago

Teamwork

The ability to work effectively as part of a team is crucial. This includes sharing knowledge, supporting colleagues and solving problems together. Teamwork also encourages development and learning within the team.

TV gif. Four casually dressed coworkers jump in the air and high-five each other as a group, clearly elated about a success. Rainbow colored sparkling text reads

Problem solving

Problem solving skills are central to the work of a developer. You need to be able to analyze complex problems, break them down and find effective solutions. A structured approach to problem solving helps you work more efficiently.

The Daily Show Lol GIF by The Daily Show with Trevor Noah

Adaptability

Technology is constantly evolving. As a developer, you must be willing to learn new technologies and methods and adapt to change. Adaptability allows you to respond flexibly to new challenges.

Agility Scrum GIF by Parade of Homes IG

Code-Reviews and pair programming ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘

Code reviews

Code reviews are an important part of software development. They make it possible to detect errors at an early stage and improve the code. Regular code reviews increase code quality and knowledge is shared within the team.

Pair programming

Pair programming is a technique in which two developers work together on one computer. One writes the code while the other reviews it. This method promotes knowledge sharing and collaboration within the team.

Test-Driven Development (TDD) ๐Ÿงช

Advantages of TDD

Test-Driven Development (TDD) is a method in which tests are written before the actual code is developed. This helps to define clear requirements and ensure that the code delivers the expected results.

Example of TDD in JavaScript

//write test
const assert = require('assert');

function add(a, b) {
  return a + b;
}

assert.strictEqual(add(1, 2), 3);
assert.strictEqual(add(-1, 1), 0);

// Implement code
function add(a, b) {
  return a + b;
}

Enter fullscreen mode Exit fullscreen mode

TDD leads to more robust and less error-prone code, as every functionality is covered by tests.

More-helpful-principles-and-techniques

Modularity

Modularity means dividing the code into small, independent modules. Each module should have a clearly defined task. This makes it easier to test, maintain and reuse code.

// Unmodular code
function processData(data) {
  // Data validation
  if (!Array.isArray(data)) {
    throw new Error('Invalid data');
  }

  // Data processing
  const processed = data.map(item => item * 2);

  // Data output
  console.log(processed);
  return processed;
}

// Modular code
function validateData(data) {
  if (!Array.isArray(data)) {
    throw new Error('Invalid data');
  }
}

function processData(data) {
  return data.map(item => item * 2);
}

function logData(data) {
  console.log(data);
}

const inputData = [1, 2, 3, 4, 5];
validateData(inputData);
const processedData = processData(inputData);
logData(processedData);

Enter fullscreen mode Exit fullscreen mode

Automated testing

Automated testing is essential to ensure that the code works correctly and that new changes do not break existing functionalities. Unit tests, integration tests and end-to-end tests are common types of tests.

const { expect } = require('chai');

// Function for testing
function add(a, b) {
  return a + b;
}

// unit test
describe('add', () => {
  it('should return the sum of two numbers', () => {
    expect(add(1, 2)).to.equal(3);
    expect(add(-1, 1)).to.equal(0);
  });
});

Enter fullscreen mode Exit fullscreen mode

Documentation

Good documentation is crucial to help other developers (and your future self) understand the code. This includes both comments in the code (e.g. for JavaScript JSDoc) and external documentation such as README files.

/**
 * Adds two numbers.
 *
 * @param {number} a - The first number.
 * @param {number} b - The second number.
 * @return {number} The sum of the two numbers.
 */
function add(a, b) {
  return a + b;
}

Enter fullscreen mode Exit fullscreen mode

Documentation - JSDoc ReferenceWhat JSDoc does TypeScript-powered JavaScript support?

Conclusion ๐ŸŽ‰

Good code is clear, readable and maintainable. It enables efficient collaboration and facilitates the maintenance and further development of projects.

You can significantly improve the quality of your code by applying best practices and principles such as clarity over cleverness, consistency and DRY. Functional programming and the development of important soft skills will also help you become a successful developer.

Remember that you are not only writing code for yourself, but also for other developers and for your future self.

Tim And Eric Omg GIF


If you like my posts, it would be nice if you follow my Blog for more tech stuff.

Top comments (1)

Collapse
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ

Readability, maintainability, and clearness are entirely subjective. Being too dogmatic about 'clean code' can, over time, have a detrimental effect on the quality of developers.