According to Tiobe analysis, there are close to 300 programming languages today. Their analysis is a bit complex though. It rules out markup languages and frameworks from the equation, it also expects programming languages to have a certain number of Google search hits in order to include them, etc.
About ⅔ of programming languages listed there, I've never even heard of. And mind you, I've coded anything in just a bit under two dozen of them. At the same time, there are about 100 code generators out there. Some of them support a significant number of those programming languages. Within the community, however, there are polarized views on when to go with generating as opposed to coding the whole thing manually.
Do you even have to pick one over the other? I'm a bit biased here - I do both. Some of it I code and some of it I find easier to generate and even more easy to maintain.
Do I think one is better than the other?
YES. And also NO.
It's basically up to the person to decide how they want to approach a certain challenge. I'll get back to this later. If you stick until the end of this blog post, you'll see why we used and even built on top of the code generator. Hence the pun in the title. I know, hilarious. But for now, back to the basics.
Here are some of the stronger arguments against using code generation tools:
- It's an anti-pattern.
- Can reduce the performance.
- Not as clean, tidy, and fancy written as manually written code can be.
- Manually written code still does the same thing but better.
- You cannot really generate business logic.
When you hear this, you might wonder why even bother using something that might reduce performance.
Additionally, potentially cause a worse experience if, for example, we're creating the library which wraps around HTTP API. In my team in Infobip, we do exactly that.
You see, here in Infobip, my team is in charge of DevRel. To simplify, our main goal is to enhance the developer experience when using or better say integrating our platform.
If you are not familiar with Infobip, our platform is about omnichannel engagement. Our job is to help businesses of our clients and partners to communicate with their own clients while using any of the communication platforms we can provide.
A few months back we were set a challenge to upgrade API client libraries for the company's complete product stack for a programming language of our choice, but which will continuously keep on adding additional features and programming languages.
We needed to make a decision on how to tackle this. There were several options. Creating libraries for dozens of products and dealing with different programming languages wasn't easy. Upgrading existing ones at times can be even harder. A lot of questions popped up as well as potential red flags. How to scale it? How not to end up in never-ending library development in multiple, different, programming languages? How not to create a horrible piece of software if we go with code generation.
Some code generation tools are configurable and programmable. You can do just about anything. For example, you are able to produce a complete set list of language-specific details or which library should you use to generate a certain functionality.
But yes, more often than not, generated code does not look awesome. Now, why don't you write one piece of software and built on top of a certain code generator? And then you can programmatically refine the generated code.
Let me introduce the OpenAPI.
Long story short, OpenAPI Specification is an API description format for REST API. Basically, it's a file in which you can, by following certain standards, describe your entire API, authentication, and more. The cool part is, several code generation tools can read it, understand it, and translate it to a compilable, working project in one or more available programming languages. The tool we are using within my team is called OpenAPI Generator and we love it.
What we did was we wrote a service that consumes this tool and builds on top of it to enhance the generated code. Yes, we wrote it manually. That service basically serves as a set of preprocessing and postprocessing steps around the configuration for code generation and extends the certain functionalities of the generator itself.
So what that means in our case is that, among the rest, we integrated a linter for each of the programming languages we are generating the code for. Some linters simply give results that feel more natural and configuring the right one is of great importance. We defined an additional set of rules to make it look more natural and less generic to someone to integrate that library into its own project.
When you think of it, we literally extended a code generation tool by writing our own set of rules on some things. Also, we've enriched the result of the code generation execution by both using the language-specific options within the generator and furthermore by applying a linter to it.
You might ask:
"Well, isn't this an overkill?"
Or you might even go on and say:
Even if you saved time on learning the specifics of certain programming languages, you still had to write the configuration for each and every one of those generations. You had to implement the service which is building on top of it, AND you had to test all that afterward and not simply just push it to the world to use it."
If you look at it that way, then yes, it's an overkill. BUT!
Let's say you manually write all the code for a single product - a set of API methods, by hand, for all the programming languages. And let's assume for the sake of the argument, that it takes exactly the same amount of time to create the release-ready product in both cases.
Would you still consider using code generators as an overkill approach?
If the answer was yes, well, I'd have to disagree. And I'm going to break it down as to why I disagree.
In an ideal universe maybe, but just maybe, it would be safe to say that API will be built in such a way that no breaking changes will occur. Take an even more utopian scenario where the product team created the API endpoints in such a way that they cover all the aspects of its usage, all the use-cases ever needed, and that all the potential authentication processes are covert.
That does not happen, like, ever.
This means that for every single update you need to do, you will have to write code for every programming language you used in the initial release. Furthermore, this means that you will keep having to do it over and over again.
And we only talked about one product written in several programming languages. In reality, we are not taking care of only one product. We're talking about multiple products developed by different teams.
How would you handle that?
And up until this moment we talked about the code, but there are so many more things that make a project complete.
So if you have a similar use case like my team, I would be more inclined to say that an overkill would be manually coding over and over again all the things that need to be changed in every single programming language. Instead, in most cases, all we need to tweak after an initial release is that OpenAPI Specification. And it will use the same building blocks we prepared up until that point and do the heavy work for us.
I've tried to emphasize this at the very beginning. There isn't a deterministic answer as to which approach is better. Well, at least I don't see a deterministic answer.
As with most of the things in the world of software, I find that it very much depends on what you are trying to achieve. And on which problem are you trying to solve.
To be fair, most of us do use code generation to some extent every single day. If you're programming in a language like Java, using annotation libraries like Lombok per definition ends up with having parts of your code generated in the compile time. But whichever side you are leaning on, do what feels the best for you.
When facing certain challenges, take your time. Try to get a bigger picture rather than trying to solve the issue instantly. It just might save you a ton of time later on.