We often concentrate on the logic and syntax in the beginning of our software career. Eventually there is a time where everyone starts focusing writing a clean and readable code. Writing clean code is hard-work. It takes a lot of effort from a developers side to create a software which is based on simple code. For such a software code itself is the design.
Here are some tips that matters to all java developers which can be used as a checklist before delivering their code.
- The first rule of methods is that they should be small.
- Do one thing inside a method – follow single responsibility principle.
- The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance, error handling complete according to an articulated strategy, and performance close to optimal so as not to tempt people to make the code messy with unprincipled optimizations.
- Use Intention-Revealing names for methods.
- Avoid disinformation in names.
- Make meaningful distinctions: you can’t use the same name to refer to two different things in the same scope.
- Use Searchable Names: Single-letter names and numeric constants have a particular problem in that they are not easy to locate across a body of text.
- Use descriptive names: A long descriptive name is better than a long descriptive comment. Use a naming convention that allows multiple words to be easily read in the function names, and then make use of those multiple words to give the function a name that says what it does.
- Function Arguments: The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.
- Flag Arguments: Passing a boolean into a function is a truly terrible practice. It immediately complicates the signature of the method, loudly proclaiming that this function does more than one thing.
- Functionality is implemented in a simple, maintainable, and reusable manner.
- Use descriptive and meaningful variable, method and class names as opposed to relying too much on comments.
- Class and functions should be small and focus on doing one thing. No duplication of code.
- Functions should not take too many input parameters.
- Use a standard code formatting template.
- Declare the variables with the smallest possible scope. (For example, if a variable “tmp” is used only inside a loop, then declare it inside the loop, and not outside.)
- Don’t preserve or create variables that you don’t use again.
- Omit needless and commented out code. No System.out.println statements either. (Use proper logging frameworks)
- Make a class final and the object immutable where possible. (Immutable classes are inherently thread-safe and more secured. For example, the Java String class is immutable and declared as final.)
- Use right data types.
- Return an empty collection or throw an exception as opposed to returning a null. Also, be aware of the implicit autoboxing and unboxing gotchas.
- Don’t log sensitive data.
- Favor immutable objects.
- Use Prepared statements as opposed to ordinary statements.
- Release resources.
- Don’t let sensitive information like file paths, server names, host names, etc escape via exceptions.
- Don’t ignore or suppress exceptions. Standardize the use of checked and unchecked exceptions. Throw exceptions early and catch them late.
- Presence of hard coded config values. Externalize configuration data in a .properties file. Sensitive information like password must be encrypted.
- Avoid multiple if/else blocks.
- Opt for caching if possible.
- Use exceptions rather than return codes.
- Don’t return null from a method. So that the caller of the method don’t have to check for null. Example:- Use Collections.emptyList() if a method usually returns a Collection object.
- Don’t pass null to a method.
- Clear and expressive code with few comments is far superior to cluttered and complex code with lots of comments.
- Legal comments:copyright and authorship statements are necessary and reasonable things to put into a comment at the start of each source file.
- Informative comments: It is sometimes useful to provide basic information with a comment.
- Explanation about the intent.
- Warning of consequences.
- Add TODO comments when needed.
- Function headers: Short functions don’t need much description. A well-chosen name for a small function that does one thing is usually better than a comment header.
- Do not write comments for what you are doing, instead write comments on why you are doing. Specify about any hacks, workaround and temporary fixes. Additionally, mention pending tasks in your to-do comments, which can be tracked easily.
- Check the test coverage and quality of the unit tests with proper mock objects to be able to easily maintain and run independently/repeatedly.
- Test only a unit of code at a time (e.g. one function).
- Unit tests must be independent of each other. They should run independently.
- Set up should not be too complicated.
- Mockout external states and services that you are not asserting. For example, retrieving data from a database.
- Avoid unnecessary assertions.
- Start with functions that have the fewest dependencies, and work your way up.
- Write unit tests for negative scenarios like throwing exceptions, negative values, null values, etc.
- Don’t put try/catch inside unit tests. Use throws Exception statement in test case declaration itself.
- Ensure that the unit tests are written properly and giving 100% code coverage.
- While going through the code, check the code formatting to improve readability and ensure that there are no blockers.
- Use alignments (left margin), proper white space. Also ensure that code block starting point and ending point are easily identifiable.
- Ensure that proper naming conventions (Pascal, CamelCase etc.) have been followed.
- Code should fit in the standard 14 inch laptop screen. There shouldn’t be a need to scroll horizontally to view the code. In a 21 inch monitor, other windows (toolbox, properties etc.) can be opened while modifying code, so always write code keeping in view a 14 inch monitor.
- Remove the commented code as this is always a blocker, while going through the code. Commented code can be obtained from Source Control (like SVN), if required.
- The application should require the least amount of effort to support in near future. It should be easy to identify and fix a defect.
- Readability: Code should be self-explanatory. Get a feel of story reading, while going through the code. Use appropriate name for variables, functions and classes. If you are taking more time to understand the code, then either code needs refactoring or at least comments have to be written to make it clear.
- Testability: The code should be easy to test. Refactor into a separate function (if required). Use interfaces while talking to other layers, as interfaces can be mocked easily. Try to avoid static functions, singleton classes as these are not easily testable by mocks.
- Debuggability: Provide support to log the flow of control, parameter data and exception details to find the root cause easily. If you are using Log4Net like component then add support for database logging also, as querying the log table is easy.
- Configurability: Keep the configurable values in place (XML file, database table) so that no code changes are required, if the data is changed frequently.
- DRY (Do not Repeat Yourself) principle: The same code should not be repeated more than twice.
- Consider reusable services, functions and components.
- Consider generic functions and classes.
- Extensibility: Easy to add enhancements with minimal changes to the existing code. One component should be easily replaceable by a better component.
Object-Oriented Analysis and Design (OOAD) Principles:
- Single Responsibility Principle (SRS): Do not place more than one responsibility into a single class or function, refactor into separate classes and functions.
- Open Closed Principle: While adding new functionality, existing code should not be modified. New functionality should be written in new classes and functions.
- Liskov substitutability principle: The child class should not change the behavior (meaning) of the parent class. The child class can be used as a substitute for a base class
- Interface segregation: Do not create lengthy interfaces, instead split them into smaller interfaces based on the functionality. The interface should not contain any dependencies (parameters), which are not required for the expected functionality.
- Dependency Injection: Do not hardcode the dependencies, instead inject them.
List compiled by: Deeksha K, UST Global Inc.