Intro/Question
I fear that frameworks in many cases have become a fashion or trend and are being abused. In many ways people are sacrificing speed just because they want to keep up with every single lib that comes out. I am for a more conservative approach that only makes use of new libraries and frameworks when it absolutely makes sense.
Related: When NOT to use a framework
Update/Disclaimer (May 18, 2017)
I've come to the conclusion that rapid application development, ease, and maintainability are the targets. So frameworks (new and old) should be used provided they help in reaching a final product quicker. Only when special cases exist (actual measurable performance issues) should we switch to custom code... or when the project itself is legacy and the cost of completely rewriting are too high.
Case
We have a Tomcat6 Servlet production that has been ongoing development since 2008 and there are many hours of work behind it. The code is very mature but it does not use any frameworks.
A new team member loves using frameworks and patterns for everything. Except for the love of frameworks, they also love cutting edge new releases and even proposed migrating from Tomcat6 to somethiong newer (JBOSS,GLASSFISH, or TOMCAT8).
I would never move a core production service to a container/server/web-server that just came out.
Not against refactoring code to keep it clean though!
I am against adding frameworks and patterns that add delay to production code servicing thousands of requests per second.
Serialization to stream (JSON/XML) while maintaining backwards compatibility
When a request comes, we produce both XML and JSON documents containing data related to the request params.
Since we maintain backwards compatibility for older clients changes must not break older support. Since we also support both JSON and XML, we make use of the Jackson but also KXML libraries.
For both formats we have 2 different serialization methods that use the streaming API. Except for being faster, I also find it is MUCH more flexible for maintaining backwards compatibility.
I like to use a sysyem of Major and Minor version. All minor changes to the backend that cause changes to the serialized document I add with IF statements in the serialization method. Once quite a few changes have accomulated (over 1 year), we add a new serialization method. So, all requests specifying version=1.00 to 1.99 are sent to serializeJSON1. All requests using version 2 to 2.99 are sent to serializeJSON2.
I've heard the argument that we should use ANNOTATIONS and MAP to objects. I find this very distastefull as I have read streaming is the fastest.
The streaming API allows you to make necessary changes on the fly while MAPPING would require to store multiple versions of the same data as different objects, or jump through other hoops.
output.startTag(null, TagTable.PRODUCT);
output.startTag(null, TagTable.ID);
output.text(ID);
output.endTag(null, TagTable.ID);
output.startTag(null, TagTable.DESCRIPTION);
if (locale != null && Translations.containsKey(locale) && version >= 1)
output.text(this.DescriptionURL + "/" + locale);
else
output.text(this.DescriptionURL);
output.endTag(null, TagTable.LONGDESCRIPTION);
if (version >= 1 && version < 1.5) {
output.startTag(null, TagTable.POPULARITY);
output.text(String.valueOf((int) this.pop));
output.endTag(null, TagTable.POPULARITY);
} else if (version >= 1.5) {
output.startTag(null, TagTable.RATING);
output.text(String.valueOf((int) this.rating));
output.endTag(null, TagTable.RATING);
}
In the specific 3 cases above I really don't see the point to replace working mature code with frameworks/libs/patterns that may add additional overhead and cost additional development time.
So, when is it worthwhile to replace working mature code with frameworks+patterns??
Comments?
Update: Some benchmarks
For the first two examples posted previously, using REST and HASHMAPs does not make overly that big a difference in performance but does make the code more readable.
Now for the more interesting issue about serialization when used in a hotspot. Since the actual servlet in question had too much other code I made a toy example to test streaming vs mapping. The code is available at: https://stackoverflow.com/questions/21781540/
By all means test it, change it, run it.
What is very interesting is that the Just in time optimizer in java ends up making the difference negligable over time. So, here is how the percentage between mapping and streaming changes the more iterations there are.
Iter Stream Mapping
0 36,71% 63,29%
20 44,75% 55,25%
40 45,65% 54,35%
60 45,95% 54,05%
80 46,24% 53,76%
100 47,09% 52,91%
120 47,09% 52,91%
140 47,37% 52,63%
160 47,64% 52,36%
180 47,92% 52,08%
200 47,64% 52,36%
220 47,92% 52,08%
240 47,92% 52,08%
260 47,92% 52,08%
280 48,19% 51,81%
300 48,45% 51,55%
320 48,45% 51,55%
340 48,45% 51,55%
360 48,45% 51,55%
380 48,45% 51,55%
400 48,45% 51,55%
1000 48,72% 51,28%
2000 49,24% 50,76%
3000 49,24% 50,76%
4000 49,49% 50,51%
5000 49,75% 50,25%
6000 49,75% 50,25%
7000 49,75% 50,25%
8000 49,75% 50,25%
9000 49,75% 50,25%
I also re-ran one more time have first 20 iterations more detailed:
0 39,76% 60,24%
1 41,18% 58,82%
2 40,83% 59,17%
3 40,12% 59,88%
4 40,48% 59,52%
5 40,83% 59,17%
6 40,83% 59,17%
7 40,83% 59,17%
8 40,48% 59,52%
9 40,12% 59,88%
10 41,52% 58,48%
11 42,53% 57,47%
12 42,86% 57,14%
13 42,86% 57,14%
14 43,18% 56,82%
15 43,50% 56,50%
16 43,50% 56,50%
17 43,82% 56,18%
18 43,82% 56,18%
19 43,82% 56,18%
20 44,75% 55,25%
After 10,000 iterations (or calling JSON generation code 1000 times) here is the screenshot from VisualVM profiling (visualVM snapshot NPS file):
So, if this optimization behavior holds up in Tomcat servlets as well, it means there really is no point NOT to use mapping (and many other convenience functions/libs since the JVN will optimize over time).
Conclusions
Based on the answers/comments, I think at the end of the day whether to rewrite working production code is affected by business requirement, and specific cost-benefit analysis for each project.
Based on the benchmarking, it turns out avoiding some libs/frameworks may make no difference in the long run due to JIT JVM optimizations.
Update: After leaving code running all night
After 275,137 iterations and approximately 30,000 MS:
Mapping - 19,80%
Streaming - 80,20%
I really wonder what JIT optimizations happen here and whether the behavior holds for a servlet.