Hello all, if you are in software development then you know that creating an application is easy but creating a maintainable application that can pass the test of time in production is rather difficult and that's the main reason experienced Sofware developers are paid higher salaries compared to many other jobs. In this article, I am going to share with you some tips on creating maintainable Java applications which will help you not just in the new year, but also in all the coming years in your software development career. One aspect of development, which is often overlooked by developers is to create applications that are both easy to maintain and support. Since software spends 90% of its lifetime in maintenance mode, it's very important that your application is easy to configure, support, and maintain.
Also, the Java application is no different than any other software, you must pay attention to these key properties of writing production-quality code, hence this tip also applies to any programming language which is used to create real-world software which is used by real users.
If you have worked in large organizations e.g. Investment banks like Barclays, Citibank, or big Insurance companies you will find how important is support and maintenance of production applications are.
If you have worked in large organizations e.g. Investment banks like Barclays, Citibank, or big Insurance companies you will find how important is support and maintenance of production applications are.
There are several more software engineers working in support roles than programmers developing software. You have L1, L2, and L3 support and then a dedicated support team for every application. This means support is really important.
But, when programmers develop an application, they focus on features and speed of development like how quickly a feature is ready for testing, etc, and to achieve these many programmers including myself in past often ignore things that matter a lot in production.
Here are some practical tips to make your Java application more maintainable. Always remember, maintenance cost is much higher than development cost and it's easy to give a solution but it's equally difficult to give a maintainable solution i.e. something which can withstand the test of time.
Before going to explain these 10 tips which can make your Java application more maintainable and easy to support, let me tell you that I have personally made a lot of these mistakes myself. It requires a great deal of discipline, hard work, and being vigilant about writing quality code.
But, when programmers develop an application, they focus on features and speed of development like how quickly a feature is ready for testing, etc, and to achieve these many programmers including myself in past often ignore things that matter a lot in production.
10 Tips to create better Java Applications.
Here are some practical tips to make your Java application more maintainable. Always remember, maintenance cost is much higher than development cost and it's easy to give a solution but it's equally difficult to give a maintainable solution i.e. something which can withstand the test of time.Before going to explain these 10 tips which can make your Java application more maintainable and easy to support, let me tell you that I have personally made a lot of these mistakes myself. It requires a great deal of discipline, hard work, and being vigilant about writing quality code.
Sometimes you have to push back even to your team lead or managers bringing the points like support, which is often overlooked.
1. Don't swallow exceptions
Please avoid swallowing the exceptions. The stack trace is the most valuable troubleshooting information. In the production system where the priority is to bring the system up and then find the root cause, these exceptions are gold, without them, you will never be able to find what happened with your application at that moment.On the other hand, please don’t print the stack trace multiple times. Printing a stack trace is a resource-intensive process and should be controlled i.e. you print more information while running on DEBUG or INFO mode and only print essential information while running in PRODUCTION mode. This is an example of swallowing exception in Java:
This is also known as an empty try-catch block and many static code analysis tools like Fortify will catch these in the early stage of development. This also highlights the importance of static code analysis in Java development. Make sure, you integrate the static code analysis tool as part of your build process.
If you are still not convinced about static analysis, then please read my post on why static code analysis is important, that will give you more reasons to use it in your project.
1. It puts an unnecessary load on the Application. I’ve seen the throughput of the application is reduced to half due to excessive logging.
2. It can fill up the file system very quickly and that can create issues for your application and other applications hosted on the same server. This is a serious problem especially if you are co-hosted with some other application. Do you know what will happen when root directory of certain flavors of Unix systems fills up? - that’s right. No one can login into the host.
3. Troubleshooting will be painful, like looking for a needle in a haystack (if the poor support guy can ever get the log file to open).
In short, you have to keep the balance between excessive logging and not enough logging, and to be honest that is also an art, which requires a good knowledge of both application and domain.
try{ // do something }catch(FileNotFoundException fe){ // do nothing }
This is also known as an empty try-catch block and many static code analysis tools like Fortify will catch these in the early stage of development. This also highlights the importance of static code analysis in Java development. Make sure, you integrate the static code analysis tool as part of your build process.
If you are still not convinced about static analysis, then please read my post on why static code analysis is important, that will give you more reasons to use it in your project.
3. Avoid excessive logging
This tip is closely related to the first one and at first, it may look contradictory but in reality, it's not, in fact, they both compliment each other. When you run the Java application in your development environment (PC), nobody cares what’s logging level you have. Go ‘DEBUG’ or ‘ALL’ if you please. But when it goes to production (or other higher environments e.g. QA or UAT ), limit your logging to ‘INFO’ or ‘ERROR’. Excessive logging has 3 major impacts:1. It puts an unnecessary load on the Application. I’ve seen the throughput of the application is reduced to half due to excessive logging.
2. It can fill up the file system very quickly and that can create issues for your application and other applications hosted on the same server. This is a serious problem especially if you are co-hosted with some other application. Do you know what will happen when root directory of certain flavors of Unix systems fills up? - that’s right. No one can login into the host.
3. Troubleshooting will be painful, like looking for a needle in a haystack (if the poor support guy can ever get the log file to open).
In short, you have to keep the balance between excessive logging and not enough logging, and to be honest that is also an art, which requires a good knowledge of both application and domain.
This is also where experience comes into the picture, involving support guys from the UAT itself, they will give you valuable tips on logging and supporting the application. Remember, life is all about keeping the right balance :-). See here for more logging tips for Java developers.
4. Don't Forget to Close Database Connections
This is one of the most common reasons for production issues in the last decade, but thankfully with modern frameworks and libraries, this issue is actually slowly disappearing (as the framework takes care of opening/closing connections).However, make sure you always ‘close’ the database connection so that it is released back to the pool. This is also one of the JDBC best practices, I have shared with you in my earlier post 10 Essential JDBC best practices for Java programmers. If you haven't read it yet, make sure you read it in 2017.
A common mistake is not closing the connection in the ‘finally’ block of a ‘try catch’. If there is a connection pool leak, your connection pool with be exhausted soon and your user will experience immediate slowness.
The same rules go to closing sockets and streams if you don't close them you will run out of resources pretty soon. Sometimes, Java developers think that they have closed the connection but in reality, they were not closed, hence you must know the right way to close streams in Java.
This is part of general resource management best practices. Not to discourage you but I have personally found C++ developers excel Java developers when it comes to resource management. They are more vigilant about closing the connection and releasing resources, something Java developers can learn from C++ programmers.
You might have heard the developer talking to support personnel that ‘It works fine in my development environment. But when it went to production, it crashed’.
Yes, it is the job of the load testing team to test your application with the production like a load. But that does not mean that as a developer you write code that does not scale well. For example, it works fine for 1 user, but what happens when there are 500 users online simultaneously.
The issue is brutally exposed while writing concurrent code because the probability of race condition is much higher in production than in any other environment. So, always keep the production load in mind and code.
You should also read my post about essential multi-threading and concurrency best practices for Java programmers if haven't read it already. That will help you to envision some of the things which you might not think otherwise.
Secondly, you just cannot keep the data in your application forever because it may become stale hence you need to load it again.
So, instead of loading all records in one go, implement some sort of ‘pagination’ and/or ‘lazy loading’ so that you don’t have to load everything in the beginning.
This is where ORM and caching framework like Hibernate helps a lot. They simply free up Java developers from worrying about lazy loading and pagination. If you want to learn more about how lazy loading works in Hibernate, I suggest reading Java Persistence with Hibernate or watching High-Performance Java Persistence by Vlad Mihalcea both are great books that every Java developer using Hibernate should read.
Instead of hard-coding configuration parameters, you must externalize them in a property file. It seems simple enough but I have seen it over and over again that somehow some hard-coded value sneaks in and breaks when it goes to production. In one word, managing configuration information within the code is a nightmare. Never do that.
This will not work whenever your company decides to move to Windows from Unix and it will be painful to refactor hundreds of lines of code containing such code. This is again the case where static code analysis can help you a lot. Make sure you integrate tools like Sonar or Fortify in your build process to regularly scan code for such code smells.
For example, if you have a scheduled job within the code, what will happen to it if you run multiple instances of the same application? Wouldn’t it run multiple times? What are the side effects of this?
It's best to think to cluster at the start of development and avoid scheduling jobs from Java code directly, think about using more useful tools like Autosys for Job scheduling and monitoring.
You must have a clean build and versioning processing, especially for internal applications. Consider using Maven for dependency management, it makes life a lot easier than keeping versioned JAR files in the lib folder.
The infamous ‘ClassCastException’ or ‘NoClassDefFoundException’ is most of the time due to the version mismatch of third-party jar files. Make sure you only pack one copy of the jar file and your build is consistent across environments like Dev, QA, UAT, etc.
It's also a good practice to keep configuration separate from binaries so that you can release the same binaries across the environment e.g. promoting the same binary from UAT to Production after testing successfully.
That's all about some practical tips on how you can make your Java application easy to support and maintain. These small things can make big difference when it comes to developing and maintaining a real-world Java application. If you are aspiring to become a solution architect or Java architect, paying attention to these details will help you put your case forward more strongly. A good Java architect will ensure that the application is both easy to maintain and support.
You can also do good if you include various stakeholders early in the development stage e.g. support team, testing team, middleware guys, Unix and infra guys, networking peoples, and business guys. Though don't overwhelm with a lot of details coming from every direction, just keep calm and drive.
Other Java best practices articles to improve code quality of your Java applications:
A common mistake is not closing the connection in the ‘finally’ block of a ‘try catch’. If there is a connection pool leak, your connection pool with be exhausted soon and your user will experience immediate slowness.
The same rules go to closing sockets and streams if you don't close them you will run out of resources pretty soon. Sometimes, Java developers think that they have closed the connection but in reality, they were not closed, hence you must know the right way to close streams in Java.
This is part of general resource management best practices. Not to discourage you but I have personally found C++ developers excel Java developers when it comes to resource management. They are more vigilant about closing the connection and releasing resources, something Java developers can learn from C++ programmers.
5. Don't underestimate production load
If you are an experienced Java developer you would have noticed that most of the issues are exposed in the production environment rather than in UAT or QA environment, especially concurrency-related issues? Do you know why? because of production load.You might have heard the developer talking to support personnel that ‘It works fine in my development environment. But when it went to production, it crashed’.
Yes, it is the job of the load testing team to test your application with the production like a load. But that does not mean that as a developer you write code that does not scale well. For example, it works fine for 1 user, but what happens when there are 500 users online simultaneously.
The issue is brutally exposed while writing concurrent code because the probability of race condition is much higher in production than in any other environment. So, always keep the production load in mind and code.
You should also read my post about essential multi-threading and concurrency best practices for Java programmers if haven't read it already. That will help you to envision some of the things which you might not think otherwise.
6. Avoid loading large result sets from Database
This is one of the common mistakes made by beginners and intermediate Java programmers who don't know about paging or pagination. You simply cannot load every record e.g. order or trade from the database in one call. In some cases, obviously, you will run out of memory and it is also a waste of network bandwidth, CPU, and memory as the user might not need all of those data.Secondly, you just cannot keep the data in your application forever because it may become stale hence you need to load it again.
So, instead of loading all records in one go, implement some sort of ‘pagination’ and/or ‘lazy loading’ so that you don’t have to load everything in the beginning.
This is where ORM and caching framework like Hibernate helps a lot. They simply free up Java developers from worrying about lazy loading and pagination. If you want to learn more about how lazy loading works in Hibernate, I suggest reading Java Persistence with Hibernate or watching High-Performance Java Persistence by Vlad Mihalcea both are great books that every Java developer using Hibernate should read.
7. Avoid hard coding Configuration Parameters
You might have heard this tip several times but you would be surprised if you look at the code written by many professional software engineers and programmers. There is hardly a code where something is not hard-coded but hard-coding configuration values like URLs, directory locations, username/passwords, cache sizes, log levels, etc in the code results in hard-to-maintain Java applications.Instead of hard-coding configuration parameters, you must externalize them in a property file. It seems simple enough but I have seen it over and over again that somehow some hard-coded value sneaks in and breaks when it goes to production. In one word, managing configuration information within the code is a nightmare. Never do that.
There is a flip side as well, where many programmers just create too many property files with the hope to generalize everything. It does make sense to keep related properties in one place e.g. if a couple of configuration parameters is shared by multiple application then externalize them in one property file like. database and middleware URLs, username, password, etc and let other application import that file, but if you do it over a limit then it becomes a maintenance nightmare.
You should also never mix environment-related configuration parameters e.g. URL, directories, username/password with application-related parameters e.g. config parameters to enable disable some functionalities.
It's better to keep separate properties files for application properties and environment properties. This way, you would have one application properties across the environment which is essential for testing and production release.
8. Don't write Platform-specific Code
Many Java programmers just don't give a shit to writing platform-specific code, thinking that Java is platform-independent. Even though Java is platform-independent, if you are not careful you will end up making your Java application platform dependent. Java programmers should not code anything that is related to the local operating system. For example, executing a Linux command (example: uname -a) from java and handling the output.This will not work whenever your company decides to move to Windows from Unix and it will be painful to refactor hundreds of lines of code containing such code. This is again the case where static code analysis can help you a lot. Make sure you integrate tools like Sonar or Fortify in your build process to regularly scan code for such code smells.
9. Consider Clustering
This is one area where even many experienced Java programmer also fails. Since every application doesn't run in the cluster it's possible to not think about clustering at the start but if you ever decide to run your application in a cluster in the later stage of development, it would be really hard to refactor your application.For example, if you have a scheduled job within the code, what will happen to it if you run multiple instances of the same application? Wouldn’t it run multiple times? What are the side effects of this?
It's best to think to cluster at the start of development and avoid scheduling jobs from Java code directly, think about using more useful tools like Autosys for Job scheduling and monitoring.
10. Avoid packing multiple versions of the same JAR files
Packaging utility jar files in several places, especially various versions of the same utility jar in various locations is the cause of many production issues.You must have a clean build and versioning processing, especially for internal applications. Consider using Maven for dependency management, it makes life a lot easier than keeping versioned JAR files in the lib folder.
The infamous ‘ClassCastException’ or ‘NoClassDefFoundException’ is most of the time due to the version mismatch of third-party jar files. Make sure you only pack one copy of the jar file and your build is consistent across environments like Dev, QA, UAT, etc.
It's also a good practice to keep configuration separate from binaries so that you can release the same binaries across the environment e.g. promoting the same binary from UAT to Production after testing successfully.
That's all about some practical tips on how you can make your Java application easy to support and maintain. These small things can make big difference when it comes to developing and maintaining a real-world Java application. If you are aspiring to become a solution architect or Java architect, paying attention to these details will help you put your case forward more strongly. A good Java architect will ensure that the application is both easy to maintain and support.
You can also do good if you include various stakeholders early in the development stage e.g. support team, testing team, middleware guys, Unix and infra guys, networking peoples, and business guys. Though don't overwhelm with a lot of details coming from every direction, just keep calm and drive.
Other Java best practices articles to improve code quality of your Java applications:
- 10 Java Exception Handling Best Practices (read)
- 10 Java Multithreading and Concurrency Best Practices (click here)
- 10 Programming best practices to name your variables (learn)
- 10 Tips to Avoid NullPointerException in Java? (read)
- 5 Method and Constructor Overloading Best Practices in Java? (learn)
- 10 JDBC Best Practices for Java Programmers (learn)
- 10 Tips to follow while writing Code Comments (follow)
- 10 Logging Best Practices Every Java Developer should follow (read)
These were some collections of essential best practices for Java programmers, but you will find a lot more if you explore the Javarevisited blog like why use SLF4j for logging over Log4J in Java, etc. I have shared a lot of small tips and best practices that will help you to write better code in Java. Once you learn about them they will go into the back of your mind and alert you whenever you are writing code that violates those rules.
Though these best practices will help you to write better code in Java, they are not substitutes for good programming habits and essential programming best practices given in the Clean Code by Uncle Bob Martin. If you want to do one thing in the new year to improve your programming, I suggest you must read the Clean Code next year.
Though these best practices will help you to write better code in Java, they are not substitutes for good programming habits and essential programming best practices given in the Clean Code by Uncle Bob Martin. If you want to do one thing in the new year to improve your programming, I suggest you must read the Clean Code next year.
4 comments :
Hi!It was only have 9 tips.:)
Thanks for reading article John, yes, it is indeed 9 tips in headlines but a lot more if you read details :-). Don't worry I'll add more if you guys like this kind of posts.
Nice
Great tips!
Post a Comment