16 November 2025


Currently I'm developing a service in Javalin (later on that) and I thought it's a good idea to give service admin a companion tool for interacting with files, passwords and other entities.
Well, you might ask, Nadiyar isn't this a web service with a front-end and authentication mechanism which gives authorized user special permissions? Then why another UI? If so, you're not alone someone actually phrased it like this:

Again, this doesn't make sense. If you are doing a web service, web apps have no CLI. They don't even support stdin/stdout/stderr.

The reason is I like the idea to separate the functionality between web and cli, because first it simplifies the web interface, and second we'll have fewer online API endpoints; but more importantly I'm looking for a reason to have new experiments.
I really love to explore other areas where you can use Java and also write my first CLI application. Sounds cool right?
spoiler alert: It is!

I had lots of fun doing this and had to make some decisions and answer some questions along the way, which we will review here.

But doesn't JVM take 3, 5 or 30 seconds to start?

It's true, Java a language originally designed for embedded has evolved to be the king of back-end development over the past 30 years.
Originally Java uses a JIT compiler which is perfect for long running applications, it dynamically optimizes code so you get near perfect performance but with a huge caveat, it increases startup time massively.
But this is not how CLIs (or any UI) should behave, they must be responsive and instant, so what's the solution? You guessed it, we should ditch the JVM and its JIT.
It's about language duh, not the VM :)

Fortunately beside OpenJDK, there several other compiler tool chains for Java, including OpenJ9, Codename One and GraalVM. Each has different characteristics and purposes but for us GraalVM is the most interesting one, since it provides AoT compilation and compiles Java code into native binary.

Using Graal we can compile our CLI and get instant (<50ms) start-up time. Of course it means we're losing the JIT optimizations and other benefits of employing a VM but it doesn't matter since CLIs execution period very short.
You can read more about Graal's native-image learn more about how applying it is different than traditional Java.

Writing everything from scratch is exhausting

Yes? and that's why we're not doing this.
Even though Java has never been a popular choice for writing CLIs you have a admirable library on your fingertips: PicoCLI
Pico is a production ready piece of beauty and has been under active development for more than 8 years which supports annotation-based, Programmatic and mix-and-match (hybrid) programming models thus enables you to build a functional solution in a few minutes while not losing on control.

TIP: you almost always want to use annotations unless you have a good reason not to

PicoCLI has first-class but not out-of-the-box support for GraalVM. Means you can AoT compile your code easily but it needs a little configuration, usually you only need picocli-codegen in your class path.
Further information can be found here.

Bang! Bang! Jbang!

This is not exactly a necessity for our small project but it's a good quality of life tool to know about and consider.
In one sentence, Jbang makes compiling and executing Java the easiest thing in the world.
It can be installed anywhere, manages dependencies and JDK versions, don't need configuration files or specific project structure. You can even run Java in Jupyter Notebooks and it works like a piece of art!

Of course it means you lose the robustness of Maven or Gradle like build scans, multi-module project support or incremental builds, but that's not an issue when developing a CLI. At least in similar situations to mine where re-engineering the existing project structure and tooling was a heavy task.

How this work together?

OK so I'm not trying to teach you how to write a CLI app or how to use any of the tools mentioned above; In this section we're just looking at demonstration of a workflow that worked for me.

First install Jbang via SDKMAN:

sdk install jbang

Then initiate a new project using Jbang:

jbang init --template=cli NewCLI.java

WARING: you can create a file without the .java extension but there's bug you should watch out for

Edit the file in your favorite text editor (Neovim in my case):

nvim `jbang edit -b NewCLI.java`

And add these two lines somewhere on top of your Java file (not the first line) also make sure to replace all the Xs with the corresponding PicoCLI version:

//DEPS info.picocli:picocli-codegen:X.X.X
//NATIVE_OPTIONS -Ob --no-fallback -H:+ReportExceptionStackTraces

Now you can build and run a native image of your CLI:

jbang run --native --fresh --build-dir=. NewCLI.java

Congratulation! that was it.

Final thoughts

Now we know it's possible and easy to develop a CLI application in Java.
A tool for each one of your needs is always there and you can finish the projects that are on your mind using the language you're comfortable with.

So there's an ecosystem :)
Is it perfect? no
But you'll definitely have lots of fun with low frinction using it.