I am a Java developer who has been working on different products in different industries for the last 9 to 10 years. One thing I found very common is the frameworks or libraries on which we as developers rely during the development phase.
Most of the time the frameworks itself create antipatterns as part of their minor or major releases. The frameworks do this as part of a hack to nullify the pain caused by them in the earlier releases. Developers will have different opinions around this topic which I am well aware of. However, to be honest I found a huge percent of likeminded people who agree to a lot of pain points but discard them on a daily job though due to different unfamous reasons :).
To be frank, most of the developers know what are be the typical hardships on the maintainability topic or at least faced a couple of them during their daily coding times. Libraries are developed and open-sourced mostly as part of helping the society in a way to avoid reinventing the wheel. But the problem is, some wheels are reinvented only thinking about the near future which is good, but not great in terms of people opting to use the libraries. And these are the services which need to be maintained for the next 20 years.
We use some libraries to decrease the effort of typing some code and increase productivity by letting them generate the static code for us. A sign of relief, right?
Tomorrow someone else in your team updates or includes a completely unrelated library and this breaks the above library or even has a side effect. Now, this is not fun anymore. This allows me to dive into my next sub-topic.
There have been days where I start my day with a super happy face in the morning where my task is to upgrade the dependent library and thinking about releasing it in another 10 mins. However, we end up doing pair programming figuring out the other dependencies which break my tests now. At-times after spending a day, we revert the changes and think of not updating the library in this sprint since it was not productive and ends up never taking up this task. This adds up the technical debt and also makes some of your colleague's lives hard in-case we switch the company or team without proper comments or findings.
Absolutely hell! Most of our projects by itself will have tons of dependencies, even though what we needed was just a couple of classes from those libraries. Then each of these dependencies will add up another hundred of them. I agree DRY principle is one of the important concepts which everyone needs to have in their mind before coding. However duplicating some classes in each of our services instead of including the whole dependency is not a bad idea, isn't it? We see microservices already adopted duplicating data in their schemas and moving away from the old idea of having data at a single place(one big database). Packaging tools or build automation tools are nowadays used in most of the projects and personally, I have worked with maven a lot. That is one of the reasons I love this plugin https://plugins.jetbrains.com/plugin/7179-maven-helper, and I highly recommend this. At least it eases the pain to some extent on getting closer to heaven from hell (a dream though :)).
Most of us join the team by thinking we make the code so beautiful and make them perform better. Also, use all these fancy words such as reliability, performance, availability, etc. a lot. But very few of us deletes or clean up libraries which may not be used anymore. Some of us do not even care or have a simple read about them. However, I cannot complain since we rarely get time to code every day as 90 percent of the day will be in meetings and then we end up working overtime to release features on time.
Most of us rarely think of copying the logic from the library and not using it as a dependency inside the service itself. Say even if someone does that, Pull Requests will become the real enemies here. There will be comments and declines from most of your teammates saying the library is already available and why are you reinventing the wheel. Most of the time we end up in long discussions which are not productive and finally ending up with adding the library to the service itself. At this point, what I used to do is create a single-purpose library including only the necessary code and use it in this service and not using the entire library with 50+ files.
Say we follow all the clean code and clean architecture principles, can we say our service follows these principles unless you know how the dependent libraries are developed? At least, I do not agree with this. However, we could argue, that whatever comes under our responsibility or in plain words, whatever we develop or maintain only matters.
Some libraries generate getters and setters for the classes using annotations, and we then spread it everywhere or overuse it. Most of us may not be known, some of these generated methods have the visibility mode set to public and forgetting anyone can mutate the states of these objects.
It is been a long time I am working in Java and people complain about Mutability. Really? Most of these people talking also support a lot of libraries that modify byte code that gets generated during compile time :). I guess sometimes we need to broaden our mindset and try to accept the facts. I prefer immutability and that is one of the reasons for hating these libraries.
It is quite hard to change or improve things at times. I remember an example, we use spring framework and also a dependency which helps us to produce/consume events in Rabbitmq. So there were scenarios where we need to switch off our consumers but still produce messages. Nearly impossible to control it, since the consumer implementation has a parent class and it has a private variable where the threads are getting initialized. It took us one week to deprecate the whole library and write simple AMQP classes to produce/consume messages.
One of the reasons why we integrate some libraries is that it would be solving your current problem. However, one thing most of us do not research is how is it being solved and what are the hundred other problems we could end up shortly. Some of the fancy libraries come up with just one line to include in the package management XML file and then its all about creating key-value in the property files (developer-friendly), easy right? Believe me, changing something to include your application-specific feature would be hard in those libraries. Why? Either the library is built in such a way it would be so tightly coupled to the properties and specific logic, or it would be so generic, you need to write code to include your specific logic by implementing or extending the library classes. At times, in the end, you will not be needing the library itself because you wrote almost all the classes including most of the business logic.
I was even thinking of leaving a blank line for this topic. It is understandable since we hear it from everyone during conferences and talks :).
At this point, let me Thank God for super-powerful cloud machines. We use profilers to measure the problems in our code and we try to improve them. But can we do something about the 100ms spent inside the framework filters and logics? No. Most of the frameworks use reflections and annotation processing to support generic implementations.
This is why I always think twice before creating services for doing something generic. Do we need it for the future? The YAGNI principle is so true and admirable.
Similarly, one thing I noticed in some libraries is the development complexity. Simple business logic sits inside another 10 classes and it is like finding the treasure. Somehow I am a big fan of simple and understandable codebase rather than having all the fancy interfaces and abstract classes thinking about future implementations and making something very generic. Most of the time, as developers or users of these libraries, are interested in the file where the business logic is present and ignores all those files in the stack hierarchy.
I have some friends working in companies like Revolut and they used to tell me about the development framework used within their company. The framework is very slim and helps the developers to produce more effective code (understanding the concepts before coding) and do not use all the freebie objects strangling around in those fatty libraries. They do strict code checks and have detailed discussions before anyone commits new code into their existing frameworks or libraries to prevent it from being another fat jar. Of course, this kind of development strategy will make you see a lot of duplicate code in the services lying around.
When we talk about the library, I want to see something small with a maximum of 5 files in it. And doing just one thing and only built for serving one single purpose. But most of them start by doing one thing but then end up supporting a hundred other things with hundred classes and around 10k lines of code. Often I think about having libraries and frameworks helping us via GRPC or maybe extreme cases acting like a microservice. One fine example would be Zalando's Nakadi which has a very similar implementation for event streaming.
Extreme Programming as a methodology for example tells us to have minimum static documentations and have only detailed documentation that will be generated dynamically on every release. Up to an extent, this avoids having outdated pieces of information on what the service does. I agree with this and likes to see libraries with brief documentation on what the library does, what are the side effects if it has any, FAQ's, etc...
I believe after talking about documentations, "real test cases" should be the next ideal sub-topic. Having some test classes or files to depict the working scenarios will help us understand what should be expected as the outcome and what should be provided as input. In this way, "copying and pasting" (developer's favorite) the sample code from the test cases would also make things super easy and reduce the time spent to integrate.
Some libraries even go to the extent of making us developers sit idle and do nothing. I hate to use these libraries. Once we integrate them into our service, they start creating and initializing hundreds of objects and among them, only two of them would be relevant to us. Ninety percent of the time, developers don't care about starting the application in TRACE or DEBUG level to visualize what kind of objects are being created or lying around and do we need them, etc... We only start to analyze them once we end up in OOM error :)
Finally, I would like to emphasize a couple of things. Only include what is necessary and do not accept the big package which is delivered to you for free. Integrating would be easy and we end the day thinking of us being productive on finishing the task.
But the reality is you could be making your colleague's life harder soon. This write-up is not to say all the libraries are bad and is just a waste of time or criticizing some one's excellent job. I was spitting out my feelings and some hard realities I faced in my past nine development years. Ending in a positive note, I do adore some libraries which are supposed to do one thing smart and efficient :).