8. Writing Squeaky Clean Code#
8.1. Introduction#
Welcome to the essential guide to clean coding standards. Writing clean code is not just about making your code work; it’s about making it understandable, maintainable, and efficient. This chapter will cover the core principles and practices that will help you write clean and professional code. New programmers are often exposed to the new skill of reading and debugging code that isn’t their own, and writing cleaner code helps developers work better together.
In school, you often write singular functions, avoiding many functions that work together or definining functions yourself. It is important that before you start writing code, to make sure you sit down and plan out how to break down the problems you’re dealing with into smaller problems that can be defined as functions. But once you’re done planning, here are some good rules to follow while writing code in a proffessional or open source setting.
Image Source: https://medium.com/geekculture/principles-mindset-of-clean-code-be-a-better-programmer-a700d64abee1
8.2. Naming Conventions aka The Art of Naming#
Variables should be named in a way that clearly describes their purpose. Avoid vague or generic names. For example, use totalAmount
instead of t
or temp
. Descriptive names enhance readability and make your code easier to understand. Try to step out of yourself and see if a stranger could understand what your variables mean!
Function names should clearly convey their purpose. A function named calculateTotalAmount()
is much more informative than one named doTask()
. Functions should perform a single, well-defined action. (Pro tip: single and well defined is a good rule of thumb when planning your code). This not only makes them easier to test but also simplifies debugging and maintenance.
8.3. Function Design#
A function should do one thing and do it well. If a function is doing too much, consider breaking it down into smaller, more focused functions. This makes your code more modular and easier to test. And think about lengh when writing your functions- keep ‘em short! Long functions can be difficult for other people to understand and maintain. Try to keep functions within a screen’s length so that they can be viewed without scrolling.
8.4. Class Design#
Just like functions, classes should have a single responsibility. They should encapsulate related data and methods that operate on that data. Avoid turning a class into a “god object” that does everything. Are you sensing a pattern about single responsibility, yet? And, in the same vein, try to ensure that all parts of a class are related to the funcion’s purpose. Cohesion within a class means that its methods and variables are closely related, making the class easier to understand and maintain.
8.6. Code Consistency and Style#
Adhere to a consistent style guide throughout your codebase. That means making sure your naming conventions, indentation, spacing, and other formatting rules are consistent throughout your code base, sometimes this means adapting to someone else’s coding style. Consistency makes your code more predictable and easier to read.
One way to do this is to use linting. Linters help to enforce coding standards and catch potential issues early. They do this by automatically checking if you’re adhering to style guides, and they highlight common errors (we all miss semicolons, brackets, and commas sometimes), and doing this improves overall code quality. They can also increase turn-around in code review time, making sure when your code review is focused on logic and not silly errors like misspellings or forgotten parenthesis.
Another thing to look out for is code duplication! You can avoid code duplication by refactoring common logic into functions or classes. Duplicated code increases the risk of bugs and makes maintenance harder. It also makes your life easier, and code more readable! Save yourself the time and effort. A good rule of thumb is not to write the same code twice.
Long parameter lists can be confusing and more prone to error. If you want to group things together, try using objects or structures to group related parameters, so your function calls are more readable
Lastly, try to avoid using hard-coded numbers or “magic numbers” in your code. For example, instead of using 365
, try using DAYS_IN_YEAR
to clarify what it’s doing and make your code more flexible.
Source: https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/
8.7. Importance of Testing#
Testing is crucial for ensuring code reliability. Write unit tests to verify individual functions and integration tests to ensure that different parts of your system work together correctly. Automated tests can catch errors early, reducing the risk of bugs in production.
Some things to focus on when testing could be checking if the data is empty or null, if there are invalid inputs or outputs, checking if data is too large to handle, or too small to take in. Pay attention to special characters as well. Try thinking like a user who often makes mistakes! Does double clicking on a button duplicate an entry in a database? If you change your workflow on your product and use commands in the order they’re not usually done, does it change the outcome? Try to break your code when you test!
Aim for high test coverage, but prioritize critical paths and core functionalities. Comprehensive testing increases confidence in your code and simplifies future changes and refactoring.
Here are some example tests for a Java class called BigInt, whose job is to find the biggest integer in a list:
public static void main(String[] args) {
System.out.println("Unit tests for the BigInt class.");
System.out.println();
// Test 2: Printing BigInt object b1
System.out.println("Test 2: result should be 1234567");
int[] a1 = {1, 2, 3, 4, 5, 6, 7};
BigInt b1 = new BigInt(a1);
System.out.println(b1.toString());
System.out.println();
// Test 3: Printing BigInt object b2 (all zeros)
System.out.println("Test 3: result should be 0");
int[] a2 = new int[20]; // array of zeros
BigInt b2 = new BigInt(a2);
System.out.println(b2);
System.out.println();
// Test 4: Testing for IllegalArgumentException
System.out.println("Test 4: should throw an IllegalArgumentException");
try {
int[] a3 = {0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // invalid input
BigInt b3 = new BigInt(a3);
System.out.println("Test failed: Expected IllegalArgumentException was not thrown.");
} catch (IllegalArgumentException e) {
System.out.println("Test passed: IllegalArgumentException thrown as expected.");
} catch (Exception e) {
System.out.println("Test failed: Threw wrong type of exception.");
}
System.out.println();
}
You can see these tests focus on edge cases, yes, your code’s main functionality should work, but what if the user inputs something that they aren’t supposed to? What if they input really large numbers, or really small numbers? What if they input the wrong data type? The tests above are only a short amount of tests that you might want to explore. Ensure the the tests have a large number of cases, and try to break your code!
The best approach is to think like a user, or for an even better approach- a monkey with no idea what it’s doing. The best kind of code can be used by anyone!
Unit tests can also serve as documentation in some ways, looking at unit tests can help you understand what code someone else is doing, and should evolve with your code!
8.8. Conclusion#
Clean code is the foundation of effective software development. By following these best practices and standards, you can write code that is not only functional but also readable, maintainable, and robust. Embrace these principles to improve your coding skills, and contribute to higher-quality software projects.
Happy Coding!
8.5. Comments and Documentation#
Use comments to explain why something is done, not what is done. Don’t restate the obvious, focus more on the intention rather than the purpose of certain syntax. Well-written code should be self-explanatory, but comments can provide context or rationale behind complex logic. You can also use comments to remind or share with your collaborators that something may not be fully implemented.
An example of a reduntant comments would be:
Though this code is pretty simple, the comments don’t help us increase our understanding. Can you think of some better ways to write these comments?
These comment do a better job of expressing the purpose of the code, rather than reduntantly explaining code sytax.
8.5.1. Documentation#
Maintain clear and concise documentation for your codebase. This includes inline comments, function and class descriptions, and external documentation. Good documentation helps others (and your future self!) understand and use your code effectively.