---
title: "Lombok is Still Relevant in the Age of Java Records"
slug: lombok-is-still-relevant-in-the-age-of-java-records
url: https://listedarticles.com/articles/lombok-is-still-relevant-in-the-age-of-java-records
canonical_url: https://swaranga.dev/posts/on-lombok/
content_type: opinion
language: en
published_at: 2026-07-12T00:00:00.000Z
updated_at: 2026-10-19T08:09:29.502Z
author: "Swaranga Sarma"
authored_by: human
publisher: "Swaranga Sarma"
publisher_url: https://swaranga.dev/
topics: ["Programming", "Programming Languages", "Software Engineering"]
license: all-rights-reserved
word_count: 1007
reading_minutes: 4
citation: "Swaranga Sarma, Swaranga Sarma. \"Lombok is Still Relevant in the Age of Java Records.\" 12 Jul 2026. https://swaranga.dev/posts/on-lombok/ (all-rights-reserved)"
# The full text follows. The web page shows an extract and sends readers
# to the source above; quote the citation and link the canonical URL.
---

# Lombok is Still Relevant in the Age of Java Records

> Swaranga Sarma, a Java developer who has used Project Lombok for about 15 years, rebuts common arguments against it (weird bugs, compiler hacks, Records making it obsolete, future JDK breakage) and explains why its benefits still outweigh the costs, with delombok as an easy exit.

# Lombok is Still Relevant in the Age of Java Records

Every six months, like clockwork, a new JDK drops—and right on cue, the Java community’s ideological purity tests begin.

The usual suspects crawl out to complain about Project Lombok. They argue that it does “weird hacks” to the `javac` compiler, that it slows down upgrades, and that we don’t need it anymore because we have Records.

I am a little tired of this constant principled stance against its usage. I am an application developer, not a compiler engineer. I’ve been using Lombok for roughly 15 years now, and I can clearly say its benefits massively outweigh its cons. I’m glad I don’t work with people who hold that dogmatic opinion, because at the end of the day, we are paid to deliver working software—not to coddle the compiler.

I’m still betting on Lombok, and for me, the arguments against it usually fall flat in the real world.

## Lombok introduces “weird bugs”[#](#lombok-introduces-weird-bugs)

In over a decade and a half of using Lombok, I have only seen exactly *one* issue where Lombok was called out in a post-mortem as the root cause—and even then, the blame was unfair IMO.

We had a class annotated with `@Data` containing a primitive `boolean` field. When deserializing a message from SQS, the incoming payload was missing this field. Because it was defined as a primitive, Jackson assigned it the default value of `false`, which accidentally triggered a different code path.

The post-mortem labeled it a “Lombok issue.”

**I disagree.** Lombok simply generated the setter. The true root causes were:

1. **Bad data modeling:** The field logically required three valid states (`true`, `false`, and `null`), so it should have been a boxed `Boolean` type from the start.
2. **Lack of test coverage:** This edge case should have been caught in testing immediately, or flagged during the PR review.

Blaming Lombok for a developer coding error and a gap in test coverage is just lazy.

## The Myth of “We Have Records Now”[#](#the-myth-of-we-have-records-now)

Whenever you defend Lombok, someone inevitably chimes in with, *“Just use Records!”*

Records are fantastic for immutable data carriers, but they are not a complete replacement for Lombok. To prove this, I did a quick check on my current project to see which Lombok features we actually rely on.

Here is the raw data:

```
find . -name "*.java" | xargs cat | grep "import lombok" | sort | uniq -c | sort -n -r

    144 import lombok.extern.slf4j.Slf4j;
    131 import lombok.NonNull;
    120 import lombok.RequiredArgsConstructor;
     68 import lombok.Builder;
     30 import lombok.Data;
     24 import lombok.Getter;
     22 import lombok.NoArgsConstructor;
     22 import lombok.AllArgsConstructor;
     12 import lombok.experimental.Accessors;
      8 import lombok.extern.jackson.Jacksonized;
      6 import lombok.EqualsAndHashCode;
      4 import lombok.AccessLevel;
      2 import lombok.Singular;
      2 import lombok.experimental.UtilityClass;
      1 import lombok.ToString;
      1 import lombok.extern.log4j.Log4j2;
```

What dominates the top of that list is not @Data. It’s logging and code to support dependency injection.

I cannot imagine coding without @RequiredArgsConstructor. In Spring, you just declare your injected services as final fields, add that one annotation, and your constructor boilerplate vanishes. Records do absolutely nothing to solve this. Add @Slf4j and @Builder to the mix, and you are saving hundreds of lines of tedious, error-prone boilerplate per module that you’d otherwise have to write, review, and maintain.

And as for IDEs generating getters and setters? You still have to read that cluttered code. Plus, adding a new field means remembering to manually regenerate the equals and hashCode methods; a step that is easy to forget.

## The Lombok “Dark Magic”[#](#the-lombok-dark-magic)

Purists argue that Lombok’s AST manipulation is “magic” and that modifying the compiler’s behavior is an architectural sin. But these same developers will happily build entire enterprise systems on top of frameworks that do far worse things at runtime.

Take Jackson, for example. The Java purist wants everything to be explicit and strictly abide by JVM rules. Yet, when Jackson deserializes a JSON payload into an object that lacks a default constructor, it often uses reflection (or libraries like Objenesis) to allocate memory for that object *without ever actually calling a constructor*. You are upset that Lombok generates a predictable `setName()` method at compile time, but you are perfectly fine with a framework completely bypassing the JVM’s object-creation lifecycle at runtime?

Or look at Hibernate and JPA. You define a standard `List<Order>` in your entity and write your business logic against it. But at runtime, Hibernate silently intercepts your object and replaces your standard Java `ArrayList` with its own `PersistentBag` proxy. If you try to iterate over that list after the database session closes, your application explodes with the dreaded `LazyInitializationException`.

That is more opaque, runtime sorcery where standard data structures are hijacked in an invisible manner.

Compared to that, Lombok is incredibly boring. The generated code exists completely at compile time. You can view the `.class` files. You can see exactly what it generated, and if a getter returns a value, you can step right through it. If we are willing to tolerate the massive, opaque runtime machinery of Jackson and Hibernate because of the velocity they provide, we should absolutely apply that exact same pragmatism to Lombok. AOP is another such tool with lots of gotchas - I remember once debugging a Try-Catch block in an AWS SWF (Simple Workflow) FLow Framework implementation where the Try and Catch blocks were literally executing on different machines.

## A future JDK release will completely break Lombok’s compiler hacks[#](#a-future-jdk-release-will-completely-break-lomboks-compiler-hacks)

First, the upgrade panic is overblown. Unless you are raring to upgrade on day zero, Lombok is usually patched and ready within days of a major release. (I’m already running JDK 26 with Lombok).

But let’s say the absolute worst-case scenario happens, and Lombok is permanently banned from future JDKs. If that day comes, my migration strategy is incredibly simple:

- Run delombok on my source code.
- Commit the generated boilerplate and remove the dependency.
- Upgrade the JDK in a separate PR.

There is zero runtime lock-in. Until that hypothetical doomsday arrives, I will gladly save my time and skip writing code that I simply don’t have to write. Purity is great in theory, but pragmatism ships features.
