Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Show HN: Minum – A minimal Java web framework (github.com/byronka)
165 points by 7ep on Sept 25, 2023 | hide | past | favorite | 100 comments
I am happy to announce my minimalist zero-dependency web framework, Minum, is out of beta.

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)



The size statistics page is super cool: https://github.com/byronka/minum/blob/master/docs/size_compa...

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.


I did a survey of light frameworks and settled on the Javalin + JDBI (from DropWizard) combo. That was at a Spring/Boot shop where we needed something with very short startup time for rapid scaling.


> Reasoning this way about software and dependencies more often seems like a good thing

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...


3,757 what? KB, MB, GB, TB, PB, LOC, hours to download?


On that page it says:

> (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.


LOC + Dependency LOC, it seems.


> 3,757 what?

3,757 of reading comprehension.


LOC


I can see clear value in a framework that minimizes dependencies. The choice to supply both a web framework and a persistence / database layer puzzles me somewhat. I understand this can be the basis of an "everything included" framework, but I think especially for persistence, most would like to make a conscious choice independently.


There’s nothing to prevent using any external db if desired. In fact i’m doing that for a client.


Sure, but do you want to elaborate why you included it?


in case you truly want to go as absolutely minimal as possible. i do. for my own personal projects, i use that database.


One suggestion I have would be to include a very minimal implementation of login management (basically just properly hash/salt passwords and provide some easy way to add users). I've used Django before for the sole reason that it includes a database and user management because some API had to be login only since said API had high resource usage.

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).



Why is this under src/test/java?


Because it's not something within the Minum framework. It's an example implementation that sits outside of it.


What does ‘minimal’ even mean though? Code is code - either you pull it down as a dependency or you write it yourself, it all executes the same in the end.

The 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.


> What does ‘minimal’ even mean though?

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.


> Code is code - either you pull it down as a dependency or you write it yourself, it all executes the same in the end.

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?


I'm reading it as "minimal in scope". Scope creep tends to be a given for established/battle-tested libraries, and I'd argue that over time scope creep could counteract security benefits of being battle-tested.

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.


This is definitely true. The more complex the library is, the more chance for a vulnerability to happen. And combining 2 independent low level vulnerabilities may end up being a critical vulnerability.

The same may happen in your project, but if you use simple(r) dependencies, it's less likely.


Awesome, love to see more Java devs pushing back on the spring madness.

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?


There are locks around those - for example, writeLock


I mean race conditions between the operations, e.g. an update followed by a delete:

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.


great find. i’ve got some adjustments in mind, mainly carefully placed locks. i’d like to give you credit for the call out in the docs/acknowledgment file. is there some better way to reference you than jabradoodle? the code i’m considering is in the branch under_consideration.


Thanks that is kind of you. For the time being I would rather not tie my real life identity to my Internet identity.


hmmm. it does seem possible. Let me look into that, thanks!


Good job on coming out of beta. It's crowded out there so best of luck!

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.


Congratulations!

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.


I'm thinking of this as a nice programming exercise for the author. As for being a framework which promises more with less, that only holds until feature x is required.


a perfect example of the thinking behind most frameworks. I don’t think most people are looking for minimal. For those that are, this might be right up their alley.

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.


All a stock JDK lacks to handle this out of the box is a JDBC driver (which it has innate support for), and a template engine.

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.


Modularism is my favourite. I am enjoying using ExpressJS of all things, because it comes bare bones but you are a few mix/match middlewares from having it set up as you like. And the process of setting them up is not a chore, it is valuable, you now know how your thing works. Whereas frameworks give you the kitchen sink behind a facade that makes most happy paths easy. But eventually you have to deal with that complexity one way or another, and usually it is by hacking together stack overflow and github issue answers :-)


Your kind of playing a little fast and loose with the term 'database'. You've implemented a naive persistent cache. (Don't get me wrong, I have too at times. they can be quite useful.)


I'll accept the naive mantle. But is this not a database? A database is an organized collection of structured information, or data, typically stored electronically in a computer system.


I didn't see looping support for templating, like for rendering collection to HTML table. Did I miss something?


You did not miss anything. It lacks that functionality, making it necessary to template the inner part, and then use that within an outer part.

Fully-featured templating libraries provide this in the form of special syntax. This does not provide that level of capability.


I see. So you intend to make templates logic-free. Perhaps you can implement like xtemplate that I used to use last century

https://eeperry.wordpress.com/2009/08/08/php-xtemplate-the-b...


Why does almost all the Java based web development framework use "template" system, instead of generating HTML from code(like Flutter/Swift)? Is this due to the inherent limitation of Java language/syntax?


In order to make this not extraordinarily painful, you need some good language support for internal DSLs, or a template language / preprocessor, both of which Java doesn't really have. You could still do it somehow, but it probably wouldn't be the nicest experience. Plus, historically, the Java community has been in love with XML, so you'll find lots of templating libraries based on XML.

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


>instead of generating HTML from code

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.


Just a note about the “.do” extension: if I correctly recall, it was introduced by struts framework which used “Action” as a naming convention such as Spring uses “Controller” as suffix, and so they used “.do” as extension.


I've never used 'struts' personally, however mapping servlets directly to ".do" was a recommendation (can't quote books any longer, though - it has been well over 2 decades)


I wonder why more java frameworks don't reuse the parsing from JSP, but just drop the servlett part. Though maybe that's not as easily done as said.


JSP literally compiles/translates to Servlets, as in extends them in the code, and everything you see is in their 'service' method effectively. JSP can consider JSP alike to C's preprocessor.


JSPs are implemented by transpiling them into servlets, so JSPs without the servlets doesn't really save anything (assuming you want to re-use what's out there).


The author has written their own HTTP server rather than using the one provided with the JDK. They need to have a really, really good reason to do this, and i haven't seen such a reason reason documented anywhere.

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.


> You will be hard-pressed to find another modern project as obsessively minimalistic. ... This follows self-imposed constraints, predicated on a belief that smaller and lighter is long-term better.

the reason: obsession and self-imposed constraints.


Your run of Apache Bench only specifies -c20.

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?)


One way in which this could be slightly more minimal is to remove the mvnw wrapper.

Honestly if you want to do development on a Java/Maven project, I don’t think it’s unreasonable to have Java and Maven installed. The wrapper adds three directory entries to the top-level repo directory, and is lots of copy/pasted code.


It's absolutely unreasonable to expect that, and the wrapper should stay. I don't have Maven installed anywhere, and i don't have Gradle on the global path, because i work on projects which require different versions of Gradle.

Saying that removing this would make the project more minimal seems deeply confused to me. Minimalism is not measured in the number of directory entries at the top level, but by the amount of stuff the user has to understand. Taking the wrappers out does not reduce that.


it’s all a balance. i’m obsessive, yeah, but i’m also trying to have a bit of balance for the bigger picture to each choice. if you look back a few commits you’ll see where i wasn’t even using maven, just GNU Make! But i was convinced over time that taking a more conventional route for that would yield better returns.


There is a severe lack of minimal web frameworks available in Java so I appreciate the effort behind this project. Would it be possible to separate the database and templating packages into separate libraries? For example, could I used Minum with JDBI for the db library and Thymeleaf for templating?


yes, certainly you could.



This is really cool to see. Congratulations!

The existence of a project like this can lead to feedback that can improve other web frameworks and the JDK itself.


Name same as a python visual framework closely related to 3b1b. One may say it is apple record vs apple computer.


To anyone developing libraries: please, for the love of god, just put a damn code snippet right at the top. A simple hello world would do.

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.


I was thinking the same thing so I dived in the example. And now I understand why there wasn't a code snippet in the README: even a minimal app seems to requires multiple files in many subdirectories. I guess java stays Java, even when using a "minimal" framework.


Click the giant "QUICKSTART" link and you'll get what you're asking for.


I agree with the OP: you shouldn't have to click anything to get an idea of what it even is and whether it's remotely interesting to you. To me, "quick start" implies that my interest has been piqued enough for me to dive in further. If you can't convince me to continue reading in the first ~10 seconds, there's a high probability that I'm closing the tab.


It's near the bottom of the page. It's admittedly a very little bit of with, but other frameworks don't make you work as hard.


I think I gave it 3 seconds.


Am I missing something or does the quickstart recommend an XSS vulnerability?

    String name = request.startLine().queryString().get("name");
    return Response.htmlOk(String.format("<p>Hi there %s!</p>", name));
Maybe having a little bit more available would be valuable.



Making the user manually escape parameters (and be very careful not to miss any) seems at odds with the goals of ease of use and maintainability. Most template libraries do this automatically by default.


You are not wrong. Just, here, priority is

1. minimalism 2. ease of use

It's an uneasy balance. I am well aware of the value of fully-powered templating engines.


For the record, I love this project's minimalist goal. Having said that, I agree with the other commenter that escaping by default (with an opt-out mechanism) is probably the better choice.

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).


Interesting point. Definitely worth considering. Thanks.


Looks like that should be the example rather than promoting unsafe coding.


Ah but its "minimal".

Taking the web back to its roots.


Names.

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.


Agree. Looking at the example project, this is not a single case, when a name could be better. Helpers and Utils are naming anti-patterns, so naming function ˋ(de)serializeHelperˋ is not a good idea.


Names. One of the two hard problems in computer science, along with cache invalidation. And off-by-one errors.


Valid point. I was going off documentation like here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#s...


That page uses Method though, and places nouns at equal importance with verbs when describing them?


I built my bread and butter with Java and Spring for ~20+ years, and I can safely say whilst commendable to reduce the bloat, it's the language itself which is bloated af.

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.


Java is 28 year old, Typescript is 10.

Give it another 20 year and similar patterns will emerge as the language matures.


True, but imho at some point people need to let go of legacy code because of the maintenance headache and start again, same is true for the whole eco system. I fully expect if I'm still alive in another 10 I'll be choosing something other than TS as a main language (fwiw I still do Java).


I like this Java movement back from overbloated frameworks with decades of cruft.

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.


How is maven nowadays?

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?


Maven is always more or less the same - you have properties, dependencies, build (plugins), profiles, maybe modules and dependencyManagement if you have multi-module build. That's it, every project is the same.

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.


It usually ‘just works’ on your side. Configuring it properly is also fine, but a bit more involved.


Congratulations. I believe there is room for a micro framework that is smaller than Javalin or SparkJava.

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/


it is my first time seeing java web framework project on HN. I am sure that this is a great project but for the love of god, just let Java die , please. There are plenty of better alternatives to choose from (nodejs,go, python, elixir, clojure, Ruby etc)


I chose Java for one of the microservices that leverages JTS library, which was the tool I wanted for the purpose. I wanted to use normal Java, because the thing I needed to do was very simple. It works very well. Used Micronaut by the way.

Java has its place.


[flagged]


Actual Java dev here, not just someone who used it in college and got annoyed -- but I have no idea what you're talking about. Java has not been boilerplatey for a long time, I'd say since sometime between Java 5 and Java 8.

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.


> Java has not been boilerplatey for a long time

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.


> The real nice thing about Java is everything is statically typed

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.


I have never ever ever seen a null inside an Optional -- the most common constructor actually Optional::ofNullable makes the correct decision, so you can't even fck it up, and if you deliberately try to, I'm sure intellij would scream your head off.


The reason you've never seen it is because it won't let you,

private Optional(T value) { this.value = Objects.requireNonNull(value); }


The problem is whether the optional itself is null, not whether the contained value is.


That’s not a problem either.


Yep, this is a great point, and while these days people use stuff like @NonNull/@Nullable, it does not help a lot, for the exact same reason.

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


You're correct ... in theory.

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,... ?


Well, I can't speak for how others write Java. It's like any other language -- if a function or file or class is growing too big and complex, you split it up. If you have several that are too tiny and they are logically related, you can group them together.

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.


I think the verbosity of Java is all in your head


I prefer verbose code over unknown defaults. If I need to comeback and read that code again, I'll know exactly what is going on at first glance.

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.


Yeah if I wanted to write for a JVM, I'd use kotlin


Interesting you say that: https://github.com/byronka/r3z

Basically the same thing as Minum, but in Kotlin.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: