Use Lombok @Data for small, stable data carriers, but prefer explicit getters and setters when behavior, validation, persistence rules, or public API stability matter. The annotation saves real time, yet it also hides generated code that can affect equality, logging, serialization, and database entities. A serious Java project should not choose it just because it makes a class shorter.
TLDR: @Data can remove 40 to 70 percent of boilerplate in plain DTO classes, especially when a class has 10 or more fields. For example, a request object with 15 fields may shrink from about 120 lines to 25 lines. That is useful in a team building many API models each week. But if that same class contains validation rules, JPA relations, or security sensitive fields, explicit methods are safer and easier to review.
What @Data actually generates
Lombok’s @Data is a shortcut annotation. It combines several Lombok features into one. In practice, it generates:
- Getters for all fields.
- Setters for non final fields.
equals()andhashCode().toString().- A constructor for required fields, such as
finalfields.
That is a lot of behavior from one short annotation. In a clean DTO, this is convenient. In a domain object, this can be too much.
Where @Data works well
@Data is strongest in classes that are simple by design. Think API response models, internal transfer objects, configuration holders, or test fixtures. These classes often have fields and almost no logic.
For these cases, explicit getters and setters add little value. They take space. They also distract during code review. Nobody wants to scan 200 lines just to confirm that getEmail() returns email.
A typical good use looks like this:
@Data
public class UserResponse {
private Long id;
private String email;
private String displayName;
}
This is readable. It says, “This class is just data.” That signal can be useful. It also keeps merge conflicts smaller when fields are added or removed.
Where explicit getters and setters are better
Explicit methods are better when access has meaning. A setter that trims input, rejects invalid values, records an audit event, or protects an invariant is not boilerplate. It is business logic.
Consider this example:
public void setEmail(String email) {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
this.email = email.trim().toLowerCase();
}
If you replace that with @Data, the rule disappears unless it moves somewhere else. That can create bugs that look harmless in a pull request. It drives me crazy that a one line annotation can hide a change in object behavior so well.
Explicit methods are also better for public libraries. Once other teams depend on your class, generated methods become part of the contract. Removing a setter later can break consumers. Changing equality can break maps, sets, caches, and tests.
The biggest risk: equals(), hashCode(), and toString()
The most serious concern with @Data is not the generated getter. It is the generated object methods.
equals() and hashCode() are easy to get wrong in mutable classes. If a field used in hashCode() changes while the object is inside a HashSet, lookup can fail. That is not a theoretical issue. It causes ugly production bugs.
toString() can also expose data. A password, token, national ID, or internal note might appear in logs because @Data includes fields by default. Logs tend to spread. They go to monitoring tools, support systems, vendor platforms, and long term storage.
With explicit code, the choice is visible. With Lombok, reviewers must remember what is generated. That adds mental load.
Be very careful with JPA entities
Using @Data on JPA entities is usually a bad choice. It may work in small demos. It often hurts real systems.
JPA entities have identity rules. They may start with a null ID, then receive one after persistence. They may also contain lazy relations. Generated equals(), hashCode(), and toString() can trigger database access, recursion, or stack overflow when bidirectional relations exist.
For entities, prefer a more controlled style:
- Use
@Getterand selected@Setterannotations if Lombok is still desired. - Exclude relations from
toString(). - Write equality rules by hand when identity is subtle.
- Avoid public setters for fields that should change only through domain methods.
For example, order.cancel() is clearer than order.setStatus(CANCELLED). The first expresses intent. The second just mutates state.
Team readability and tool support
Lombok depends on annotation processing. Most modern IDEs support it, but setup can still be annoying. A new developer may open the project and see false errors until the plugin or compiler settings are fixed. Expect to waste time on that at least once in a mixed IDE team.
There is also a review issue. Generated code is not shown directly in the source file. Senior developers may know what @Data does. Newer developers may not. That knowledge gap matters in regulated systems, financial software, medical products, and security focused applications.
This does not mean Lombok is unprofessional. Many serious teams use it. The safer stance is to use narrow annotations instead of broad ones. @Getter is easier to accept than @Data. @ToString(exclude = "secret") is clearer than hoping no sensitive field gets logged.
A practical decision rule
Use this simple rule during review:
- Use
@Datafor DTOs, request models, response models, and temporary internal data structures. - Avoid
@Datafor JPA entities, domain models, security related classes, cache keys, and public SDK models. - Use explicit methods when validation, normalization, access control, or audit behavior exists.
- Use narrower Lombok annotations when only part of the generated behavior is wanted.
Recommended project policy
A strong policy is simple and written down. For example:
@Datais allowed only on DTOs and test objects.- Entities must not use
@Data. - Classes with sensitive fields must define
toString()explicitly or exclude fields. - Any custom setter logic blocks the use of
@Dataon that class. - Public API classes require approval before generated setters are added.
This policy keeps the benefit without letting the annotation creep into places where it creates risk.
Final recommendation
@Data is not bad. Blind use of @Data is bad. It is a productivity tool, not a design strategy.
If a class is plain data, stable, and low risk, @Data is a reasonable choice. If a class protects rules, identity, security, or persistence behavior, write the methods or use smaller Lombok annotations. The extra lines are worth it when they make the object’s contract clear.

