HN user

javanonymous

46 karma
Posts0
Comments21
View on HN
No posts found.

There are several open source tools for Java (Eclipse, Visual Studio plugins, Netbeans and others).

The reason I don't use them is not because they are bad, but because IntelliJ is so much better.

I even use IntelliJ Ultimate for non Java code like React, even though Visual Studio Code seems to be de-facto standard for React developers and guides.

why would we ever want in Java to hold a variable that you can't read immediately what is the type

I can use my IDE to see the type if necessary.

Everything that came after isn't really memorable nor helpful,

There are several improvements that are very helpful

One example is how multi line strings help me to read more clearly without the unnecessary string concatenations:

   var sql = """
             SELECT foo
             FROM bar
             WHERE last_updated > :lastUpdated
             """;
Another example is how switch statements have improved code readability, at least from my personal subjective viewpoint.
   String dayName = switch (day) {
      case 1 -> "Monday";
      case 2 -> "Tuesday";
      case 3, 4, 5 -> "Other day";
      default -> "Weekend";
   };

I think it has a lot to do with work culture. Many tend to mimic what others are doing in order to not stick out.

At my previous job some were able to change that by consistently using "modern" features of Java. It inspired others to change and eventually we ended up with a good code base.

Be the one to start the change by implementing new features using good code. This will give others "permission" to do the same. Also try to give soft suggestions in code reviews or pair programming of simpler ways to do it (don't push too hard)

At my current job all of us were eager to try the latest features from the start, so we never had to convince new hires.

I haven't used it myself, but I know there is a plugin for a Maven build cache. It seems like it should improve incremental builds

https://maven.apache.org/extensions/maven-build-cache-extens...

Incremental builds work on the modified part of the project graph part only

Subtree support for multimodule projects builds part of the codebase in isolation

Version normalization supports project version agnostic caches

Project state restoration (partial) avoids repeating expensive tasks like code generation

The JVM tends to use as much memory as it can for performance reasons. It is not a reliable indicator of how much memory it actually needs. Why spend resources on clearing memory if there's still unused memory left?

If memory is an issue, you can set a limit and the JVM will probably still work fine

It sounds good but in reality people end up spending time messing around with config files and annotations.

I use Spring Boot at my day job and write mostly web services. I don't spend time messing around with config files and annotations. When I create a service class, I annotate it with @Service, and that is mostly what I need.

Example:

   @Service
   public record ItemsService(ItemsRepository repo) {

      public void doStuff(String country) {
         var items = repo.findByCountry(country);
         // do stuff with items
         
      }
   }
Later versions of Spring Boot has reduced a lot of the annotations necessary, like @Inject if you use constructors etc. There are of course other annotations and configurations, but 90% of what I do is similar to the example I gave above. Things may have changed since last you used it, but the amount of "magic" and annotations is often much less than what is posted in these types of discussions.

which is something that I would have said would never happen if you had asked me five years ago.

I think a lot of people are noticing the changes Java has had in the previous years. The language has made a lot of improvements, and I feel that the mind set of the community has changed. The old enterprise way of factories and unnecessary abstractions have lost a lot of popularity, and is mostly still alive in legacy software/teams and universities who have not yet caught up.

Even Spring Boot is now a valid approach for getting sh*t done for startups. There are of course frameworks that are more light weight, or you can start from scratch and choose your own libraries to keep the size down. But SB is simply good enough for most use cases, and even supports native compilation now.

I think named parameters would be a great addition

For now, I use Lombok's @Builder annotation. It makes it much easier to create and copy a record, where non-assigned attributes are set to default.

Example:

   var bmw = Car.builder().make("BMW").build()
It also has a practical toBuilder() syntax that creates a copy of the original record, with some attributes changed
   var other = bmw.toBuilder().year(2024).build()

The Java library actually supports this case.

It may not have the convenient "years" method directly, but you can add an arbitrary unit of time quite easily

Example with duration:

    Duration.of(1, ChronoUnit.YEARS)
Example with datetime:
    today.plus(2, ChronoUnit.YEARS)
It even supports decades, centuries, eras and half days :-)

For those curious about how it handles leap years:

    var leapDay = LocalDate.of(2024, 2, 29)
    leapDay.plus(1, ChronoUnit.YEARS);
   
    // ==> 2025-02-28

    var lastYear = LocalDate.of(2023, 3, 1);
    lastYear.plus(1, ChronoUnit.YEARS);

    // ==> 2024-03-01

    var lastFeb = LocalDate.of(2023, 2, 28);
    lastFeb.plus(1, ChronoUnit.YEARS);

    // ==> 2024-02-28

    var firstFeb = LocalDate.of(2024, 2, 1);
    leapDay.with(TemporalAdjusters.lastDayOfMonth());

    // ==> 2024-02-29

I'm not sure if I understand your point, but I haven't seen any NIH at the places where I have worked. We have been encouraged to use popular stable libraries when possible.

Java has several 3rd party dependencies that are expected to just be there in projects where I have worked. Examples are Lombok, Apache Commons and SLF4J. These have become so widely used, that I have stopped thinking about them as external dependencies.

Guava used to be more popular too, but now that Java has Optional and Streams, I don't see it as often.

If I understand your use case, most loggers (like Logback) have the possiblity to automatically include the name of the method that called the log-statement.

Logback even supports including the call chain in the output, so you could include the parent method, grand parent method etc. This probably uses reflection, so it could have a negative impact for applications that need a high performance.

https://logback.qos.ch/manual/layouts.html

Lombok can be used for more than just getters/setters.

I personally use the @Builder annotation on records with more than 3-4 fields. I find it much more readable than a long list of arguments to the constructor.

It also makes it easy to return a copy of the record where only a few fields have changed:

   var r = book.toBuilder()         
           .lastUpdated(now)
           .title("...")
           .build()

I also use other annotations, but I could work without them if a future version of Java provides a builder-like pattern (or named arguments)

I've gotten 100x improvement with no code change by just adding an index in the database table. An inexperienced developer might have blamed the database and insisted on moving to NoSQL because of "web scale". If they got the chance to rewrite it, they could have pointed to the performance increase as a proof that they were right.

They wrote it from scratch with the benefit of all the knowledge they had gathered after running the old system for years. A 2X improvement would not be surprising to me, even if they had rewritten it in the same language. .

According to others in this discussion they also made architecture changes (DB, Kafka etc.). Do we know if that improved the performance?

There is no objective way we can tell if Elixir had any performance impact. It could have been due to the rewrite, the architecture change or a combination of both.

I'm curious about how you plan to work on checked exceptions.

If it's just about ignoring forced exception management, Lombok provides the @SneakyThrows annotation.

I would need something more before I add a new type of extension. Maybe if you could solve the null-check issue.

i.e. rewriting

    var x = a.getFoo()?.getBar()
to
    var x = a.getFoo() != null ? a.getFoo().getBar() : null
In that case I may consider it for my personal projects, and then potentially recommend it at work once it becomes more widespread and has IDE support.

At one of my previous jobs, they had a "BOM" that each service inherited from. This contained most of the core dependencies (Spring, database drivers, logging, monitoring, auth, service discovery etc.) with a default configuration that worked in all environments. This was built on top of Spring Boot, so if you needed to use Postgresql, you just added the basic database configuration to your property file and the BOM would make sure that the necessary beans and service discovery was loaded.

This meant upgrades were usually as simple as increasing the version, as the infrastructure team had already made sure that the other dependencies had been tested. Each team were free to ignore this and do whatever they wanted, but the benefits were too great to ignore for most teams.

At my current job we don't have this type of shared configuration, which makes micro services much harder to maintain.

There are tools for enforcing boundaries.

One name for this is "Modulith" where you use modules that have a clear enforced boundary. You get the same composability as micro-services without the complexity.

Here's how Spring solves it: https://www.baeldung.com/spring-modulith

It's basically a library that ensures strict boundaries. Communication has to go through an interface (similar to service api) and you are not allowed to leak internal logic such as database entities to the outer layer

If you later decide to convert the module into a separate service, you simply move the module to a new service and write a small API layer that uses the same interface. No other code changes are necessary.

This enables you to start with a single service (modulith) and split into microservices later if you see the need for it without any major refactoring