Lombok
why I don’t like to use Lombok
It constantly comes up in code reviews, people asking “why did you not use lombok for this?”, so instead of having the same discussion over and over again in private chats, I decided to write it down.
First of all, I’m ok if you feel differently. I’m ok if you use Lombok. But I’m rather writing getters, setters, constructors, builders, and the other things Lombok can do, myself than having it generated and invisible from me.
- Most of the time, we read existing code. When we generate getters and setters through annotations, I cannot see them because my IDE does not display them. So I cannot even see if they are used or not. And in case they are unused, I’d like to remove them, because we shouldn’t keep unused code around.
- it’s another library which we have to master in order to use it properly. Everyone on the team has their own understanding of how it works, which annotations exist, and what they actually achieve. This means we can achieve the same result in many ways, adding ambiguity to our code base, which again makes it more difficult to read the code. Often I’ve found classes annotated with @Data, @Builder, @NoArgsConstructor and @AllArgsConstructor. Yesterday such a class even had additionally @Setter(AccessLevel.NONE). This raises way too many questions in my head which distract me from understanding the code. Just a few examples:
- Why not just @Getter instead of @Data when we declared the Setters as private?
- Why did we not make the fields final to achieve the same?
- Do we need the implementation for equals, hashcode and toString generated by lombok?
- Why no-args AND all-args constructors?
- Why a builder additionally to all the constructors?
- Who uses this class? Is one of these constructors for jackson? If so, does this class model an incoming json request that is mapped to this object? But why do we also have a builder and all-args constructor then?
- Due to @Data, Lombok will implement a constructor (it’s derives @RequiredArgsConstructor from @Data) which in this case is identical to AllArgsConstructor, so why was @AllArgsConstructor separately declared?
- The main use-case for lombok seems to be that one can avoid writing (and seeing?) getters and setters, so called boilder-plate code. If you read some literature about object oriented code, getters and setters are an anti-pattern and should not even exist. Objects should expose methods of behaviour and what data they carry inside should be invisible to the user. So if your code base is plagued with getters and setters everywhere and you try to get rid of them by using annotations, you’re just masking a smell. What you have is an anemic domain model, where services, helper and util classes pull out all data from your classes and operate on them. I recommend to have a look at https://www.martinfowler.com/bliki/AnemicDomainModel.html
- But I’m using frameworks&libraries that require getters and setters and no-args constructors. That is correct. Certain libraries somewhat force us to implement no-args constructors and to have getters, so that they via reflection can discover the fields in our class in order to serialize it to JSON or map it to database table columns. These are all concerns about infrastructure which usually does not have anything to do with our core business domain. In a hexagonal architecture, I’d have such “non-oo data classes” then together with my adapters, far away from my core domain. Most often, I see such data classes leaking from the infrastructure-context into the domain. It somewhat sounds logical, why would I map my EmployeeDTO to an Employee class when they both have the same fields with getters and setters and then using it in a xxService, xxHandler or xxUtil. That’s a misconception though. The Employee class in your current context should not expose getters for everything, it should rather expose the behaviour through well defined methods that hide what fields which were used to make that decision, removing the need to have xxHelper etc classes.