Best Practices for Writing Clean and Maintainable Code

(Because Nobody Has Time for a Code Spaghetti Party)

Ark Mahata
Tech Padawan Chronicles
7 min readMay 16, 2023

--

Let’s face it, nobody has time for a code spaghetti party. You know the one — where you’re elbow deep in a tangled mess of poorly written, convoluted code that’s impossible to decipher. It’s like a bad dream that never ends.

As software developers, it’s our responsibility to write clean and maintainable code that doesn’t leave our fellow coders cursing our names. In this article, we’ll explore some best practices for writing code that’s not only efficient and effective but also easy to read and modify.

  1. Keep it simple: One of the most important principles of writing clean and maintainable code is to keep it simple. Avoid adding unnecessary complexity or clever tricks that may be difficult to understand or modify later on. Instead, focus on writing code that is clear, concise, and easy to read. This will make your code easier to maintain and update as your project evolves.
// Avoid overly complex code like this:
if ((x > 5 && y < 10) || (z == 7 && x <= 2)) {
// do something
}

// Instead, simplify the logic:
boolean isInRange = (x > 5 && y < 10);
boolean meetsCondition = (z == 7 && x <= 2);
if (isInRange || meetsCondition) {
// do something
}

2. Use descriptive names: Naming is crucial when it comes to writing clean code. Use descriptive and meaningful names for your variables, functions, and classes. This makes it easier for other developers to understand what your code is doing and reduces the chances of bugs or errors.

// Avoid unclear or abbreviated names like this:
public void init() {
// do some initialization here
}

// Instead, use descriptive names:
public void initializeDatabaseConnection() {
// initialize the database connection here
}

3. Write modular code: Break your code into small, modular functions that perform a single task. This makes it easier to read and understand, as well as facilitating testing and maintenance. Avoid writing long, monolithic functions that are difficult to comprehend.

double calculateShippingWeight(String[] items) {
// calculate the total weight of the items
}

double calculateShippingDistance(String origin, String destination) {
// calculate the distance between the origin and destination
}

double calculateShippingCost(double weight, double distance) {
// calculate the shipping cost based on the weight and distance
}

String[] items = {"item1", "item2", "item3"};
String origin = "Delhi";
String destination = "Kolkata";

double weight = calculateShippingWeight(items);
double distance = calculateShippingDistance(origin, destination);
double cost = calculateShippingCost(weight, distance);

4. DRY (Don’t Repeat Yourself): Repeating code is a common source of bugs and can make your code hard to maintain. Instead, strive to follow the DRY principle and write code that is reusable. This saves you time and effort in the long run and helps you avoid bugs and errors.

double calculateTax(double price) {
return price * 0.1;
}

double price1 = 50.00;
double price2 = 100.00;

double totalPrice1 = price1 + calculateTax(price1);
double totalPrice2 = price2 + calculateTax(price2);

5. Test your code: Writing tests is crucial when it comes to writing clean, maintainable code. Tests help you catch bugs early and ensure that your code is working as expected. They also make it easier to refactor your code and make changes without introducing new bugs.

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

@Test
void testAdd() {
assertEquals(5.0, add(2.0, 3.0));
assertEquals(0.0, add(-2.0, 2.0));
assertEquals(0.0, add(0.0, 0.0));
}

6. Use version control: Version control is a crucial tool for software development, helping you track changes to your code over time. Using a version control system like Git helps you keep track of your code changes and collaborate effectively with other developers. For example, if you make a change to your code and commit it to Git, you can easily revert back to a previous version if something goes wrong.

7. Keep your code organized: Keep your code organized by following a consistent directory structure and naming conventions. This makes it easier to find files and functions, especially as your codebase grows. For example, you could organize your codebase into separate directories for each feature or module and name your files using a consistent naming convention.

project/
├── src/
│ ├── feature1/
│ │ ├── Component1.java
│ │ ├── Component2.java
│ ├── feature2/
│ │ ├── Component1.java
│ │ ├── Component2.java

8. Use comments wisely: Use comments to explain why you wrote certain code or to provide additional context. However, avoid commenting code that is self-explanatory or that could be improved through better naming or abstraction. For example, instead of commenting on the purpose of a variable, you could use a more descriptive variable name.

// avoid:
int x = 5; // the number of items

// prefer:
int numItems = 5;

9. Use Consistent Formatting: Using consistent formatting throughout your code can make it easier to read and understand. This includes things like indentation, spacing, and the use of brackets and parentheses. Using a consistent style will make your code look more professional and can make it easier for other developers to work with your code.

// Avoid inconsistent formatting like this:
public void badFormatting() {
if(condition){
doSomething();}
else {
doSomethingElse();
}
}

// Instead, use consistent formatting:
public void goodFormatting() {
if (condition) {
doSomething();
} else {
doSomethingElse();
}
}

10. Consistency: Maintaining consistency throughout your codebase helps make your code more readable and easier to maintain. This includes naming conventions, formatting, and code style.

// Bad code example with inconsistent naming conventions and formatting
public class UserLogin {
private String username;
private String password;

public UserLogin(String UserName, String passWord) {
this.username = UserName;
this.password = passWord;
}

public String getUserName() {
return this.username;
}

public void setUserName(String UserName) {
this.username = UserName;
}

public String getPassword() {
return this.password;
}

public void setPassword(String passWord) {
this.password = passWord;
}
}

// Good code example with consistent naming conventions and formatting
public class User {
private String username;
private String password;

public User(String username, String password) {
this.username = username;
this.password = password;
}

public String getUsername() {
return this.username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return this.password;
}

public void setPassword(String password) {
this.password = password;
}
}

// In the bad code example, we have inconsistent naming conventions and
// formatting. The variable names are capitalized inconsistently,
// and there are unnecessary spaces in the constructor parameters.
// This makes the code more difficult to read and maintain.

// In the good code example, we have consistent naming conventions and
// formatting. The variable names are all lowercase, and there are no
// unnecessary spaces in the constructor parameters.
// This makes the code easier to read and maintain.

11. Refactoring: Regularly refactoring your code helps keep it clean and maintainable. Refactoring involves restructuring your code to make it more efficient, modular, and easier to understand.

// Bad code example with duplication and unnecessary complexity
public class Employee {
private String firstName;
private String lastName;
private int age;

public Employee(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

public String getFullName() {
return this.firstName + " " + this.lastName;
}

public void printDetails() {
System.out.println("Name: " + this.firstName + " " + this.lastName);
System.out.println("Age: " + this.age);
}
}

// Good code example with reduced duplication and increased modularity
public class Employee {
private String firstName;
private String lastName;
private int age;

public Employee(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

public String getFullName() {
return this.firstName + " " + this.lastName;
}

public int getAge() {
return this.age;
}
}

public class EmployeeDetailsPrinter {
public void printDetails(Employee employee) {
System.out.println("Name: " + employee.getFullName());
System.out.println("Age: " + employee.getAge());
}
}

// We start with a class that has duplication and unnecessary complexity.
// We refactor it to reduce duplication and increase modularity.
// We extract the printing functionality into a separate class
// to make it more reusable and easier to understand.

12. Performance: Writing performant code helps ensure that your applications run efficiently and smoothly. This can include optimizing algorithms, reducing memory usage, and minimizing network requests.

// Bad code example with inefficient algorithm
public class Fibonacci {
public int calculate(int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return calculate(n-1) + calculate(n-2);
}
}
}

// Good code example with optimized algorithm
public class Fibonacci {
public int calculate(int n) {
int a = 0, b = 1, c = 0;
if (n == 0) {
return a;
}
for (int i = 2; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}
}


// We start with an inefficient algorithm for calculating Fibonacci numbers.
// We refactor it to use a more optimized algorithm that
// uses iterative calculation rather than recursive calculation.
// This results in a significant improvement in performance,
// especially for larger values of n.

Conclusion:

Writing clean, maintainable code is essential for the success of software projects. By following best practices like keeping it simple, using descriptive names, writing modular code, following the DRY principle, testing your code, and using version control, you can ensure that your code remains maintainable over time. This not only saves you time and effort but also helps you deliver high-quality software that meets your clients’ needs.

--

--