Building software at scale is tricky, and when you have lots of software, you have more problems to solve. Several times engineers look for ways to make their lives easier: make it simple, transparent, and easy to use. That sounds like a good idea, right? What can go wrong with that?
Typing is never the bottleneck and will never be. We are all obsessed with reuse; several times, the reuse is marginal and brings way more problems than benefits. Working with an Object-Oriented Language such as Java makes a lot of sense to leverage Dependency. Injection and Ioc(Inversion of Control). Because it’s easier to test the Application and also easier to change the Code.
In the past, Spring Framework was all about XML; today, all configuration is pretty much done via Code. It’s possible to use property files to configure some aspects of backend services written in Java with Spring, yes, but in reality, most of the Code is in the Code in the form of Java Spring Beans.
Spring Framework allows you to use annotations such as @Conmfiguration, where we can have a bunch of @Beans and multiple java files for configuration. The issue is when we build libraries and want to make the engineer’s life easier by pre-hooking all spring-beans configurations for them.
@Configuration and Shared Libraries
We have a contention point when we have shared libraries, like a significant bottleneck, more libs, and more bottlenecks. Why? Because libraries have binary coupling with Services, it does not take long to be in the position where changing the libs is expensive; updating all other services and/or libs is neither easier.
But why it’s terrible? Well, it’s bad because at some point is not hard to lose control and pretty much have coupled with configuration. At the point that if a Spring Bean changes, you can break several libraries and services.
It’s possible to do configuration testing; most companies do not do it. Because spring has a Runtime nature, you only will see problems when you boot up the spring context and reach some parts of the app. You end up in a scenario where you only will see the issues if you write some smart configuration testing or expensive integration test. This could not easily happen, leading to several problems at runtime or upper environments, meaning a high feedback cycle and less agility.
Engineers dont want to break other engineers’ codes (not by design), so what happens is that people end up copying and pasting configuration does not take long before this becomes a mess. Now it becomes tough to reason what’s going on and what configs are needed or not.
Services need to Own their Configurations.
Shared libraries should not bundle pre-hooked configurations(Spring Beans @Configuration files). This needs to be done by consumers on their side. There are plenty of reasons why it’s better to do this why, such as:
- You understand what’s going on and how your service behaves.
- During Migrations and Refactorings, there are fewer changes config changes will break you by accident since you own them now.
- IF different services tweak, their configs will not affect each other; therefore, you get isolation and more autonomy.
- You can cherry-pick what you need and therefore reduce configuration complexity.
- Shared-Libs changes can be dealt with in isolation for each service, allowing more paralyzation when change needs to happen.
Of course, this creates a small problem: Ok, so how do I know what to do now? IMHO this is not a problem but a feature. As engineers, we should understand what we are doing at all times and not take anything for granted. How can we improve this?
- A good wiki page with documentation.
- We can record videos explaining how to configure and what options we have for libraries for consumers/users.
- We can leverage reasonable defaults; therefore, much less config is required.
- Unit Tests can teach you how to configure and/or use a library.
If something is so hard to use, Configurations can be a big smell. They are pointing out that we have an API Design Problem. I would fix the API Design instead of shipping shared configs with shared libs.
Isolation is the most crucial property of a Service(no Matter is SOA or Microservices or Monolith, Isolation matters the most. Configurations should be isolated as well.
Originally published at http://diego-pacheco.blogspot.com on June 8, 2022.