I've spent six years as a team leader and one of my responsibilities was performing technical job interviews with potential candidates for our team/company. Soon I've noticed a pattern. Candidates working with JAVA programming language were not using https://projectlombok.org/ on their projects. Yes, I'm aware it doesn't qualify for the drama-of-the-year category, but this was quite a surprise for me. Projectlombok.org was such an integral part of my daily work, one of those simple life hacks you are happy to share. So why is this simple code-generator so important? Well, if you want to join the church-of-release-from-writing-boring-and-repetitive-code, keep reading!
Simply put, if you want to spend your coding time on something new and challenging, code-generator can save you time spent on repetitive (or boring, as some may refer to it) coding. This fact alone can certainly speed up development and benefit the project or company you're working for. But, as always, first there are some issues to deal with.
"I only trust code that I wrote, should I trust generated code? " (Unknown Developer)
The idea of using code generation as a standard operating procedure within our team was first brought up in one of our meetings. We soon figured out that:
We wanted to write our own code
We trusted our code more than some auto-generated sequence
We discussed our coding guidelines, the fact that we do not like how generated code look, how it might be hard to read and understand, it might break OOP principles, clean code principles and so on. Most of the issues we've had with code generators were trust related, and this lack of trust was the first obstacle we wanted to address.
Since trust takes time, we decided that our first use case should be something simple. There are a lot of really good, simple and helpful code generators.
This blog should help you understand why and when you should use code generators, give you some example how and where we use it and give you overview of some simple code generators that have different purposes.
Reflecting on my experience and thinking about code generators seems to me that when and why to use it is highly dependent on what project are you working on, what programing language are you using, what are the use cases, how complex is part of code that you want to replace with generated code.
Remember Project Lombok? Let's give you an overview and see how it can help you to avoid repetitive, boring and error prone coding.
Let's say we want to implement simple immutable class that will represent Car. It could look something like this:
So, to achieve immutability, we needed to write constructor with null checks for fields that are not permitted to be null and get methods for each field. We also override toString, which is something we usually do to be able to print some instance of this class to get human readable data for logs, debugging and so on.
This is something that needs to be done, but it is repetitive and boring, not to mention error prone. If we lose focus we could easily forget to add Object.requiredNonNull for field that must not be null. The bottom line is that we want to speed this up and let code generator do the writing for us.
Now let's see how this class looks when we use Lombok:
It's easy to see that the amount of code we need to write is significantly lower, not to mention that the class looks cleaner, as we also removed a lot of boilerplate code. We really don't need it in in source file to know what this class contains - Lombok annotations will tell us that.
What did Lombok annotations do for us:
@RequiredArgsConstructor - generates constructor with all fields that have final modifier on them.
@Getter - generate get method for all fields in class
@ToString - overrides toString method which will use all fields to generate String representation of this class
This is just a small subset of what Lombok can do for us. However, one should be aware that Lombok does not generate new source code (new Car.java) and it doesn't replace anything in original Car.java It injects necessary code during compile time into bytecode of compiled Car.class. You can always decompile Car.class if you want to inspect what exactly Lombok generated.
Code generates can be really powerful. You could probably write most of the project code to be self-generated, and describe project using semantic needed for a code generator to generate project source code without writing a single line of project code yourself.
It's easy to get carried away and spend your time thinking about projects you could do with code generator rather than finishing the existing ones. Do not auto generate everything that can be auto generated - start with tackling simple, repetitive and boring code sequences. Try and do proof of concept and see where does that lead you to.
Some general tips to go by which might point you into direction this is where I should not use code generation. If you are doing proof of concept or you are already using code generation for some time:
If you find yourself in need to modify generated code
If you find yourself in need to inspect generated code
If you find yourself constantly working on code generator because it is lacking some features
If maintenance of code generator is taking considerable time compared to gain
Less errors - code generators do not make mistakes. They will do their job and generated code will be the same no matter how many times they generate it. Code generator doesn't get tired and it doesn't lose focus like humans do.
Time saving - generating code is fast, much faster than any human can write code. More repetitive tasks and code you need to write means more time you save using code generators.
Expected output - generated code will always be the one you are expecting. Naming, coding principles, the way code works will be something that will be same no matter who is using it. If a code is written manually by different developers, this kind of consistency usually cannot be achieved.
Cool, I don't need to write this code again - as developers, we often found ourselves writing similar code all over again. This can be really boring, and in the end we should have fun when writing code, and code generators can provide us extra time to write a more meaningful code.
We need to modify code generator again - our code generator requires constant maintenance. We are finding new use cases for it and it is getting more convoluted and time consuming to maintain it.
Code generated is not optimized - if your code and project needs to squeeze every bit of performance, then code generators might be obstacle. Usually, to optimize code for performance you do it manually, change architecture of code, maybe change order of code lines or just rewrite it completely. If your code is generated, this can be really hard to do, and could require extensive modification on code generator you are using or it just might not be possible.
Generated code is a mess - although this technically goes into cons, this should not be your concern and you should not be worried about how the code looks. Code should be something that works for you, strictly utilitarian.
Our project is microservice oriented so we have multiple instances of various services and uptime of those services is very important. We collect over 200 metrics data and constantly monitor what is the state of our system/services.
Most of our metrics are custom made. As the number of metrics increased, we found our self spending a lot of time writing a code that collects metrics required for some feature we were working on.
This was our scenario:
Writing repetitive and error prone code
Writing it for almost every new feature we added
It was boring and it was slowing us down
Let's say now we want to implement a set of metrics that will count how many red and blue cars we sold:
This is how our base for code generation looks like. Here we used our custom defined annotations (@MetricSchema, @MetricCounter) as metadata that we needed as an input into code generator and our custom defined metric type(ResetableCounter).
This is how part of our generated source code looks like:
This generated class will be used as our metric object when instanced to collect metric on our application. It has all that we need to start collecting and reporting this metric.
We have static factory method that will help us to instance metrics and we have methods to increment, reset metrics and method to retrieve metric value.
Next example shows us how to use this generated code to collect metrics:
We are using https://github.com/jtwig/jtwig-core as a code templating engine, and this is how our template looks like. Without going into too much details about jtwig, this template allows us to generate CarMetric class from AbstractCarMetric. This is how we generate all the metric classes that we need.
This is relatively simple code generation. Considering that we are writing and collecting a lot of metrics on our project, we did this to release us from the need to write metrics ourselves.
https://projectlombok.org/ — we discussed it earlier, it has a lot of functionality, most commonly used functionality is going to free you from writing repetitive boilerplate code (constructors, getters and setters and so on)
https://www.jooq.org/doc/3.14/manual/code-generation/ — this code generator is used in conjunction with library itself. Main job of this code generator is to inspect your database schema and generate classes that represent object model of your database. This is very common case for code generators as writing object model for database manually is very repetitive, error prone and time consuming.
https://developers.google.com/protocol-buffers — protocol buffers are usually used in conjunction with https://grpc.io/docs/what-is-grpc/introduction/ but they do not have to be used for that exclusively. This is example of language agnostic code generator that can generate code from defined protobuff semantic for different programing languages.
Code generators are a broad and complex topic. It might not be clear where you should start with code generators because use cases for them are virtually endless. You might end up using code generator and walk into something that in the long run will be a really big burden for the project you are working on.
If you are just starting to use code generators, start with baby steps. Find your simple use case (repetitive, boring, error prone coding), do your research and find if someone already solved your problem.
When you decide on which code generator you are going to use and what use case you want to cover, do proof of concept for it and move forward from there. Choose wisely I hope this blog post will be of help on your coding path!