Performance is a world that is always in the minds of all developers and engineers. Many things used among tools, libraries, frameworks and techniques have good performance as its one of the reasons to its success. Not only the performance while developing, building and deploying, but, most importantly, the performance perceived by the client. That was the lesson I learned this week. I was unconsciously aware of this fact, but seeing it happening for the first time was an experience I will not forget from now on.
The story is very simple, at my company we are developing a web application using Spring Boot 2.0 while also learning how Spring Framework does things, it has been a learning process from JAX-RS and pure Java EE to Spring Boot 2.0, which facilitates a lot of things, but you still have to know how Spring Boot can help you. It has been great although we make mistakes and have to refactor things. It was one these moments that the lesson came.
The project is already almost ending and we are in the final tests, still, it was perceived by the client a certain slowness on the website loading. To be sincere, by my standards, it was not that slow, other sites with similar purpose performed similarly or even worse. Despite that, I took the challenge to investigate and solve the problem, improving the performance. Google Chrome DevTools for the rescue at first to analyze its performance.
First I used Lighthouse to audit the site and have some ideas to where the performance was poor. There I discovered that the main reason for the poor performance was the loading of resources: CSS and JS, principally. The server's answer was very fast, but the page was taking too long to load. On the "Network" tab you can see the distribution of the time for each resource and everything corroborated to what Lighthouse suggested. So I went to the code to see two things:
- Minify resources
- Cache resources
My priority as a backend developer was first look at the caching policy of the resources. There was none at that time, I confess that it was my mistake and the team mistake, but now that we knew the problem, it was better to solve it the way it should be solved. I started to research for how Spring handles the issue and once more I realized how great this framework is.
Spring Framework has a lot of different features to handle this issue, from the cache itself, like put the appropriate headers to all resources automatically to versioning all resources. One article that was very helpful was by Baeldung, but it is for pure Spring Framework and although it is basically the same thing some times you need to translate it to the Spring Boot way, otherwise some errors can happen. When you see that things are too complicated, you are probably doing it wrong. Unfortunately, I don't remember all the details that brought me to the solution, I had the idea of the text after the event, but the final configuration is as follows:
spring.resources.cache.cachecontrol.max-age=365d
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
The first reference for Spring Boot application are the application properties available. The first line tells the amount of time the resource will be cached, 365 days in this case.The second line enables the Content Versioning strategy, which basically appends a hash of the file to the name of all resources in the HTML. The last line tells the paths where the versioning will be applied, which are all.
After that you have to tell what urls the versioning are needed. Maybe there is a more automatic way to do this, but it was the solution I found, if you know a better way, please comment. First, on a Controller Advice, I inject an instance of a ResourceUrlProvider, which is responsible to build the new url for each resource.
@ModelAttribute("urls")
public ResourceUrlProvider urls() {
return this.resourceUrlProvider;
}
After that, you just need to use the method getForLookupPath()
method of the class on the urls. It will verify that it is a resource, do the hash and append to the file's name. If the files change, its hash you change, forcing the browser to reload it.
<script src="<c:url value="${urls.getForLookupPath('/js/jquery.js')}"/>"></script>
It was a day work that changed the way the client and the manager perceived the performance of the application. The loading time went down from approximately 10 seconds to 2 seconds at max, or even faster depending on the page. I think I was never praised like that, that's the lesson. It is always good to show the result of your development in way that it is clear for the client and management. For a backend developer it is not always easy and it is mostly about performance. Therefore, always look for ways to improve your performance. It is not easy, many of the solutions on the web are the easiest, but not always the best, so try to be critical, measure the performance, see if there is a way to improve even more.
After that event I reinforced my care for performance. I hope I can keep improving like this from now on. :)
Top comments (0)