Show HN: Minum – A minimal Java web framework (github.com)
http://github.com/byronka/minum
You will be hard-pressed to find another modern project as obsessively minimalistic. Other frameworks will claim simplicity and minimalism and then, casually, mention they are built on a multitude of libraries. This follows self-imposed constraints, predicated on a belief that smaller and lighter is long-term better.
Caveat emptor: This is a project by and for developers who know and like programming (rather than, let us say, configuring). It is written in Java, and presumes familiarity with the HTTP/HTML paradigm.
Driving paradigms of this project:
* ease of use * maintainability / sustainability * simplicity * performance * good documentation * good testing
It requires Java 21, for its virtual threads (Project Loom)
112 comments
[ 2.9 ms ] story [ 191 ms ] threadThe minimal/zero-dep solution will have have bugs that have already been fixed or avoided by battle-tested libraries, and it will very rapidly lose minimal status as you start to build something serious in it.
It doesn't include the batteries.
The batteries - extra features - weight you down if you don't need them. So you'd better be able to avoid them. Large projects can be hard to audit to remove things which could be nice and often useful - but just not in your case.
Smaller projects have an advantage that you can easier understand them and decide what you need to add and, crucially, how to add that better, from your personal viewpoint. It's really hard to have nice, tiny, composable building blocks, so we have rather few attempts at that. Actually, a big part of programmer's education is teaching him how to properly write something which is well known, just have many small variations which depend on both requirements and some preferences. With a smaller project you can see those choices easier - and have better chance to correct them the way you need.
> The minimal/zero-dep solution will have have bugs that have already been fixed or avoided by battle-tested libraries
Maybe. Maybe not - you need to have all branches of execution checked, and with all possible input data combinations - and then all problems to get noticed and, crucially, corrected. Some problems, like unnecessary delays, can avoid e.g. representation in the logs.
On the other hand, with smaller projects you have less moving parts and better visibility where, and how, things could go wrong. If you want ultimate reliability with proofs, you'll find it easier going with smaller projects.
So, as they say, there are programs so simple they're obviously correct and so complex it's not obvious which errors they have.
This looks like an oversimplification sometimes. You surely can solve the same problem in programming in different ways. An important part could be unstated assumptions you relied on when you wrote your code - and sometimes it's really important to understand those assumptions, or the code could strangely misbehave. "All executes the same" could literally be not true - some testing may suggest that, but more expensive testing, which is often not done, could show that for some unusual combinations the behavior is different.
How do you know it's the same?
Note that I'm not basing this on data, just on my intuition that a lot of major security bugs seem to be related to obscure/infrequently used functionality.
The same may happen in your project, but if you use simple(r) dependencies, it's less likely.
I can see how having your own database is kind of "minimal" in the Java sense of the word. You don't need to install 50 different packages just to make a simple web app. There are minimal frameworks in the sense that they only provide a basic web server, but those are so minimal that you are forced to install a bunch of packages. It's minimal in the sense that create-react-app was minimal (RIP).
https://github.com/byronka/minum/blob/master/src/test/java/c...
https://github.com/byronka/minum_usage_example_mvn/blob/d649...
1. minimalism 2. ease of use
It's an uneasy balance. I am well aware of the value of fully-powered templating engines.
I don't even think this violates your prioritization because it should result in less code when considering an app in its entirety (framework + logic).
Taking the web back to its roots.
Minimalism is an interesting ideology. it’s about choosing to do more with less, and there’s a great deal of ingenuity in saying, i only have this one knife and a rope, how do i cross this chasm. so to speak.
no offense intended to the batteries included crowd. this is just something i have developed a taste for.
It has a built in HTTP server which has recently been improved. It’s also getting, in preview now, a string formatting subsystem which is not quite a template language, but it’s interesting. And, to be fair, out of the box the JDK does have a template system: XML and XSLT 1.0 support.
The HTTP server is usable, but could use some sugar. Same for the logging system, usable but could use some sugar as well.
It has TLS built in as well.
It does not have JSON built in. In todays world, that along with persistence are probably two true must haves. Mind, the JDK used to ship with JavaDB, which was a version of the Apache Derby database.
It is trivial to add the SQLite jar to a project. You wouldn’t even need a connection pool with that.
Anyway, the point being that the JDK is almost there just by itself to be capable of serving a lot of use cases if you’re willing to put in a little bit of work to make your particular work flows a bit easier to use.
The problem always comes down to naming.
`StartLine.Verb.GET`
is not a good name.
Neither StartLine nor Verb are used in that context in the HTTP spec.
The most widely used name for GET/POST etc. is Method.
The author will find that these seemingly small things will hinder adoption.
The existence of a project like this can lead to feedback that can improve other web frameworks and the JDK itself.
You do know you can declare multiple classes in a single file (static nested classes), contrary to the popular convention, right? And "public static void main" doesn't count as boilerplate, that's like one line in the whole program...
The real nice thing about Java is everything is statically typed, unlike in TypeScript where just your code and if you're lucky some of your dependencies BUT NOT ALL OF THEIR TRANSITIVE DEPENDENCIES are properly statically typed, and before you know it you've lost the nice safety and it's a crapshoot whether your type errors will really be caught, because a transitive dependency can do whatever it wants with your callback that you typed one way but it didn't care because it was plain javascript....
This is why I am not a fan of either Typescript or PEP 484 statically typed python. Typing works best when it was mandatory from day one, as long as there is some random untyped code in there somewhere nothing is really guaranteed.
Optional and generics would like a word...
Edit: OK, since this got a downvote, let me be clearer, with some help from the parent:
You just use Optional<T>, and if you're lucky some of your dependencies BUT NOT ALL OF THEIR TRANSITIVE DEPENDENCIES also use Optional<T> correctly and don't return null, and before you know it you've lost the nice safety and it's a crapshoot whether your Optional<T> will be null.
private Optional(T value) { this.value = Objects.requireNonNull(value); }
I wish Java had had non nullable types as default from the getgo, and it's hard to add now for the same reason JS/PY have a hard time adding static typing
At least in this project example code, i see tons of empty class like Result.java with empty content, on separate file.
So is this normal convention for Java dev ? No eslint (or similar tool) to prevent boilerplateness ? That's my point.
Do you Java dev have strict standard on prevent such things spread on all codebase, ecosystem,... ?
For some reason, some early java devs got the idea that you weren't allowed to have multiple classes in one file (an easy misunderstanding if you follow a Hello World tutorial and stop learning) so yeah, you'll find plenty of projects that have a bunch of class file with 3 lines.
So what? If you find this kind of mistake in a project you're working in, it's going to be very easy to refactor it safely, since Java is statically typed and IDEs like IntelliJ can use that static typing to make moving classes around extremely safe.
Considering records just made it into the language, I think "for a long time" is a very misleading description. And records only solve the problem if your type is actually immutable - which you may not be able to do if you have to interface with stuff like Hibernate which assumes that it can just mutate everything everywhere.
That's why people use Lombok - because the language itself is too clumsy.
And that's just one example. The way checked exceptions don't work well with lambdas and require manual catching and rethrowing would be another. And so on.
> The real nice thing about Java is everything is statically typed, unlike in TypeScript where just your code and if you're lucky some of your dependencies
Thankfully, there are many other statically typed languages, so you don't have to decide between Java or TypeScript/typed Python. With Scala or Kotlin, you don't even have to leave the JVM.
I've never understood the desire to type less. Yes, there's a certain amount of tedium involved. This is life. This is why it is "work". Take away the tedious elements and you'll find problems in other areas. Everything is a tradeoff. I gladly accept the comfort of verbosity over unknown ambiguity.
Actual programming is the last mile of my workflow. Why would I want to be lazy there, or cut corners? The finish line is already within sight. Identifying a problem and coming up with a solution is the bigger task.
Basically the same thing as Minum, but in Kotlin.
I think the initial README could do a better job of showcasing the framework. I think including the Maven coordinates and the quick start example instead of linking to other pages could be a step in the right direction. Check out the README I wrote in https://github.com/tofflos/undertow-examples to get an idea of what I'm looking for.
That said, if you want to cash even more on minimalism, you may want to provide a single-file example using JBang[0].
[0]. https://jbang.dev/
Please try this with Platform Threads - not virtual ones. It'd be very interesting to see the performance.
(This should just need a 1-line change of the Executor?)
In Db.java, with the write/update/delete locks, that mutate both in memory and on disk, is there not a bunch of race conditions there?
1. update x in memory
2. delete x in memory
3. update fn checks for file on disk
4. delete x from disk
5. update x on disk
Is this not possible and you end up with different state in memory and on disk.
Reasoning this way about software and dependencies more often seems like a good thing, just so we're aware of what we're actually getting into, especially with projects that use npm.
I actually hadn't heard of Javalin before, which also seems nice: https://javalin.io/
Aside from that, I've also had good experiences with Dropwizard - which is way simpler than Spring Boot but at the same time uses a bunch of idiomatic packages (like Jetty, Jersey, Jackson, Logback and so on): https://www.dropwizard.io/en/stable/
I do wonder whether Minum would ever end up on the TechEmpower benchmarks and how it'd stack up against the other libraries/frameworks there, those benchmarks are pretty interesting.
> (all measurements of lines of code are for production code - that is, non-test-code)
Given that this is Java, the author could have measured the total size of the bytecode, which is probably a more style-neutral way of measuring the fundamental quantity of code.
3,757 of reading comprehension.
except I can build the usual Twitter clone in 15 minutes in a couple hundreds lines of code, but then the question is: is it really doing the same thing Twitter does (did at this point)?
minimal is good and we all should strive for minimal amount of code strictly necessary, but I'm not sure that 73 lines of code cover every edge corner of templating that Mustache covers or that in 152 lines of code it is possible to replicate PostgreSQL...
Fully-featured templating libraries provide this in the form of special syntax. This does not provide that level of capability.
https://eeperry.wordpress.com/2009/08/08/php-xtemplate-the-b...
If you're pitching me something, show it to me. People don't have to dig around to see what you've made. I think the average visitor will give you about 10 seconds to decide if this is something they might be into, and if they can’t tell then they’ll leave.
Swift has function builders which are part of the magic that makes such DSLs practical.
In Kotlin, which also runs on the JVM, this is possible much more easily than in Java: https://kotlinlang.org/docs/typesafe-html-dsl.html
At the relative bottom (Servelet API level) it works that way, servlets have a direct access to input/output stream, and they have to write plain byte[] (or string via Writer).
As for java syntax, it's not great for string manipulation, encoding (you will need some functions for all the html/javascript escaping).
If you see url's containing ".do" extensions - that was the standard for calling servlets w/o anything, it has changed, of course.
I thought about building my own libraries as well. Don't have time for that yet.
What I don't like about this library:
Java 21 requirement. Virtual threads are performance optimization, they should not be a requirement. If I'm fine with running thread per request, that's on me. I mean, we built apps 20 years ago which did that and they worked. They wouldn't work worse today.
Everything bundled. I'm fine with mini-framework with everything, however it should be split with separate libraries.
No module-info.java. I mean, we're talking about Java 21 which is 12 releases after Java 9. Time to embrace modules.
Java has its place.
Every time I see an interesting java project, I think about using it, and then I see "maven" and I remember the terrible experiences I had with it.
Has it improved at all in the past 10 years?
In Gradle there is are infinite number of ways how to configure it. I haven't seen 2 projects done in the same way, when I want to understand a new gradle build, I need to learn few new Greadle (or Groovy) features.
Sure, in very complex Maven builds there might be some quirks and some plugins have bugs, but it works so much better than anything else out there.
Features bolted on late like function pointers (sorry lambda functions, which are effectively delegates, something they said they'd never do https://web.archive.org/web/20080127045532/java.sun.com/docs... ), too many deprecated APIs, keywords that mostly go unused and have alternatives (volatile - a CPU option for memory access with shared threads, transient - now handled by serialisation frameworks, synchronized - now handled by locks, protected - barely used in preference to package etc).
I moved to Typescript, AWS lambdas and Express framework, it has all this framework's simplicity baked in.
To other Java Devs who have made their bread and butter with the language (and perhaps nothing else), don't knock it until you try it, the path from Java to Typescript or other typed language is easier than it looks and you'll wonder why it ever took so long to knock up quick systems.
Give it another 20 year and similar patterns will emerge as the language matures.
Looks like a fun project and Java really could need a bit of minimalism. I'm always intrigued by small frameworks like Minum et al. even if it's just to learn how things are done in an approachable way.
Love to see stuff like this on HN, and I'm a bit surprised about the negativity in the comments.
Other, much larger, web frameworks provide their own HTTP servers (or bundle Jetty etc), because they can make them more scalable, or more featureful, or support protocols the JDK one doesn't. But i don't think the author's HTTP server does that.
the reason: obsession and self-imposed constraints.