Coding guidelines are extremely important part of a professional developer’s day to day practices.
Following these guidelines differentiate between an experienced developer and a rookie.
It surprises me that so many companies still ignore them and produce poor quality code that results in very expensive maintenance over the period and is so fragile that every time you add a new feature immediately bugs creep in.
I am sharing some of these guidelines which are far from exhaustive but are most important for me. Some things people might not agree with but these are my experiences and many of them are borrowed from classic tests.
General Coding guidelines
These are general predefined standards for developing a code. This reduces
- Naming Conventions should be descriptive (Variable as well as functions).
- Your application must have separate static and dynamic parts.
- No Hard Coding. Find an appropriate place where you can define constants or enums.
- Prefer simplicity over complexity. If your code is turning out to be very complex most likely you are doing something wrong. As the saying goes its “hard to build simple things”
- Avoid premature optimization. Define premature optimization for your own use case. Well sounds awkward. Trust me it is. Only experience can tell you what does this really mean
- Always look for possibility of following a standard Design Pattern. Tweak it for your own use case
- Strictly prohibit repetitive code. If code is repeating it’s a candidate for refactoring.
- Always align your code properly before committing code
- Class should not be more than 600 lines.
- Constructor should not have any complex logic and has to be exception safe.
- Prefer composition over inheritance
- Follow one responsibility rule everywhere
- Design for extensibility
- If in Object Oriented language always define an interface
- Avoid circular dependency. If working with a web framework consider using
Comments and Error Messages
- Write comments at all critical places in your code including variable name, their usage, function signature (input/output/parameters).W
- Work with error messages framework. Using error codes for displaying error messages is confusing as it’s hard to figure out which error code is coming from which place. To avoid this chaos, it is recommended to use error message framework.
- Do not write deep nested if else statements.
- If nesting is getting deeper break your code into multiple functions
- Operator precedence for your language can introduce nasty bugs in your code which are extremely hard to debug. Follow a policy of using parenthesis while writing long if else conditions.
- It is recommended to implement OOPS in your code as much as possible.
- Program to an interface (contract),not class. Do not change the interface as much as possible
- Try to make an abstract class for a business service (in case of python/C++, interface in case of Java).
- Follow DRY Principle (Don’t repeate yourself). Use Design Patterns to promote code reusability
Java Best Practices
- Use interface when declaring collection variable like Map<String,Object> = new HashMap<String,Object>();
- Avoid using Object as much as possible. Thrive for TypeSafety
- Use StringBuilder for performance and safety
- Use java.time package when dealing with deals https://www.programcreek.com/java-api-examples/index.php?api=org.joda.time.format.DateTimeFormatterBuilder
- Use same timezone everywhere in application
- NativeQuery also takes class as a parameter so try using this overload…https://vladmihalcea.com/the-jpa-entitymanager-createnativequery-is-a-magic-wand/
- Function should not be more than 25 lines.
- Always check for valid parameters inside public functions. Throw an exception to report an error in params
- To group the statements logically, try to divide different sections of a function into other smaller functions. E.g. Separate function for initializing values for every possible activity.
- Use functional programming capabilities if your stack supports it. I.e. pass around functions to write reusable code.
- Follow Single Responsibility Rule as closely as possible.
- Functions have to be testable (I should be able to write unit test case for this function). In other words promote loose coupling via Dependency Injection or otherwise.
- To continue with loose coupling follow the rule “Prefer composition over inheritance”.
- If you are working with Java8 Never return null. Consider returning Optional
- Try to avoid multiple return statements. This can put nasty bugs inside programs so it’s best to avoid them as much as possible.
- Check Big O Complexity of algorithm you are writing. Especially for the case, where you are writing a lot of lines of code or for functions which are on critical path.
Function overloading should follow convention
- foo(int), foo(int,double), foo(int, double, object) i.e. least needed parameter at the last.
Follow layered architecture in true spirit. Upper Layer should call into lower layers and each layer has to be designed for specific purpose. E.g. while following MVC, Logic in views has to be related to view and all heavy lifting shall be done by service layer.
Package Structure andnaming conventions
- All Java Packages should start with com.broctagon. Check for specific naming convention in your stack but topmost package has to be com.broctagon.
- Define functions in packages instead of utility. It’s a common malpractice to put every seemingly useful function inside utility classes. And while writing code it becomes difficult to look into these packages. If it’s a business utility function then try to find a proper package for it rather than putting function inside utility classes. Utility classes generally shall have function related to common tasks like String Reverse or some Math functions or may be email format checking utility.
- It is recommended to use logging, wherever possible. Purpose of the logging is to diagnose any potential issues in production. Logging is useful but it incurs significant overhead on the application so it must be used wisely and only information required shall be logged.
- Logging should not be cluttered, it must follow same consistent pattern across the application. Identify a pattern for logging for your specific use case
- Logging libraries are incredibly useful. Use their package level capabilities to switch on/off selective logging at different levels.
- Do not suppress exceptions
- If an exception is explicitly raised in a function then it should not be handled in that same function. Create a separate function to handle exception and process.
- Do not suppress original exception even if you have to create a new exception
- Try to use already available functions in logging libraries.
- Comment on bypassing function i.e if we are passing any exception then mention in comment why we are doing this.
- Try following naming convention for exceptions as per your language e.g. Exception suffix in Java
- Do not write complex code in handler. Lot of times this code block throws an exception and hides original exception
- Read about exception handling best practices for your respective language and follow same.
- Always follow MVC pattern
- Do not bloat your controllers
- Make your URLs simple and easily understandable by end user
- “/admin/orderhistory” should be changed to “/admin/order/history”
- Make your services code “testable” which means loose coupling
Spring Best Practices
- Always Follow Builder pattern for response entity
Releasing the software
- All Java services should follow microservices architecture and be packaged as Fat Jar.
- Docker is used to deploy the software