To Lombok or not to Lombok

To Lombok or not to Lombok

@CanIbebotheredtowriteallmycode

I have recently had some perspective on Lombok; a code generation library for Java. I am sure alternatives exist in Java and other languages. I am not going to go into the inner workings of these libraries, more the pros and cons of delegating boiler plate code to a 3rd party.

Story Time

I am currently a Java developer having converted a couple of years ago from a more C# background with a bit of JS for good measure. The first project I joined after starting my new role was fully tooled up with Lombok annotations and, being a Spring Boot project, Spring annotations. I was initially put off by all the bizarre lables above the classes and methods and also baffled as to how on earth everything worked with apparently so little code. Once I got up to speed with what was going on, I became quite adept manipulating these annotations to achieve exactly what I wanted. I could generate fairly complex classes without being bogged down with constructors, getters, setters, equals, tostring etc. This made development fast and the business logic clear.

I struggled to see how I could manage without such a tool. Which, incidentally was exactly what I had to do. I moved to a new project with slightly more old-fashioned architects who had made the decision to not let us use it. This struck me as a bizarre decision at first and was certainly frustrating. However, after my Lombok detox I have started to understand some of its potential pitfalls and how easy it can be to misuse.

In this article I am going to go into reasons for and against the usage of Lombok in particular and these types of libraries in general. As mentioned above, I will not go into what goes on under the hood, security or performance impacts. This will purely be the impact of development effort and code readability/maintainability.

Pros

It takes less time to write code

No surprises here. If a library generates code for you, generally speaking, you don’t have to write as much code. Lombok is a massive time-saver. Many IDEs can also generate this code for you in a more one-off manner rather than at compile-time so you will rarely have to actually tap out getters and setters yourself. However, this is still more cumbersome than just adding an annotation. It is also less reactive to changes such as properties being added, removed renamed etc. Once Lombok was gone from my day-to-day life I realised just how verbose Java can be. Lombok also includes handy features such as generating builder classes which would approximately double the amount of non-business logic boilerplate code to write and maintain yourself.

The important stuff is less buried

Boiler plate code such as constructors, getters, setters, equals, hash code and tostring take up a huge number of lines. A simple value class I wrote went from ~20 lines with Lombok to over 150 without. This is before I put any business logic in. All of this code is auto-generated and uninteresting. Who cares if my IDE generates it or if a library generates it? There is nothing surprising or useful about it, it just gets in the way. Also all lines generate a maintenance effort, a greater inertia to refactoring and, consequently, a higher chance of accumulating technical debt.

Incetivising standardised approaches

Lombok generates code in a specific way. Levereging its benefits usually requires adopting a certain uniform approach to development. Getters and Setters are consistently named and don't have any unexpected side-effects or logic, everything uses builders etc. I always like things to be consistent with my codebase. It means I find it easier to work out what's going on.

Implicitly comprehensive

Working with methods like equals and hash code in classes under active development is a pain. You have to remember with any property change to add checks in these methods or your equality tests may cease to be accurate. This can lead to false positive unit tests, duplicates in sets, innaccurate logs and more. It is nice to eliminate this potential source of error and to just decide with the addition of an annotation to automatically include all properties in these checks (which is what the IDE generated code does anyway).

Cons

Higher barrier to entry

As I hinted in my introductory paragraph, it took me a bit of time to get to grips with Lombok. For those not familiar it introduces another aspect of the programming language to learn and to more junior developers getting to grips with the vanilla language this can prove challenging. Without these annotations, the code is more explicit, everything is on the page. This can make it easier to onboard new people onto the project and have them contributing sooner.

Misuse

Linked to the previous point, not only does this extra aspect to learn increase the time to get to grips with the code base as a whole, but it also introduces a larger possibility that this library is misused. Lombok is a very powerful tool in a developer's arsenal but it is also easy to misuse. Careful considerations of immutability can be quickly eradicated by the careless addition of an annotation. Private properties with no need to be exposed can get added to the blanket array of public getters and also feature in equality checks and are converted to the string representation of the object. This accidental misuse can make your code inefficient, misleading or sometimes just wrong.

Convenience masking bad design

An extension of misuse is that the ease of using of tools like Lombok often prevent anti-pattern warning sirens from being sounded. When your constructor or equals method is 20+ lines long you pause to think about whether you can restructure and break your classes up into simpler chunks. You only realise this when you can see the code. The ease with which you can create a class with 15 properties using Lombok means you are less likely to stop and think whether or not it is a good idea. The @AllArgsConstructor can hide many sins. I also think that overusing Lombok can make you fall into common anti-patterns such as the Anemic Domain Model. This is because value classes are trivial to create and it becomes very easy to pass them around and keep all your logic in the service layer. Developers born and raised in such an environment may learn bad habits and perpetuate some bad practices.

Loss of control

A downside of auto-generated code is that you haven't written it. You have less control over what is produced. This is the flipside of the incentivising standardised approaches advantage; sometimes customised approaches are much more suitable for particular sitautions. Lombok can give you a fine level of control by explicitly excluding certain items and using more advanced configuration options but with more configuration you lose the terseness and hence the main benefit to begin with. To make things more marginal, the advent of records in Java 14 reduces the amount of boilerplate code you need to write for data classes. Also your source code ceases to really be source code. It becomes a set of instructions to generate source code. The interpretation of these instructions being delegated to a library which you do not own. You never know how new JDKs are going to be supported or how breaking changes between library versions might affect your code. Tightly coupling your code to another library introduces a dependency you may not want that could be very difficult to get rid of.

Implicitly comprehensive

An advantage becomes a disadvantage depending on how you look at it. Java was intentionally written so that you explicitly consider every property when writing a class. This increases the verbosity and effort during development, but this reflection is important during the design process and shouldn't be overlooked. Due care and attention needs to be paid when deciding on which annotations to leverage and also when changing anything on a class with annotations as you can be unintentionally changing something else. Sometimes it is safer to selectively include rather than include by default.

Final thoughts

All-in-all I am not really fully decided on whether I am for or against. I suppose it depends on many factors such as team experience, code maturity and any constraints on speed of development. Personal preference comes into it as well. Perhaps you could control which annotations you like with style guidelines. Static code analysis such as Sonarqube also warns you of obvious misuse which can help reduce bugs and educate against potential pitfalls.

It may look like I have come up with more cons than pros but most of the cons are potential problems whereas the pros are easier to realise. I think I still fall on the side of using it although having it taken away from has given me an interesting perspective.