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 vigilance in
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:
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, which 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 complement each other. When you
run the Java application in your development environment (PC), nobody cares
what 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 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 the root directory of certain flavors of Unix
systems fills up? - that’s right. No one can log in to 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 the 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 for 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 that C++ developers excel over 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 the 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 conditions 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 the 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 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 result 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 or disable some functionalities.
It's
better to keep separate properties files for application properties and
environment properties. This way, you would have one application property
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 about clusters 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 ‘Class Cast Exception’ or ‘No Class Def Found Exception’ 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 that 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 people, and business guys. Though don't overwhelm with a lot of
details coming from every direction, just keep calm and drive.
Comments
Post a Comment