When to Replace Mature Code with Frameworks and Patterns

javapatterns-and-practicesprogramming practices

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):

enter image description here

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

enter image description here

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.

Best Answer

They say

a bird in the hand is better than 2 in the bush

Applied here, I take it to mean its better to have something that works and is maintainable than to tear it all down for some fantasy daydream of "but it'll be vaguely better if we use X Y or Z cool new technology for the sake of using the cool new technology".

I mean, you could have rewritten it in a functional language a few years ago, or Ruby on Rails a couple of years ago, or Node.js a year ago or... whatever comes along next that gets attention in the technical blogosphere. None of these things would create you a stable product that satisfies the 'must use new stuff' people as they will be considered old technologies before they're even finished and they'll want to re-rewrite!

So I have to ask "what's your problem?". You have good code that might be a bit mucky here and there, but in my extensive experience, when you start a big rewrite you end up with code that's a bit mucky (often quickly implemented due to inexperience with the tooling or done under time pressure that you will come back to and fix up nicely, promise.)

When you use a framework is when you are doing a new project and you want a load of code written for you, that's the time to go framework, to re-use all that boring boilerplate that you'd have to write yourself. You never go for a framework because its there to be used, especially when you have an existing framework that you know (even if you don't call it a framework because its grown by itself, it's effectively still one).

One thing to note, I do find a lot of the people who insist on using any kind of new technology want to for 1 of 2 reasons: they either want to boost their CV, or they do not want to learn the existing technology you use (after all, learning tech x is way more fun than actually doing work on existing product). I treat all calls with suspicion as in either case their intention is not in the best interest of the product or the business.

Heavy refactoring...that's a different story, and usually a good one.