The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Revolutionizing time tracking: how Quarkus transformed our backend development

GRAN Software Solutions is a German company that designs and builds modern backend solutions. We work with large automotive clients and others to restructure and create new solutions. We also develop and offer SaaS tools to help us and others in our daily work.

One such tool we built for ourselves and others is a time tracking application called Sheetty.

The time tracking challenge

Frustrated Developer

We needed to create a time tracking application because the existing solutions on the market did not meet our specific requirements. They were either not designed for developers, lacked the simplicity we needed, or were loaded with unnecessary features. We wanted to build a tool that was perfectly tailored to our needs, using the extensive experience we had gained from working on client projects over the years. We also wanted to create a more modern and user-friendly design, which would be fun to use and incorporate newer technologies such as Quarkus.

The main issue we faced with existing time tracking solutions was the lack of an easy way to switch between clients. We also found that they did not support quick actions or shortcuts, which we were used to, and there was no visual way to see the time entries we made during the day. Additionally, we wanted to track time within the context of contracts signed with our clients in terms of daily rates and contract caps. That’s why we decided to create a custom solution to address all of these specific needs.

Discovering Quarkus

When we were choosing the technology stack to use for our backend, our main goal was to use technologies that we were already familiar with, such as the Kotlin programming language, Spring Boot framework, and Postgres database. We also wanted to select an ecosystem that could provide us with libraries for database connectivity, web client, caching, and other similar features. Additionally, we wanted to use a high-performance solution to keep our hosting costs low and avoid high memory requirements.

After analyzing various solutions on the market, we decided to use the Quarkus framework as it met all of our requirements.

Our backend development experience with Quarkus: the key features

We have designed our application architecture to separate the frontend and backend parts. To secure our backend APIs in a modern and secure way, we opted to use JSON web tokens, and Quarkus has excellent support for them. We also use role-based security for our APIs, and Quarkus makes it easy for us to implement this. We have different roles in our application, such as regular users and admins, and this information is encoded in our JSON web tokens. Quarkus ensures that these tokens are not tampered with or manipulated when they reach our back-end systems.

@RolesAllowed for authorization of our API endpoints
@Path("/clients")
@RolesAllowed("User")
@Produces(MediaType.APPLICATION_JSON)
@ApplicationScoped
class ClientResource(
    private val getClientsHandler: GetClientsHandler,
    private val newClientHandler: NewClientHandler,

We relied heavily on rich JSON support to model our data flexibly and delegate much of the functionality to Postgres itself to manipulate the data. This way, we could pass the already-built JSON objects back to the API client, which significantly reduced the time it took to make design decisions in the application code. Quarkus provided fantastic support for JSON object APIs. We believe that Postgres is the right place to perform data manipulations and aggregations, not the application code, due to performance and code maintenance reasons.

Using JsonObject to pass our data in and out
@GET
@Produces(MediaType.APPLICATION_JSON)
suspend fun getProfile() = db.preparedQuery(
    """select profile from "user" where email = $1""".trimIndent()
).execute().awaitSuspending().first().getJsonObject("profile")

Although Quarkus primarily targets Java programming language, Kotlin support is also quite good. We used coroutines and suspending functions, which allowed for greater performance and much simpler code compared to some other asynchronous programming models that are available. Kotlin’s structured concurrency enabled us to write seemingly sequential code but in reality, very performant asynchronous code. Quarkus provides excellent Kotlin extension methods built on top of existing asynchronous APIs such as Mutiny.

We executed the database migration on application startup, which was very important for us. Fortunately, Quarkus has excellent Flyway support, so all our database migrations were in one place and executed during our backend booting process. This kept our database schema and data transparent and reproducible.

Database Migrations
Figure 1. Using Flyway to execute database migrations

For our deployments, we use Kubernetes. Before using Quarkus, we described our application requirements using helm packaging, but with Quarkus, we opted for another approach as Quarkus offers a great Kubernetes extension. Instead of writing any code, we described our Kubernetes resources using an application.yaml file, keeping our complete application configuration in one place. This extension generated Kubernetes resource files behind the scenes, which we then applied to our Kubernetes cluster. This works well for us.

Kubernetes configuration
Figure 2. Using the Kubernetes extension to generate Kubernetes resources

For packaging our backend API, we used the Jib extension. To package our application in a container, all we had to do was use the application.yaml file and set all the required parameters such as image name tags repository, and so on. We didn’t have to maintain the Docker file on our own, which was very convenient.

Our time tracking application needs to send emails to our users and admins on various occasions. To keep things simpler, we decided not to go for any third-party API-driven email-sending approach. Instead, we send emails ourselves, and for that purpose, we use Qute email templates, which make composing and sending emails to our users very simple. This extension provides support for coding coroutines, allowing for non-blocking sending and higher throughput.

Qute Templates
Figure 3. Using Qute email templates to send emails

Development journey

The Quarkus development process has been excellent so far. Compared to other frameworks like Spring Boot, Quarkus has a faster startup time and a smaller memory footprint. It also provides profiles, which allows us to have slightly different configurations or behaviors between environments. We can easily substitute some hard-to-run third-party services with local mocks, leaving the application code unchanged. Quarkus is also great in terms of configuration and how easily we can overwrite values stored in the application.yaml file with external environment variables. Although the hot reload mode didn’t work well with Kotlin, I believe all the bugs related to it will be solved in upcoming releases. During development, we had to restart our running service most of the time for code changes to take effect.

Our backend API functionalities took approximately a month and a half to complete. Considering that only two developers worked on the backend, I think it was a good result. In this phase of our product lifecycle, we decided against writing automated tests due to constantly revisiting requirements and our needs. Instead, we went for manual testing for now. Once our time tracking application gets more active users, we plan to start writing automated tests using Quarkus test support, including Testcontainers and others. Developing a full-blown API, including API security with JSON web tokens and authorization in place, having database migration automatically applied during application boot time, having a flexible and maintainable code base revolving around JSON, with the ability to package and deploy our API to our Kubernetes cluster, is quite an achievement for just a month and a half of work.

Conclusion

We are glad to share that using Quarkus, Kotlin, and Postgres as the foundation of our backend API has been a fun and productive experience for us. Quarkus’s ability to experiment quickly and leverage ready-made components has made us confident that we made the right technological choice. Although there are some imperfections with hot reload and some quirks with Kotlin, we are waiting for the fixes to be made and have no doubt that Quarkus is the best solution for us.

We are working smart and hard to bring new features to our time tracking application. To achieve this, we will continue to use the great features provided by Quarkus, which dramatically reduce the time needed to roll out our features quickly. We invite you to try our time tracker at sheetty.com.