Why we rebuilt our backend tech stack on Kotlin
A few months ago, we had to make a very important choice at Inventa, that would shape our engineering and set its bases for the future: which would be our technical stack (languages, frameworks, cloud, etc). In this article, we will show which was the context leading to this choice, the criteria we used to decide, the decision itself, and finally, which were the results so far. Without further ado, let's go!
A bit of history
Inventa is a Brazilian startup that aims to be the largest B2B marketplace in Latin America, and it has grown immensely in its one and a half years of age. The first version of the product, seeking to prove the market fit of the company, was built using fast prototype stacks, more specifically, Shopify, Python Lambdas to extend Shopify features (using Chalice framework to abstract the deploy/infrastructure complexity), and DynamoDB.
Although this stack has served well in its initial market fit purposes, it was crystal clear to us that it would be not sustainable and scalable in the long term. This is a tonic of many fast prototype solutions: they help us a lot in the beginning, but do not scale or are not flexible enough as the company grows, the usage patterns change and we need more robust solutions. This was the scenario that led to the crossroads we mentioned at the beginning of this article. Which way should we go?
But why choose a default stack?
This may be the first thing you are wondering now. Why should a company choose a default stack when it can let each project owner define the technology to use? You build it, you run it, you fix it, right? More or less…
Although this degree of autonomy is very nice, it demands an extreme level of maturity and ownership throughout the whole engineering team, which we didn't have at the time, we don't have it now and I doubt more than a couple of companies in the world have this state of grace. These kinds of loose patterns are theoretically wonderful, but when it comes to reality, it makes very difficult to manage at an organizational level, especially in a startup in hypergrowth.
Therefore, in our vision, having a default stack would allow us to:
- Build application templates, keep improving these templates continuously, and reuse code as much as possible
- Implement standards at the company level
- Easier knowledge sharing among engineers and teams
- Unified engineering vision
- Minimize ramp-up when teams' formations change
- Easy of maintenance given multiple teams and individuals throughout the company have knowledge of the stack
What did we take into consideration?
Although at first, this choice seems purely technical, this is far from the truth. Actually, choosing the technical stack means choosing the engineering culture you want to foster in the company.
I explain: there are some languages, like Ruby for example, that given its experimental approach encourage a 'hacking' and innovative behavior. On the other hand, stacks like Java and .NET are heavily built upon architecture and design patterns, favoring more structured and enterprise behavior.
This was the first and main factor we took into consideration: as we mentioned in the context section, Inventa was passing from a hypothesis validation phase to a more robust and scale-ready moment, so it would be important for us to choose a stack that was heavily focused on architecture and design (modularization, encapsulation, etc). Therefore, a typed object-oriented language was the first requisite.
Ecosystem support
This factor basically means: is this stack being maintained? Will it survive in the long run? Engineers are mostly tech enthusiasts and sometimes choose project stacks based on hype and curiosity — although this is fine for PoC and parallel projects, it does not go well as a strategic decision for a company. So it was important for us that our stack was being maintained and having fixes and improvements delivered in a timely fashion.
Popularity
Given the chosen stack must scale in terms of personnel and product, and that we will need to interact with external resources (forums, other engineers, etc), it is important that our chosen language has a certain degree of popularity. We broke this subject down into 3 components:
- Hiring: to scale our company and fulfill our business aims, we will need to hire more people, from both junior and senior levels and although this is not an inflexible requirement, it is better if a share of these people have some previous knowledge and experience with the stack the company is using.
- Community support: the more popular the stack is, the easier it will be to find material on it on forums. This is substantial because it is very unlikely that we will build our codebase without this external help. Stack Overflow, Baeldung, and other content sources are fundamental to our engineering daily work.
- Don't reinvent the wheel: the more popular the stack is, the larger the number of frameworks, libraries, and other tools we can use instead of having to build them in-house. This allows our teams to focus on the business use cases, which is what actually creates value for our company. Creating our own dependency injection, ORM, or API client solutions would create much more trouble than it would solve.
Security
This topic is extremely important, given nowadays it is very common to see companies suffering hacker attacks and having leaked information, which leads to financial, operation, and reputation damages, some of which are never truly recoverable.
To mitigate the risks, we must have a strong security culture in the company, and this also includes having an engineering stack reliable in terms of security.
Performance and Scalability
These two points refer to the resource management of the chosen stack, including response times, CPU and memory management, and also the ability to scale the applications running on this stack easily, via horizontal and/or vertical scaling.
Development experience
The stack that we choose must not only empower us to make a great and scalable product, but also provide a nice experience to everyone developing it. This includes being easy to learn the rules, developing and maintaining code, and also being transparent about operations, avoiding obscure functionalities and misunderstandings.
Besides that, it is also important to have the capacity within the company to train the people who are not used to that stack yet. This means having technical references that will conduct bootcamps, write documentation and pass to the other engineers the patterns and way of thinking of that certain stack. If the company lacks these references, it is likely that the final code is below the bar in terms of quality and maintainability.
Patterns and documentation
Last but not least, we have the documentation offered by the language, explaining and detailing its features and APIs, and the patterns the language provides, be them purely code-design related, be them architectural. The existence of these patterns is really helpful to increase code reuse and maintainability.
Alternatives considered
Given the criteria defined in the previous section, we have considered the following alternatives, dividing them into two groups:
- Fast prototyping languages: these languages focus on speed, so they are based on minimal code for maximum functionality. Patterns and underlying code design are less important. Examples of this tier are Ruby, Python, and JS.
- Enterprise languages: in this paradigm, the key is reusable, pattern, architecture-based, and large-scale software. Examples of this tier are Kotlin, Java, and C# with .NET.
After defining this working group, we evaluated each one of them against the criteria and compiled this information. After all this work (Phew!…), we could decide on our backend stack.
Decision
We decided to use Kotlin (with Spring framework) as our backend stack given the following reasons:
- Kotlin has all the pros of being a Java-based language, including architecture and design-based development approach, security, scalability, and robustness.
- We also gain most of the Java frameworks, libs, tools and also, engineers. The move from Java to Kotlin is one of the smoothest ones you can ask for. Hence, we could hire engineers more experienced with Java and easily ramp them up with Kotlin.
- Kotlin goes beyond its Java parent for having an easier syntax, with less verbosity and a more modern approach. This would help the transition of our team, which was used majorly to Python.
- Finally, the popularity of Kotlin is growing substantially in the last few years, making it a very attractive stack in terms of hiring and employer branding.
How is it going so far?
After a few months of this decision, the subsequent question is: how did it turn out? Do we regret the choice?
And the answer is: we are doing great, thanks for asking! Choosing a robust while simple to learn and use language helped us to accelerate the ramp-up of our engineers while keeping quality, scalability, and reliability at hand.
Currently, we have a Kotlin-Spring application template that teams can fork in order to easily set up a new application. Together with our CI/CD pipeline (nice subject for another article by the way), we estimate that we reduced the time to put a new application live in development and production environments from days to less than one hour. This is the beginning of our paved road at Inventa, and we believe this road will take us to be one of the most important and worthy technology companies in Latin America.
Final words
To conclude this article, we would like to share a few thoughts. First of all, there is no one-size-fits-all engineering stack: all of them come with strengths and limitations, and the choice must be customized by the peculiarities of each company. Even in the same company, the best stack may change over time given the evolution and maturation of the business (which was exactly the scenario at Inventa).
Second, most engineers will enjoy learning a new language if given the time and the motivation. Therefore, before the decision, we made sure to discuss it with all teams and collect feedback. After it, we worked hard on how to provide the best environment so people could learn and fully use this new stack. This included guilds, talks, boot camps, pair programming, and the application template we mentioned before. Was everybody happy when we first took the decision? No, but no stack would please everybody so after the decision was carefully made, we assumed a posture of 'this is our engineering strategy — let's make the best out of it and build the best engineering company we can. And this is how we still feel.
Finally, having a standard stack does not mean each and every application we build MUST use this stack. It means that, if your application does not have any special requirements, then let's go with the default stack. If it has, then let's investigate the best approach and validate it through a design review process (another story we can tell some other day).
It does not mean taking away the autonomy of the teams, it means making engineering decisions based on technical and business requirements, rather than on personal taste and habit.
If you want to know more about us, add me on LinkedIn or apply to one of our open positions! Let’s learn together and build something bigger and better than we could do individually!