Lombok Best Practices
Lombok removes a huge amount of boilerplate, but used carelessly it hides surprising behavior and bites teams in JPA and serialization layers. This page distills the practical rules for using Lombok safely and consistently in a Spring Boot codebase.
When to use vs avoid
Lombok is most valuable on plain data carriers and on Spring beans where it expresses a clear, conventional pattern. It is risky on persistence entities and anywhere subtle generated behavior matters.
| Use Lombok for | Be careful / avoid on |
|---|---|
| DTOs and request/response models | JPA @Entity classes (no @Data) |
| Configuration property holders | Classes with delicate equals/hashCode |
Spring services (@RequiredArgsConstructor) | Public API types you must keep stable |
| Value objects and builders | Anything where hidden methods confuse readers |
Never put @Data on JPA entities
This is the single most important rule. @Data bundles @ToString and @EqualsAndHashCode over all fields, which on an entity causes infinite recursion across bidirectional relationships, surprise lazy loading (LazyInitializationException), and broken hash contracts when a generated id changes after persistence.
// DON'T
@Entity
@Data // recursion + broken equals
public class Author { /* ... */ }
// DO
@Entity
@Getter
@Setter
@ToString(exclude = "books")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Author {
@Id @GeneratedValue
@EqualsAndHashCode.Include
private Long id;
@OneToMany(mappedBy = "author")
private List<Book> books;
}
The full reasoning is in @EqualsAndHashCode & @ToString.
Prefer @RequiredArgsConstructor for injection
For Spring beans, declare dependencies as private final fields and use @RequiredArgsConstructor. This gives clean constructor injection, immutable dependencies, and trivial unit testing.
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository repository;
private final PaymentClient paymentClient;
}
Avoid field injection (@Autowired on fields) entirely — it is harder to test and hides dependencies.
Do / Don’t quick reference
| Do | Don’t |
|---|---|
Use @Data on DTOs and value objects | Use @Data on entities |
Use @RequiredArgsConstructor for beans | Use field-level @Autowired |
Use @Builder.Default for default values | Assume field initializers apply in builders |
Use @EqualsAndHashCode.Include (allowlist) | Compare entities on all mutable fields |
Add @NoArgsConstructor when JPA/Jackson needs it | Forget the no-arg ctor after adding others |
Pair @Getter + @Builder for immutable DTOs | Generate setters on immutable values |
Pin the Lombok dependency as optional/provided | Ship Lombok as a runtime dependency |
Use delombok to inspect or remove Lombok
delombok expands Lombok annotations into real Java source. It is useful for debugging “where did this method come from?”, for generating Javadoc, or for removing Lombok from a module entirely.
# Maven
mvn lombok:delombok
# Direct
java -jar lombok.jar delombok src/main/java -d target/delombok
This writes the fully expanded source so you can read exactly what the compiler sees.
Tip: When a generated method behaves unexpectedly, delombok the class and inspect the real code. It almost always reveals the issue (a missing
@Builder.Default, an unintended setter, or an over-broadequals).
Team conventions
Consistency matters more than any single choice. A few conventions that scale well:
- Centralize behavior with a
lombok.configat the project root, for example to enforce a logging field name or copy annotations onto generated constructor parameters:
# lombok.config
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
lombok.equalsAndHashCode.callSuper = call
lombok.addLombokGeneratedAnnotation = truemarks generated members with@lombok.Generated, so coverage tools like JaCoCo can exclude generated code from metrics.- Agree on one logger annotation (
@Slf4j) across the codebase — see @Slf4j Logging. - Prefer Java records over
@Value/@Datafor immutable DTOs on Java 17+; reach for Lombok where records do not fit (mutable models, inheritance, builders on entities). - Document the entity equality strategy once and apply it everywhere.
Warning: Lombok modifies bytecode via annotation processing. If a build mysteriously cannot resolve generated methods, confirm annotation processing is enabled and that the IDE Lombok plugin matches the dependency version.