Microservices tip for this: Don't put code into a shared library without careful consideration of the consequences. Even if it is used in multiple services, consider duplicating it instead, and compare the risks and trade-offs compared to putting it in a shared library.
Common scenario:
New developer: "I'm done refactoring the implementation of the BingBong class in the shared library. It was a lot of work to change and test all the frobnicateXxxx methods!"
Manager: "Wait, I thought none of our services do any frobnification?"
The old hands discuss: "They don't. Not anymore." "Are we sure? Perhaps the Foo service we haven't touched since last year?" "No, the Foo service never did." "Let's search the repos. Maybe we can delete some of these methods." "This is weird. We have seven different frobnicate methods, and we never needed to support that many different kinds of frobnification." "Of course. It's such a pain to roll out a new major version of a library, you don't want to change anything existing." "Do you remember when the rendering service was stuck on version 3 of the auth library? It was because we removed a method from the auth library and rolled out 4.0 without it. Turns out it was being used on a feature branch in the rendering service that got merged right before the release." "I remember. I'm the one who had to roll out version 4.1 the next day with exactly the same code as version 3.7."
There's a simple way of understanding how stories like this happen, and a simple conclusion: Once functionality is added to a shared library, changing it requires much more care. Therefore, shared code is much more expensive to maintain than non-shared code. This is why it makes sense to ask a question like, "Which is cheaper in the long run, maintaining three non-shared copies of this code, or maintaining one shared copy?"
Yup, I'm a big believer in applying the rule of three ( https://en.wikipedia.org/wiki/Rule_of_three_(computer_progra... ) for moving code into a library as well. I find if I create an API with just one or two examples in mind, it often doesn't turn out to be as general as I thought it would be. By the time I've done something three times, I have a much better idea of what the different use cases will be.
Common scenario:
New developer: "I'm done refactoring the implementation of the BingBong class in the shared library. It was a lot of work to change and test all the frobnicateXxxx methods!"
Manager: "Wait, I thought none of our services do any frobnification?"
The old hands discuss: "They don't. Not anymore." "Are we sure? Perhaps the Foo service we haven't touched since last year?" "No, the Foo service never did." "Let's search the repos. Maybe we can delete some of these methods." "This is weird. We have seven different frobnicate methods, and we never needed to support that many different kinds of frobnification." "Of course. It's such a pain to roll out a new major version of a library, you don't want to change anything existing." "Do you remember when the rendering service was stuck on version 3 of the auth library? It was because we removed a method from the auth library and rolled out 4.0 without it. Turns out it was being used on a feature branch in the rendering service that got merged right before the release." "I remember. I'm the one who had to roll out version 4.1 the next day with exactly the same code as version 3.7."
There's a simple way of understanding how stories like this happen, and a simple conclusion: Once functionality is added to a shared library, changing it requires much more care. Therefore, shared code is much more expensive to maintain than non-shared code. This is why it makes sense to ask a question like, "Which is cheaper in the long run, maintaining three non-shared copies of this code, or maintaining one shared copy?"