Lombok Is Still Relevant in the Age of Java Records

Hacker News Top News

Summary

作者为 Lombok 辩护,认为在 Java Records 时代它依然不可或缺,因为它在日志注解、构造器注入和 Builder 等方面大幅减少样板代码,而这些 Records 并不能替代。文章以 15 年实际使用经验和项目中 Lombok 注解使用统计为据,反驳了 Lombok 引入奇怪 bug 和"暗黑魔法"的常见批评。

No content available
Original Article
View Cached Full Text

Cached at: 10/19/26, 05:00 AM

# Lombok is Still Relevant in the Age of Java Records Source: [https://swaranga.dev/posts/on-lombok/](https://swaranga.dev/posts/on-lombok/) 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” 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” 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” 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 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\.

Similar Articles

Age of the bespoke engines?

Reddit r/LocalLLaMA

作者讨论了LLM推理引擎生态日益碎片化的现象,从早期依赖llama.cpp转向各种fork项目(如Niffler、Strata、Avalon、Basalt等),并质疑llama.cpp为何在性能上落后于许多独立开发者的作品。

This Code Is CRAP (2011)

Hacker News Top

The author highlights Crap4J as a valuable tool for identifying dangerous code in Java projects without false positives, praises its Jenkins integration, and notes its incompatibility with Java 7.

When LLM Decompilers Recompile More and Preserve Less

Lobsters Hottest

论文指出 LLM 反编译器以可重编译性和重执行性作为主要指标时会产生误导:代码虽然能通过所有自带测试,却可能在其他合法输入上行为发散甚至使已披露漏洞悄然消失。作者提出 Decompile-Diverge 行为比较预言机,通过合成驱动程序和模糊测试语料检测行为差异,发现部分系统发散率高达 13%。

How a new DSL may survive in the era of LLMs

Hacker News Top

The article explores how new domain-specific languages (DSLs) can remain relevant in the age of LLMs by emphasizing strong documentation, interactive landing pages, and robust tooling such as language servers and diagnostics, using the author's projects Web Pipe and Datafarm as examples.