Java is fast, code might not be

(jvogel.me)

100 points | by siegers 4 hours ago

29 comments

  • liampulles 2 hours ago
    Understanding algorithmic complexity (in particular, avoiding rework in loops), is useful in any language, and is sage advice.

    In practice though, for most enterprise web services, a lot of real world performance comes down to how efficiently you are calling external services (including the database). Just converting a loop of queries into bulk ones can help loads (and then tweaking the query to make good use of indexes, doing upserts, removing unneeded data, etc.)

    I'm hopeful that improvements in LLMs mean we can ditch ORMs (under the guise that they are quicker to write queries and the inbetween mapping code with) and instead make good use of SQL to harness the powers that modern databases provide.

    • Seattle3503 17 minutes ago
      > I'm hopeful that improvements in LLMs mean we can ditch ORMs (under the guise that they are quicker to write queries and the inbetween mapping code with) and instead make good use of SQL to harness the powers that modern databases provide.

      Maybe we can ditch active models like those we see in sqlalchemy, but the typed query builders that come with ORMs are going to become more important, not less. Leveraging the compiler to catch bad queries is a huge win.

    • j-vogel 2 hours ago
      Author here. DB and external service calls are often the biggest wins, thanks for calling that out.

      In my demo app, the CPU hotspots were entirely in application code, not I/O wait. And across a fleet, even "smaller" gains in CPU and heap compound into real cost and throughput differences. They're different problems, but your point is valid. Goal here is to get more folks thinking about other aspects of performance especially when the software is running at scale.

    • cogman10 1 hour ago
      Easy to get wrong as well.

      There's a balance with a DB. Doing 1 or 2 row queries 1000 times is obviously inefficient, but making a 1M row query can have it's own set of problems all the same (even if you need that 1M).

      It'll depend on the hardware, but you really want to make sure that anything you do with a DB allows for other instances of your application a chance to also interact with the DB. Nothing worse than finding out the 2 row insert is being blocked by a million row read for 20 seconds.

      There's also a question of when you should and shouldn't join data. It's not always a black and white "just let the DB handle it". Sometimes the better route to go down is to make 2 queries rather than joining, particularly if it's something where the main table pulls in 1000 rows with only 10 unique rows pulled from the subtable. Of course, this all depends on how wide these things are as well.

      But 100% agree, ORMs are the worst way to handle all these things. They very rarely do the right thing out of the box and to make them fast you ultimately end up needing to comprehend the SQL they are emitting in the first place and potentially you end up writing custom SQL anyways.

      • philipwhiuk 1 hour ago
        ORMs are a caching layer for dev time.

        They store up conserved programming time and then spend it all at once when you hit the edge case.

        If you never hit the case, it's great. As soon as you do, it's all returned with interest :)

        • xigoi 48 minutes ago
          The question is why we don’t have database management systems that integrate tightly with the progmming language. Instead we have to communicate between two different paradigms using a textual language, which is itself inefficient.
          • runroader 27 minutes ago
            We tried that in 90’s RAD environments like Foxpro and others. If it fits the problem, they were great! If not, it’s even worse than with an ORM. They rarely fit today since they were all (or mostly) local-first or even local-only. Scaling was either not possible or pretty difficult.
          • Shorel 22 minutes ago
            Because every single database vendor will try to lock down their users to their DBMS.

            Oracle is a prime example of this. Stored procedures are the place to put all business logic according to Oracle documentation.

            This caused backslash from escaping developers who then declared business logic should never be inside the database. To avoid vendor lock-in.

            There's no ideal solution, just tradeoffs.

            • cogman10 18 minutes ago
              > Because every single database vendor will try to lock down their users to their DBMS.

              I mean, that already happens. It's quite rare to see someone migrate from one database to another. Even if they stuck to pure SQL for everything, it's still a pretty daunting process as Postgres SQL and MSSQL won't be the same thing.

          • ivan_gammel 24 minutes ago
            The answer is simple: model optimized for storage and model designed for processing are two different things. The languages used to describe and query them have to be different.
    • sigbottle 53 minutes ago
      > Understanding algorithmic complexity (in particular, avoiding rework in loops), is useful in any language, and is sage advice.

      I recently fixed a treesitter perf issue (for myself) in neovim by just dfsing down the parse tree instead of what most textobject plugins do, which is:

      -> walk the entire tree for all subtrees that match this metadata

      -> now you have a list of matching subtrees, iterate through said subtree nodes, and see which ones are "close" to your cursor.

      But in neovim, when I type "daf", I usually just want to delete the function right under my cursor. So you can just implement the same algorithm by just... dfsing down the parse tree (which has line numbers embedded per nodes) and detecting the matches yourself.

      In school, when I did competitive programming and TCS, these gains often came from super clever invariants that you would just sit there for hours, days, weeks, just mulling it over. Then suddenly realize how to do it more cleverly and the entire problem falls away (and a bunch of smart people praise you for being smart :D). This was not one of them - it was just, "go bypass the API and do it faster, but possibly less maintainably".

      In industry, it's often trying to manage the tradeoff between readability, maintainability, etc. I'm very much happy to just use some dumb n^2 pattern for n <= 10 in some loop that I don't really care much about, rather than start pulling out some clever state manipulation that could lead to pretty "menial" issues such as:

      - accidental mutable variables and duplicating / reusing them later in the code

      - when I look back in a week, "What the hell am I doing here?"

      - or just tricky logic in general

      I only noticed the treesitter textobject issue because I genuinely started working with 1MB autogen C files at work. So... yeah...

      I could go and bug the maintainers to expose a "query over text range* API (they only have query, and node text range separately, I believe. At least of the minimal research I have done; I haven't kept up to date with it). But now that ties into considerations far beyond myself - does this expose state in a way that isn't intuitive? Are we adding composable primitives or just ad hoc adding features into the library to make it faster because of the tighter coupling? etc. etc.

      I used to think of all of that as just kind of "bs accidentals" and "why shouldn't we just be able to write the best algorithms possible". As a maintainer of some systems now... nah, the architectural design is sometimes more fun!

      I may not have these super clever flashes of insight anymore but I feel like my horizons have broadened (though part of it is because GPT Pro started 1 shotting my favorite competitive programming problems circa late 2025 D: )

    • philipwhiuk 1 hour ago
      > external services (including the database)

      Or even the local filesystem :)

      CPU calls are cheap, memory is pretty cheap, disk is bad, spinning disk is very bad, network is 'good luck'.

      You can O(pretty bad) most of the time as long as you stay within the right category of those.

  • cmovq 1 hour ago
    When you're using a programming language that naturally steers you to write slow code you can't only blame the programmer.

    I was listening to someone say they write fast code in Java by avoiding allocations with a PoolAllocator that would "cache" small objects with poolAllocator.alloc(), poolAllocator.release(). So just manual memory management with extra steps. At that point why not use a better language for the task?

    • ivan_gammel 20 minutes ago
      TBH, I do not see how Java as a language steers anyone to use one those shotguns. E.g. the knowledge about algorithmic complexity is foundational, the StringBuilder is junior-level basic knowledge.
    • ablob 1 hour ago
      You might have an application for which speed is not important most of the time. Only one or two processes might require allocation-free code. For such a case, why would you burden all of the other code with the additional complexity? Calling out to a different language then may come with baggage you'd rather avoid.

      A project might also grow into these requirements. I can easily imagine that something wasn't problematic for a long time but suddenly emerged as an issue over time. At that point you wouldn't want to migrate the whole codebase to a better language anymore.

    • cogman10 1 hour ago
      Bad idea. I've made a pool allocator before, but that was for expensive network objects and expensive objects dealing with JNI.

      Doing it to avoid memory pressure generally means you simply have a bad algorithm that needs to be tweaked. It's very rarely the right solution.

  • spankalee 1 hour ago
    Avoiding Java's string footguns is an interesting problem in programming languages design.

    The String.format() problem is most immediately a bad compiler and bad implementation, IMO. It's not difficult to special-case literal strings as the first argument, do parsing at compile time, and pass in a structured representation. The method could also do runtime caching. Even a very small LRU cache would fix a lot of common cases. At the very least they should let you make a formatter from a specific format string and reuse it, like you can with regexes, to explicitly opt into better performance.

    But ultimately the string templates proposal should come back and fix this at the language level. Better syntax and guaranteed compile-time construction of the template. The language should help the developer do the fast thing.

    String concatenation is a little trickier. In a JIT'ed language you have a lot of options for making a hierarchy of string implementations that optimize different usage patterns, and still be fast - and what you really want for concatenation is a RopeString, like JS VMs have, that simply references the other strings. The issue is that you don't want virtual calls for hot-path string method calls.

    Java chose a single final class so all calls are direct. But they should have been able to have a very small sealed class hierarchy where most methods are final and directly callable, and the virtual methods for accessing storage are devirtualized in optimized methods that only ever see one or two classes through a call site.

    To me, that's a small complexity cost to make common string patterns fast, instead of requiring StringBuilder.

    • brabel 23 minutes ago
      Yeah, Java is pretty fast despite the fact that it still has these kinds of obviously suboptimal things going on.

      I love how Zig, D and Rust do exactly what you say: parse the format string at compile time, making it super efficient at runtime (no parsing, no regex, just the optimal code to get the string you need).

      I say this but I write most of my code in Java/Kotlin :D . I just wish I could write more low-level languages for super efficient code, but for what I do, Java is more than enough.

      • jandrewrogers 18 minutes ago
        > Zig, D and Rust

        Also C++, which works the same way.

    • LtWorf 51 minutes ago
      Sometimes running strace on jvm software you will see some sycall patterns that are incredibly inefficient.
  • cogman10 1 hour ago
    Nitpick just because.

    Orders by hour could be made faster. The issue with it is it's using a map when an array works both faster and just fine.

    On top of that, the map boxes the "hour" which is undesirable.

    This is how I'd write it

        long[] ordersByHour = new long[24];
        var deafultTimezone = ZoneId.systemDefault();
        for (Order order : orders) {
            int hour = order.timestamp().atZone(deafultTimezone).getHour();
            ordersByHour[hour]++;
        }
    
    If you know the bound of an array, it's not large, and you are directly indexing in it, you really can't do any better performance wise.

    It's also not less readable, just less familiar as Java devs don't tend to use arrays that much.

    • wood_spirit 58 minutes ago
      Also zap the timestamp instant objects if you really need speed; see https://github.com/williame/TimeMillis
    • Okx 1 hour ago
      maybe it would be a little better to use ints rather than longs, as Java lists can't be bigger than the int max value anyways. Saves you a cache line or two.
      • cogman10 1 hour ago
        Fair point, but it is possible this isn't a list but rather some sort of iterable. Those can be boundless.

        Practically speaking, that would be pretty unusual. I don't think I've ever seen that sort of construct in my day to day coding (which could realistically have more than 1B elements).

  • sgbeal 11 minutes ago
    Slight correction:

    > StringBuilder works off a single mutable character buffer. One allocation.

    It's one allocation to instantiate the builder and _any_ number of allocations after that (noting that it's optimized to reduce allocations, so it's not allocating on every append() unless they're huge).

  • kyrra 3 hours ago
    First request latency also can really suck in Java before hotpathed code gets through the C2 compiler. You can warm up hotpaths by running that code during startup, but it's really annoying having to do that. Using C++, Go, or Rust gets you around that problem without having to jump through the hoops of code path warmup.

    I wish Java had a proper compiler.

    • pron 2 hours ago
      You mostly need a recent JDK. Leyden has already cut down warmup by a lot and is expected to continue driving it down.

      https://foojay.io/today/how-is-leyden-improving-java-perform...

      https://quarkus.io/blog/leyden-1/

    • looperhacks 2 hours ago
      You can create a native executable with GraalVM. Alternatively, if you want to keep the JVM: With the ongoing project Leyden, you can already "pre-train" some parts of the JVM warm-up, with full AoT code compilation coming some time in the future.
      • Thaxll 1 hour ago
        GraalVM has a lot of limitations, some popular lib don't work with it. From what I remember anything using reflection is painful to use.
      • senkora 2 hours ago
        And going the other direction, if you want your C++ binaries to benefit from statistics about how to optimize the steady-state behavior of a long-running process, the analogous technique is profile-guided optimization (PGO).
      • vbezhenar 1 hour ago
        GraalVM is terrible. Eats gigabytes of memory to compile super simple application. Spends minutes doing that. If you need compiled native app, just use Golang.
        • brabel 18 minutes ago
          I used to be really excited about GraalVM but this, together with limitations in what Java code can run (reflection must be whitelisted - i.e. pain) made me run away from it. I do use Go, but my favourite substitute for Java is actually Dart. It can run as a script, compile to a binary or to a multiplatform "fast" format (a bit like a jar), and performance wise it's par on par with Java! It's faster on some things, a bit slower on other... but in general, compiling to exe makes it extremely fast to start, like Go. I think it even shares some Go binary creation tooling since both are made by Google and I remember when they were implementing the native compiler, they mentioned something about that.
    • titzer 2 hours ago
      I worked on JVMs long ago (almost twenty years now). At that time most Java usage was for long-running servers. The runtime team staunchly refused to implement AOT caching for as long as possible. This was a huge missed opportunity for Java, as client startup time has always, always, always sucked. Only in the past 3-5 years does it seem like things have started to shift, in part due to the push for Graal native image.

      I long ago concluded that Java was not a client or systems programming language because of the implementation priorities of the JVM maintainers. Note that I say priorities--they are extremely bright and capable engineers that focus on different use cases, and there isn't much money to be made from a client ecosystem.

    • bob1029 2 hours ago
      AOT is nice for startup time, but there are tradeoffs in the other direction for long tail performance issues in production.

      There are JITs that use dynamic profile guided optimization which can adjust the emitted binary at runtime to adapt to the real world workload. You do not need to have a profile ahead of time like with ordinary PGO. Java doesn't have this yet (afaik), but .NET does and it's a huge deal for things like large scale web applications.

      https://devblogs.microsoft.com/dotnet/bing-on-dotnet-8-the-i...

    • taeric 2 hours ago
      I challenge the idea that first request latency is bottle necked by language choice. I can see how that is plausible, mind. Is it a concern for the vast majority of developers?
    • pjmlp 2 hours ago
      Excelsior JET, now gone, but only because GraalVM and OpenJ9 exist now.

      The folks on embedded get to play with PTC and Aicas.

      Android, even if not proper Java, has dex2oat.

    • a-dub 2 hours ago
      i'd be curious about a head to head comparison of how much the c2 actually buys over a static aot compilation with something serious like llvm.

      if it is valuable, i'd be surprised you can't freeze/resume the state and use it for instantaneous workload optimized startup.

    • bombcar 3 hours ago
      Do none of the JVMs do that? GraalVM?
      • pjmlp 2 hours ago
        They do, to add to another comment of mine elsewhere, JIT caches go all the way back to products like JRockit, and IBM JVM has and it for years in Maestro, now available as OpenJ9.

        Too many folks have this mindset there is only one JVM, when that has never been the case since the 2000's, after Java for various reasons started poping everywhere.

      • rileymichael 2 hours ago
        the best way is via CRaC (https://docs.azul.com/crac/) but only a few vendors support it and there’s a bit of process to get it setup.

        in practice, for web applications exposing some sort of `WarmupTask` abstraction in your service chassis that devs can implement will get you quite far. just delay serving traffic on new deployments until all tasks complete. that way users will never hit a cold node

      • user3939382 2 hours ago
        My architecture builds a command registry in Clojure/JVM which runs as a daemon, the registry is shared by a dynamically generated babashka (GraalVM) shell that only includes whitelisted commands for that user. So for the user, unauthorized commands don’t even exist, and I get my JVM app with no startup overhead.
    • dionian 2 hours ago
      This is why I use java for long running processes, if i care about a small binary that launches fast, i just use something slower at runtime but faster at startup like python.
      • packetlost 2 hours ago
        Python startup time can be pretty abysmal too if you have a lot of imports.
      • cogman10 1 hour ago
        So long as you aren't in a docker container, The openjdk can do fast startup pretty trivially.

        There are options to turn on which cause the JVM to save off and reload compiled classes. It pretty massively improves performance.

        You can get even faster if you do that plus doing a jlink jvm. But that's more of a pain. The AOT cache is a lot simpler to do.

        https://openjdk.org/jeps/514

      • AlotOfReading 2 hours ago
        And then you get applications choosing the worst of both worlds, like bazel/blaze.
    • belfthrow 2 hours ago
      I really hate how completely clueless people on hn are about java. This is not, and has not been an issue for many many years in Java and even the most junior of developers know how to avoid it. But oh no, go and rust is alwaayssss the solution sure.
      • pythonaut_16 2 hours ago
        Can you provide any examples or evidence of Java apps that prove this?

        Because in my experience as of 2026, Java programs are consistently among the most painful or unpleasant to interact with.

        • belfthrow 34 minutes ago
          Crac / aot cache / ready now all can address this. Not even considering native aot. Multiple low latency trading systems across market markers, hedge funds and ibs prove this. But people just want to compare it to building a cli tool in go or rust.
      • bombcar 2 hours ago
        Ah, but let's port rust to the JVM!
  • Okx 1 hour ago
    The code:

      public int parseOrDefault(String value, int defaultValue) {
          if (value == null || value.isBlank()) return defaultValue;
          for (int i = 0; i < value.length(); i++) {
              char c = value.charAt(i);
              if (i == 0 && c == '-') continue;
              if (!Character.isDigit(c)) return defaultValue;
          }
          return Integer.parseInt(value);
      }
    
    Is probably worse than Integer.parseInt alone, since it can still throw NumberFormatExceptions for values that overflow (which is no longer handled!). Would maybe fix that. Unfortunately this is a major flaw in the Java standard library; parsing numbers shouldn't throw expensive exceptions.
  • wood_spirit 2 hours ago
    A subject close to my heart, I write a lot of heavily optimised code including a lot of hot data pipelines in Java.

    And aside from algorithms, it usually comes down to avoiding memory allocations.

    I have my go-to zero-alloc grpc and parquet and json and time libs etc and they make everything fast.

    It’s mostly how idiomatic Java uses objects for everything that makes it slow overall.

    But eventually after making a JVM app that keeps data in something like data frames etc and feels a long way from J2EE beans you can finally bump up against the limits that only c/c++/rust/etc can get you past.

    • polothesecond 2 hours ago
      > And aside from algorithms, it usually comes down to avoiding memory allocations.

      I’ve heard about HFT people using Java for workloads where micro optimization is needed.

      To be frank, I just never understood it. From what I’ve seen heard/you have to write the code in such a way that makes it look clumsy and incompatible with pretty much any third party dependencies out there.

      And at that point, why are you even using Java? Surely you could use C, C++, or any variety of popular or unpopular languages that would be more fitting and ergonomic (sorry but as a language Java just feels inferior to C# even). The biggest swelling point of Java is the ecosystem, and you can’t even really use that.

    • dikaflowt 1 hour ago
      Can you share the libs you 're using?
  • kpw94 1 hour ago
    The Autoboxing example imo is a case of "Java isn't so fast". Why can't this be optimized behind the scenes by the compiler ?

    Rest of advice is great: things compilers can't really catch but a good code reviewer should point out.

    • kllrnohj 1 hour ago
      javac for better or worse is aggressively against doing optimizations to the point of producing the most ridiculously bad code. The belief tends to be that the JIT will do a better job fixing it if it has byte code that's as close as possible to the original code. But this only helps if a) the code ever gets JIT'd at all (rarely true for eg class initializers), and b) the JIT has the budget to do that optimization. Although JITs have the advantage of runtime information, they are also under immense pressure to produce any optimizations as fast as possible. So they rarely do the level of deep optimizations of an offline compiler.
    • vbezhenar 1 hour ago
      Why should compiler optimize obviously dumb code? If developer wants to create billions of heap objects, compiler should respect him. Optimizing dumb code is what made C++ unbearable. When you write one code and compilers generates completely different code.
      • carlmr 1 hour ago
        The problem is rather that Java doesn't have generics and structs, so you're kind of forced to box things or can't use collections.
  • Izkata 58 minutes ago
    "Java is slow" is a reputation it earned in the 90s/2000s because the JVM startup (at least on Windows) was extremely slow, like several seconds, with a Java-branded splash screen during that time. Even non-technical people made the association.
  • uraura 1 hour ago
    I thought those were common sense until I worked on a program written by my colleague recently.
  • titzer 2 hours ago
    For fillInStackTrace, another trick is to define your own Exception subclass and override the method to be empty. I learned this trick 15+ years ago.

    It doesn't excuse the "use exceptions for control flow" anti-pattern, but it is a quick patch.

    • ivan_gammel 12 minutes ago
      God, please make me unsee it. That‘s a cool trick that turns into an anti-pattern itself if abused.
  • jerf 1 hour ago
    Any non-trivial program that has never had an optimizer run on it has a minimal-effort 50+% speedup in it.
  • hiyer 1 hour ago
    I ran into 5 and 7 in a Flink app recently - was parsing a timestamp as a number first and then falling back to iso8601 string, which is what it was. The flamegraph showed 10% for the exception handling bit. While fixing that, also found repeated creation of datetimeformatter. Both were not in loops, but both were being done for every event, for 10s of 1000s of events every second.
  • zvqcMMV6Zcr 2 hours ago
    > Exceptions for Control Flow

    This one is so prevalent that JVM has an optimization where it gives up on filling stack for exception, if it was thrown over and over in exact same place.

    • dust-jacket 50 minutes ago
      ah, thank you. Haven't worked in java for a bit now, but that was the only one I read where I was like "I'm sure we didn't have to avoid this when I worked on java".

      The rest were all very familiar. Well, apart from the new stuff. I think most of my code was running in java 6...

    • j-vogel 2 hours ago
      [dead]
  • comrade1234 2 hours ago
    Also finding the right garbage collector and settings that works best for your project can help a lot.
  • latchkey 22 minutes ago
    When they say that AI will replace programmers, I think of this article and come to terms with my own job security.

    Most of this stuff is just central knowledge of the language that you pick up over time. Certainly, AI can also pick this stuff up instantly, but will it always pick the most efficient path when generating code for you?

    Probably not, until we get benchmarks into the hot path of our test suite. That is something someone should work on.

  • taspeotis 2 hours ago
    Knock Knock

    Who’s there?

    long pause

    Java

  • ww520 1 hour ago
    The autoboxing in a loop case can be handled by the compiler.
  • jandrewrogers 2 hours ago
    You can write many of the bad examples in the article in any language. It is just far more common to see them in Java code than some other languages.

    Java is only fast-ish even on its best day. The more typical performance is much worse because the culture around the language usually doesn't consider performance or efficiency to be a priority. Historically it was even a bit hostile to it.

    • this_user 2 hours ago
      Performance is really not Java's issue. Even bad Java code is still substantially faster than the bulk of modern software that is based on technologies like Python or JavaScript/Node.js.
    • steve1977 2 hours ago
      Which, to be fair, in many cases is ok. If you just need to churn out LOB apps for worker drones as cheap as possible, performance is probably not the most important factor.
  • bearjaws 3 hours ago
    JavaScript can be fast too, it's just the ecosystem and decisions devs make that slow it down.

    Same for Java, I have yet to in my entire career see enterprise Java be performant and not memory intensive.

    At the end of the day, if you care about performance at the app layer, you will use a language better suited to that.

    • maccard 2 hours ago
      My experience with the defaults in JavaScript is that they’re pretty slow. It’s really, really easy to hit the limits of an express app and for those limits to be in your app code. I’ve worked on JVM backed apps and they’re memory hungry (well, they require a reallocation for the JVM) and they’re slow to boot but once they’re going they are absolutely ripping fast and your far more likely to be bottlenecked by your DB long before you need to start doing any horizontal scaling.
      • wiradikusuma 2 hours ago
        Compile it to native (GraalVM) and you can get it fast while consuming less memory. But now your build is slow :)
        • maccard 2 hours ago
          The minute a project has maven in it the build is slow. Don’t even get me started on Gradle…
    • j-vogel 3 hours ago
      Fair point on ecosystem decisions, that's basically the thesis of the post. These patterns aren't Java being slow, they're developers (myself included) writing code that looks fine but works against the JVM. Enterprise Java gets a bad rap partly because these patterns compound silently across large codebases and nobody profiles until something breaks.
    • FatherOfCurses 2 hours ago
      "Enterprise Java"

      Factories! Factories everywhere!

  • victor106 2 hours ago
    this is great, so practical!!!

    any other resources like this?

  • spwa4 51 minutes ago
    Java IS fast. The time between deciding to use Java and Oracle's lawyers breaking down your door is measured in just weeks these days.
  • null-phnix 2 hours ago
    [dead]
  • ryguz 2 hours ago
    [dead]
  • andrewmcwatters 2 hours ago
    [dead]
  • r_lee 3 hours ago
    [flagged]
  • tripple6 3 hours ago
    Do good, don't do bad. Okay.
    • abound 2 hours ago
      I don't think that's a charitable take of the article. To many programmers, it wouldn't be obvious that some of these footguns (autoboxing, string concatenation, etc) are "bad", or what the "good" alternatives are (primitives, StringBuilder, etc).

      That said, the article does have the "LLM stank" on it, which is always offputting, but the content itself seems solid.

  • koakuma-chan 3 hours ago
    As much as I love Java, everybody should just be using Rust. That way you are actually in control, know what's going on, etc. Another reason specifically against Java is that the tooling, both Maven and Gradle, still stucks.
    • jayd16 2 hours ago
      Not knowing what's going on in Java is a personal problem. The language and jvm have its own quirks but it's no less knowable than any other compiler optimized code. The debugging and introspection tooling in Java is also best in class so I would say it's one of the more understandable run times.

      Gradle does suck and maven is ok but a bit ugly.

      • ActorNightly 8 minutes ago
        Lets look at Java in modern day.

        * Most mature Java project has moved to Kotlin.

        * The standard build system uses gradle, which is either groovy or kotlin, which gets compiled to java which then compiles java.

        * Log4shell, amongst other vulnerabilities.

        * Super slow to adopt features like async execution

        * Standard repo usage is terrible.

        There is no point in using Java anymore. I don't agree that Rust is a replacement, but between Python, Node, and C/C++ extensions to those, you can do everything you need.

      • dionian 2 hours ago
        LLMs take the whole argument away. Yes, maven/gradle/sbt suck to work with. But now you can just generate it.
    • piva00 3 hours ago
      Gradle does suck, it gives too much freedom on a tool that should be straightforward and actively design to avoid footguns, it does the opposite by providing a DSL that can create a lot of abstractions to manage dependencies. The only place I worked where the Gradle configuration looked somewhat sane had very strict design guidelines on what was acceptable to be in the Gradle config.

      Maven on the other hand, is just plain boring tech that works. There's plenty of documentation on how to use it properly for many different environments/scenarios, it's declarative while enabling plug-ins for bespoke customisations, it has cruft from its legacy but it's quite settled and it just works.

      Could Maven be more modern if it was invented now? Yeah, sure, many other package managers were developed since its inception with newer/more polished concepts but it's dependable, well documented, and it just plain works.

      • koakuma-chan 2 hours ago
        I would disagree that either "plain works" because to even package your app into a self-contained .jar, you need a plugin. I can't recall the specifics now, but years ago I spent many hours fighting both Maven and Gradle.
        • piva00 2 hours ago
          Well, yes? It's a feature provided by a plugin, like any other feature in Maven, you declare the plugin for creating a fat-jar or single-jar and use that. It's just some lines of XML configuration so it plain works.

          Like I said, it's not hypermodern with batteries included, and streamlined for what became more common workflows after it was created but it doesn't need workarounds, it's not complicated to define a plugin to be called in one of the steps of the lifecycle, and it's provided as part of its plugin architecture.

          I can understand spending many hours fighting Gradle, even I with plenty of experience with Gradle (begrudgingly, I don't like it at all) still end up fighting its idiocies but Maven... It's like any other tool, you need to learn the basics but after that you will only fight it if you are verging away from the well-documented usage (which are plenty, it's been battle-tested for decades).

        • looperhacks 2 hours ago
          You "need a plugin" in the sense that every component of maven is a "plugin". The core plugins give you everything you need to build a self-contained jar - if you wanted to, you don't even have to configure the plugins, if you want to write a long cli command instead.
    • shermantanktop 2 hours ago
      I’ll never understand the impulse to tell the entire world what to do based on your own personal preferences and narrow experiences.

      It gets a reaction, though, so great for social media.

    • pjmlp 2 hours ago
      Rust has no place other than deployment scenarios where any kind of automatic resource management, be it tracing GC or reference counting, is not wanted for, either due to technical reasons, or being a waste of time trying to change people's mindset.
    • krona 3 hours ago
      > That way you are actually in control

      Programming in Rust is a constant negotiation with the compiler. That isn't necessarily good or bad but I have far more control in Zig, and flexibility in Java.

      • koakuma-chan 3 hours ago
        Yes, there is a learning curve to Rust, but once you get proficient, it no longer bothers you. I think this is more good than bad, because, for example, look at Bun, it is written in Zig, it has so many bugs. They had a bug in their filesystem API that freezed your process, and it stayed unfixed for at least half a year after I filed it. Zig is a nice C replacement, but it doesn't have the same correctness guardrails as Rust.
        • krona 2 hours ago
          Assuming we're talking about the same bug, The filesystem API freeze wasn't caused by Zig's lack of correctness guarantees, but a design flaw in Bun's implementation.
          • dryarzeg 2 hours ago
            Maybe I'm stupid, but I never actually understood people who blame programming languages for bugs in software. Because sure, it's good to have guardrails, but in my opinion, if you're writing a program and there's a bug, unless this bug lies somewhere in implementation of compiler/interpreter/etc, you can't blame the tooling, It's you who introduced this bug. It was your mistake.

            It's cool when your tooling warns you about potential bugs or mistakes in implementation, but it's still your responsibility to write the correct code. If you pick up a hammer and hit your finger instead of the nail, then in most cases (though not always) it’s your own fault.

            • chuckadams 2 hours ago
              When millions of users constantly make the same mistake with the tool, there may be a problem with the tool, whether it's a defect in the tool or just that it's inappropriate for the job. Blaming the user might give one a righteous feeling, but decade after decade that approach has failed to actually fix any problems.
              • dryarzeg 2 hours ago
                That's why I say "in most cases" - so not always, actually. There might be problems with tools, I'm not trying to deny that. And by the way, what if some (or even most) of the users just don't have enough skill to use the tool properly? Again, there could be a problem with tool, yes, but you can't always blame only tools for mistakes users make.
          • koakuma-chan 2 hours ago
            https://github.com/oven-sh/bun/issues/18192

            I am talking about this bug. It looks like it is still unfixed, in the sense, there is a PR fixing it, but it wasn't merged. LOL.

            Regardless of whether this specific bug would be caught by Rust compiler, Bun in general is notorious for crashing, just look at how many open issues there are, how many crashes.

            Not saying that you cannot make a correct program in Zig, but I prefer having checks that Rust compiler does, to not having them.

    • j-vogel 3 hours ago
      I'm a fan of Rust too. But there are millions of Java applications running in production right now, and some of them are running these anti-patterns today. Not everyone has the option to rewrite in a different language. For those teams, knowing what to look for in a profiler can make a real difference without changing a single dependency.
      • koakuma-chan 2 hours ago
        I think that right now it is easier than ever to rewrite your app in Rust, due to LLMs. Unfortunately there are still people out there who dismiss this idea, and continue having their back-end written in much inferior languages, like JavaScript or Python. If your back-end is written in Java, you aren't even in the worst spot.