blog.kronis.dev


It works on my Docker

Date:

I like the concept of containers and I like Docker. Despite jails and LXC also having been around for a while, Docker has really nice DX and no wonder that it's pretty widely used nowadays (as well as the other OCI compatible solutions).

It's a pretty good way to get rid of some of the environment specific requirements and also have a consistent way of packaging applications, giving and limiting their resources, providing configuration and all that. I use it to run most of the software on my servers and some of the software locally nowadays, when it works, it's great.

However, they are lying to you. The core premise is that if you build a container and it runs, then the same container with the same configuration will run elsewhere, for example: locally, on your CI server and also on your environments.

That is false.

There is no software reproducibility in the real world (your enterprise Java app at $DAYJOB)

Have a look at this, I have an application that runs locally, in a container that I built:

01-app-is-running

Then, when the same Dockerfile is used to build it on a CI server, when deployed on the environment, I get a circular dependency error:

02-circular-dependency-error

You might think that there are configuration changes or something else that is causing this issue. Nope, because if I change the configuration:

03-configuration-changes

Then the error also changes:

04-another-error

The error itself is cause by working on a legacy project that has circular dependencies, but Spring Boot gives you a way around that until you can fix the code (that time hopefully not being never), where you can enable that configuration, as mentioned above:

spring.main.allow-circular-references=true
spring.main.lazy-initialization=true

Of course, that does nothing for explaining why it works locally in a container, but not in the same sort of container if it's built on a CI server.

What a mess

And honestly? I don't care.

If it doesn't work, then it doesn't work and no amount of appeal to complexity of modern software will make me not hate this.

That said, I don't hate Docker. I hate Spring Boot.

Maybe not for creating a viable alternative to the likes of ASP.NET but in the JVM ecosystem, that part is actually really nice, especially because of all the integrations and how productive it can make you when things work. Maybe not even for the performance impact it has compared to some of the other options since the likes of Dropwizard can be slower and I still enjoy those, since productivity and ease of use do often trump raw performance.

But definitely for their approach to dependency injection, where a lot of it is done at runtime, as well as all of the proxying garbage that goes on under the hood, often ruining not just your stack traces, not only your transactional code, but also plenty of your sanity along the way. If you have a framework that throws runtime errors for code that you could have checked at runtime, you have largely failed to create a good DI system.

Oh, and before you say:

Just fix the code

I'd love to do that, but I have no idea how much time I'd need to do that across the whole codebase that is approaching a million lines of code. Plus, guess what, Spring Boot throws one error at a time, once you compile and launch the app, so that would be a lot of iteration and time, which I don't exactly have in ample amounts.

Summary

I'm not even sure why the likes of Dagger didn't catch on, but Spring DI is the one thing I absolutely despise, to the point where it makes me think more poorly of DI in general. Never believe when DI frameworks promise you that they'll make your life easier, because sooner or later their dynamic nature will burn you.

Maybe we still should ship my laptop, since it works there.


Other posts: Previous »