Hacker News new | ask | show | jobs
by exabrial 1549 days ago
I'll chime in my opinion: Java is getting something right that I don't see much elsewhere: Dependency Injection. Combine this with a mocking framework and you can write _actual_ isolated unit tests for every single part of your stack.

We're fans of CDI, it's a more polished Spring framework without the legacy weight. We're developing in Quarkus, MicroProfile, and bigger monoliths in Apache TomEE. We use ActiveMQ extensively for scaling. ( And I mean extensively... on a modest 512m server, we can push several thousand messages/s reliably to a _lot_ of topics and queues, all with delivered-exactly-once guarantees)

We avoid the fanfare of Docker, as really it's not needed for Java apps; they're somewhat self-contained anyway and it created more problems than it solved. For true isolation, we use systemd to create cgroups and chroots and prevent application escapes. For deployment, apps are one-jar'd down to a single executable, then packaged up in a .deb using the jdeb maven plugin. We stick with the unix philosohpy of using /etc/default for env variables that help the app locate their database or LDAP cluster.

7 comments

I've been a developer over a decade, never touched the JVM because I always figured it was bloated Enterprise garbage.

I did a project using GraalVM's polyglot abilities and needed an API.

Tried Spring, Micronaut, Helidon, Ktor and Quarkus.

Quarkus is the best web framework I've ever used, in any language. Can't go back now.

It's so well-architected that I was able to contribute an extension for Scala 3 support within a month, and the Redhat employees have answered every question + issue I've raised.

Being built on top of the Microfile spec and using Vert.x is a hell of a combo. Vert.x is the best thing since sliced bread too.

Can't recommend Quarkus enough, whether you write Java or Kotlin or Scala (I'm a big Kotlin fan myself).

Just had a look at your scala 3 extension, it's really nice. Does it support hot code reload like you would get in quarkus dev mode with java?
Yes, that's correct =)

Admittedly the install instructions for Gradle are a bit dated. Gradle 7.4 shipped with Scala 3 support.

While you are correct that DI is very good in java, DI is needed because of heavy reliance on OOP (i.e, in order to create an instance of an object, you have to lock in functionality with a dependency before you can run that functionality).

If you use a language that doesn't require OOP, you can do the same isolated unit tests without DI.

I want to expand on what you said: It's more than just OOP, or not necessarily OOP in general that needs this type of DI and IOC.

It's a specific category of OO architecture, where effects and state are buried under domain logic and information processing (via DI). If you keep that part data oriented and pragmatically functional, then you get a very testable core. Meaning no mocks, stubbing setup/tear-down (...) are required. No additional interfaces have to be specified that you didn't need in the first place. Tests are way simpler this way.

You can still use objects, interfaces and composition etc. for domain logic and information processing if you want. Point is you lift state and effects up, so this stuff happens at the edge of your program that calls into a simpler core.

> Java is getting something right that I don't see much elsewhere: Dependency Injection. Combine this with a mocking framework and you can write _actual_ isolated unit tests for every single part of your stack

Since dotnet core started this has been front and center in C#. So there are other stacks doing this.

Very happy to see this. I think it's a great pattern. It pushes functional concepts into code and stuff like state management into the runtime.
I prefer to skip the mocking frameworks and rely on Interfaces instead. My unit tests create anonymous inner classes that are passed to the constructors.

If it takes too much effort to write that anonymous inner class, then the Interface may be too big and need broken up. Or maybe you're hiding too much state and need to rethink the API.

Containerization is great. It allows you to write all the infrastructure for you Java app as code and then just simply type 'docker-compose up' to get it running (or whatever your orchestrator).

This makes it super portable.

Just something to think about.

I personally think the win's from Containerization are greater than the challenges.

I admit we do miss that part. Instead for us it's `sudo apt -y install app-name`
installing the app locally isn't as sustainable. The data and configuration for test environments gets scattered across the system and is less reproducible for new comers. As changes are made to the test environment, developers have to then install/update/reconfigure manually. It becomes an nmo effort with less consistency and more error over the single line docker-compose up
Don't disagree, but we just avoid writing stuff where you need to have 10+ apps running locally to run "an app". That itself isn't sustainable architecture, those should be one app in a monolith.

That being said, to your point, we do have some inter-service dependencies that have to run separately. We haven't figured out a good balance yet, but it's the exception not the rule fortunately, and everyone "just knows" how to run an extra app in those extreme cases.

do you have a 'sudo apt uninstall app-name' to clean everything up?
Yes! In fact, an upgrade path too in the script.
Sounds great as long as you either put work into multi OS compatibility or stick to the same OS.

Depending on your situation that sounds like you have a good solution that's working and you're happy with.

You don't necessarily need containers for that though?
Containers offer a lot of advantages, portability, IaC, OS abstraction, common predictable environments, etc.

I personally like them a lot but it's definitely not the only way to do things.

The alternative would be hosted functions, like lambda, which is just running in someone else's containers.
"something right that I don't see much elsewhere: Dependency Injection. Combine this with a mocking framework and you can write _actual_ isolated unit tests for every single part of your stack."

It's exactly the same in the .NET space

> We avoid the fanfare of Docker, as really it's not needed for Java apps; they're somewhat self-contained anyway and it created more problems than it solved.

What does this mean?

Because the code runs in a VM already, they are not concerned about isolating further.

For example, if you're writing code in C++ targeting Linux, using Docker helps with reproducible builds and deploying on any hardware.

But if you have a VM, there are less advantages. There still are some, of course, with orchestration etc if you want it.

I guess I was implying that I don't view the benefits between the prior and the later as an onto mapping.
Java apps have been "cloud ready" in a sense for about 20 years. JavaEE had a "WAR" or web archive file format that packaged an entire app up into a single file that could be deployed in an "Web Application Server". IBM wanted web servers to be mainframes, but for webapps. You could even deploy multiple apps in a single JVM, because the server would load each app in a separate classloader, meaning the apps were internally isolated from each other. The JVM even had an attempt at a cgroups like concept, called a Security Manager (that was abandoned, thank goodness), long before cgroups existed. Think WAR file = docker container.

Now, a lot of complicated bits were inherited that didn't need to be there when they brought over the mainframe thinking into Java apps, but the WAR format was actually something kind of cool they got right.

We in particular, take that WAR format, turn it into an executable, and we have a program that can execute on any hardware on any person's computer, with no pre-reqs except a JVM being installed. We test the _exact_ war file on a ARM RPI , developed on a Mac m1 or x86, and CI on a Linux x86.

Alright what does this mean? Lets take a look at another language...say a PHP application.... which may require extension Unix utilities of certain versions installed on the host system. You may need a certain version of PCRE grep, a certain version of curl, etc. This theme carries over to multiple languages, your python or ruby implementation may not support the latest version of libressl, or it might not even support your ABI at all. Docker solves these problems gracefully, by providing a fully configured virtual operating system tuned for running exactly one app.

The JVM... well doesn't need that, if written properly. Generally all the libs it uses are written in Java, they include all of their dependencies already, apps can be isolated with classloader boundaries (if that's your thing), you can download a single file and launch it if you use the "one-jar" technique.

So yeah, Docker loses a lot of it's luster with Java. It's not that you _cant_ use it, but it you can solve all the problems it does otherwise. Long rambling explanation but there you go.

I think it means that they like to install software and manage servers rather than renting a container cluster to host your app.