Lombok vs Java Records: Which Should You Use When?
I was refactoring a Java 17 codebase when I hit a wall: my @Data annotated class was perfect for a DTO, but someone suggested I should use Java Records instead. I tried converting it—and broke my JPA entities. That’s when I realized the choice isn’t “Lombok or Records”—it’s “Lombok AND Records, each in the right place.”
The Core Difference
Lombok generates boilerplate code at compile time. Records are a language-level feature for immutable data carriers. They solve different problems:
Lombok -> Reduces boilerplate on ANY classRecords -> Native immutable data carriersThe 80/20 rule applies here: Records cover about 80% of what Lombok does for data classes. But that remaining 20% (mutable state, JPA entities, builders) still needs Lombok.
When Records Work Perfectly
Records shine for immutable data transfer. If your object is a “bag of data” that never changes after creation, use a Record.
DTOs and API Responses
@Data@AllArgsConstructorpublic class UserDto { private Long id; private String name; private String email;}public record UserDto(Long id, String name, String email) {}The Record version is cleaner and intention-revealing. It says “this is immutable data” by design.
Value Objects with Validation
Records support compact constructors for validation:
public record Money(BigDecimal amount, Currency currency) { public Money { Objects.requireNonNull(amount, "amount cannot be null"); Objects.requireNonNull(currency, "currency cannot be null"); if (amount.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException("Amount cannot be negative"); } }
public Money add(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException("Currency mismatch"); } return new Money(amount.add(other.amount), currency); }}Configuration Objects
public record DbConfig( String host, int port, String database, int maxConnections) { public String jdbcUrl() { return String.format("jdbc:postgresql://%s:%d/%s", host, port, database); }}When Lombok Is Still Necessary
JPA/Hibernate Entities (Critical)
Records do NOT work with JPA entities. I learned this the hard way:
@Entity@Table(name = "users")public record User( // This will FAIL! @Id @GeneratedValue Long id, String name, String email) {}JPA requires:
- A no-args constructor
- Setters for lazy loading proxies
- Non-final fields for bytecode enhancement
@Entity@Table(name = "users")@Data@NoArgsConstructor@AllArgsConstructorpublic class User { @Id @GeneratedValue private Long id; private String name; private String email;}Builder Pattern
Records don’t have a native builder. Lombok’s @Builder remains essential for complex object construction:
@Data@Builderpublic class EmailRequest { private String to; private String subject; private String body; private List<String> cc; private List<String> attachments; private boolean html; private Priority priority;}
// UsageEmailRequest request = EmailRequest.builder() .subject("Hello") .body("<p>Content</p>") .html(true) .build();Mutable State with Custom Logic
When you need setters with validation:
@Datapublic class Account { private BigDecimal balance;
public void setBalance(BigDecimal balance) { if (balance.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException("Balance cannot be negative"); } this.balance = balance; }}Logging with @Slf4j
Lombok provides convenient logging:
@Slf4j@Servicepublic class PaymentService { public void process(Payment payment) { log.info("Processing payment: {}", payment.getId()); }}Decision Flow
Use this mental model:
+-----------------------------------+| Do you need mutable state? |+---------------+-------------------+ | +-------+-------+ | | YES NO | | v v+---------------+ +-------------------+| Use Lombok | | Is it a JPA || (@Data, etc.) | | entity? |+---------------+ +---------+---------+ | +-------+-------+ | | YES NO | | v v +---------------+ +---------------+ | Use Lombok | | Use Record | | (@Entity) | | (immutable) | +---------------+ +---------------+Quick test: “Could a Record work here?” If yes, use Record. If no, use Lombok.
Practical Scenarios
Use Records For:
- DTOs for API responses
- Method return types (tuples)
- Configuration objects
- Value objects (Money, Address, Email)
- Command/Query parameters
Use Lombok For:
- JPA/Hibernate entities
- Objects requiring setters
- Classes needing
@Builder - Logging with
@Slf4j - Legacy codebases on Java 8-13
Migration Strategy
Don’t try to convert everything at once:
- New code: Default to Records for data classes
- Existing DTOs: Convert when you touch them
- Entities: Keep Lombok—Records won’t work
- Mixed usage: Both in the same project is fine
// Same project, different toolspublic record UserDto(Long id, String name) { } // Record for DTO
@Entity@Datapublic class User { // Lombok for entity @Id private Long id; private String name;}Common Mistakes
Mistake 1: Trying Records with JPA
@Entitypublic record User(@Id Long id, String name) {}// Fails: Hibernate can't proxy recordsMistake 2: Using Records for mutable configuration
public record Config(String apiKey) { // Can't change apiKey after creation!}Mistake 3: Over-complicating Records
public record User(Long id, String name, String email, Address address, Phone phone, List<Order> orders, Preferences prefs) { // Too many fields - consider a class with @Builder}Summary
Records and Lombok serve different purposes. Records give you immutable data carriers with minimal syntax. Lombok reduces boilerplate on classes that need features Records can’t provide.
The rule of thumb is simple: start with Records for new data classes. If you hit a wall (JPA, setters, builders), switch to Lombok. They coexist beautifully in modern Java projects.
Key takeaways:
- Records for immutability (DTOs, value objects, configs)
- Lombok for mutability (JPA entities, builders, setters)
- Apply the test: “Could a Record work here?”
- Both tools can coexist in the same project
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 JEP 395: Records
- 👨💻 Project Lombok
- 👨💻 Java Records with JPA
- 👨💻 Reddit: Records vs Lombok Discussion
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments