Superlightweight Microservices and FaaS - Leveraging Microframeworks in the Cloud

Microframeworks are designed for applications with clearly scoped functionality. Their rise is largely driven by Function as a Service (FaaS) offerings in the cloud. These frameworks not only promise a small memory footprint but more importantly fast startup times, which are essential in FaaS contexts. This is increasingly made possible through support for GraalVM, which enables native executable generation. In addition, Microframeworks often treat containerization as a first-class concern, allowing for easy integration into modern build pipelines. This article explores the concept of Microframeworks in general, with a focus on several Java based options. To evaluate them in practice, an ELIZA (Wikipedia)1 chatbot was to be implemented and integrated into Microsoft Teams, across multiple frameworks.

What is a Microframework?

Microframeworks are minimalist frameworks for web applications. Unlike full-stack frameworks, they typically omit features like role and access control, database abstraction layers, or web templating engines. Depending on the implementation, some Microframeworks offer optional extensions or can be expanded to resemble full-stack frameworks, which makes the line between the two somewhat blurry. It’s not unlikely that some Microframeworks will gradually evolve in that direction. In informal usage, the term Microframework is also applied to curated sets of libraries packaged with preconfigured archetypes2, essentially functioning as software toolkits.

Key characteristics

  • Lightweight scaffolding with a limited feature set
  • Prioritizes fast startup and low resource usage
  • May lack authentication, ORM, or templating by design
  • Best suited for Microservices, FaaS, and serverless deployments
  • Designed for small, well-defined deliverables
  • Often includes GraalVM support for native image compilation

In the Java ecosystem, the term Microframework commonly implies GraalVM3 compatibility.

Framework vs. Application Server vs. Software Toolkit

Frameworks and application servers define an application’s structure and control its execution flow, often through inversion of control. They consist of cooperating classes that actively shape the lifecycle of the application. In contrast, software toolkits, being collections of supportive libraries, behave passively. They are invoked as needed, operate in a localized way, and can be flexibly extended. Their APIs align directly with the application’s logic and flow, making them ideal for modular, on-demand functionality. Frameworks and toolkits are typically packaged during the build phase as part of the final deliverable. Application servers, on the other hand, provide their features at runtime, meaning the application is deployed into an existing runtime environment, rather than embedding one.

Use Cases for Microframeworks

Microframeworks are often used to implement functionality in the context of Serverless Computing or Function as a Service (FaaS), where tasks are triggered by events and typically run for a short duration.

FaaS refers to a serverless cloud model where the underlying infrastructure is abstracted away. Functions (excessively small components) are provisioned dynamically in the cloud, billed by the second, and triggered in response to events, which they may also emit. Event producers and consumers are usually other cloud-native services or functions, connected via an Event-Driven Architecture.

Such triggers often come in the form of Cloud-Events4, which explains the high adoption of Microframeworks in cloud environments. In practice, this is common in areas such as:

  • Operations and monitoring, where FaaS is used to react to events in CI/CD pipelines
  • Stream processing, where well-defined data chunks (e.g. IoT streams or event bus messages) are processed independently
  • Scheduled batch jobs, which execute recurring tasks asynchronously
  • REST or GraphQL APIs, where HTTP interfaces expose services or FaaS functions

In all these scenarios, Microframeworks are a natural fit, especially when dealing with small, coherent processing units and well-scoped functionality, typically in workloads with low data volume and short runtimes (often around 1 MB per execution5).

Typical Use Cases:

  • On-demand workflow triggers via CloudEvents
  • Connecting cloud services (e.g., storage, UI, messaging)
  • Asynchronous stream and batch processing
  • Exposing services via REST or GraphQL APIs
  • Implementing a small number (usually fewer than five5) of focused functions
  • Handling small payloads per execution (generally < 1 MB5)

In short, Microframeworks are especially well suited for FaaS and Serverless Microservices.

Serverless and FaaS

Traditionally, Java, especially with Spring Boot or JEE, has been used in large-scale or monolithic applications. With the rise of Spring Boot and, later, MicroProfile for JEE, Java has firmly established itself in the Microservices space. However, FaaS and serverless architectures introduce a crucial shift in requirements: Whereas startup time wasn’t critical in long-running monoliths or Microservices (as long as throughput was acceptable), in serverless scenarios, startup latency becomes a primary concern.

In an Event-Driven Architecture, functions must respond to triggers quickly while avoiding idle costs. In the cloud, every running service incurs cost, even when doing nothing. Yet, all these independent units still need to be integrated into a functioning system. FaaS is a natural fit here: small, stateless functions triggered by Cloud-Events orchestrate workflows by chaining together various cloud services.

The key strategy: Provision a function only when invoked, execute it, and shut it down immediately to avoid costs, a model that requires extremely fast startup. This is precisely where GraalVM comes into play.

GraalVM

When Java applications run on a traditional JVM, they achieve peak performance only after a warm-up phase, which follows a heavy startup phase. This makes freshly launched applications slow to respond, a major drawback for FaaS, where cold-start latency is critical.

What benefits long-running services, namely JIT (Just-in-Time) compilation and dynamic profiling by the JVM, becomes a liability in the serverless world. To address this, Oracle introduced the GraalVM in 2019, a polyglot virtual machine that allows Java (and other languages) to be compiled ahead of time into native executables with near-instant startup. This native image support allows Java applications to meet the strict performance demands of FaaS and can fully replace the JVM in such contexts.

  • Drastically reduce startup time
  • Increase overall performance
  • Enabling polyglot development
  • Supporting diverse runtime environments

GraalVM is available in two editions: The Community Edition (open-source) as well as the Enterprise Edition, which differs mainly in runtime performance, not in functionality. In addition to Java Bytecode, GraalVM supports other languages like JavaScript, Ruby or Python. Its goals include: Drastically reducing startup time, increasing overall performance, enabling polyglot development and supporting diverse runtime environments (e.g. Oracle DB, OpenJDK, Android, iOS), following the principle of “write once, run anywhere”.

That said, not all Java applications are GraalVM-ready out of the box. Special attention is required when:

  • Using native libraries via JNI
  • Relying heavily on reflection, which has limited support in native images
  • Packaging external resources (e.g. media files), which must be declared explicitly

Positioning Microframeworks in the Java Ecosystem

Where do Microframeworks fit within the broader Java landscape? For many years, application servers like J2EE and later JEE dominated the enterprise world. These platforms hosted countless business applications and formed the foundation of large-scale enterprise systems. The emergence of the Spring Framework and later Spring Boot introduced a lighter-weight alternative. Spring brought features like dependency injection and presented itself as a leaner option compared to the often heavyweight application servers of the time. Today, Spring Boot is widely considered a default choice in many enterprise environments.

Interestingly, several concepts from the Spring ecosystem have since influenced JEE itself, most notably CDI (Contexts and Dependency Injection).

As Microservices gained popularity, JEE gradually fell out of favor. Its comparatively heavyweight nature made it less suitable for the demands of highly modular, service-oriented architectures. In contrast, Spring Boot continued to rise, favored for its flexibility, faster bootstrapping, and developer-friendly design. JEE’s MicroProfile is an attempt to address Spring Boots’ leadership in this areas.

Framework categories

In response to the shifting demands of modern software architecture, MicroProfile emerged as a lightweight standard derived from JEE, tailored specifically for microservice development within the Java ecosystem. Unlike traditional JEE, MicroProfile is designed to support streamlined applications and is well-suited even outside of traditional application servers, packed with proven standards:

  • CDI (Contexts and Dependency Injection)
  • RESTful APIs (JAX-RS)
  • JSON processing (JSON-P / JSON-B)

As a result, several Java Microframeworks have embraced MicroProfile, combining its open, vendor-neutral specifications with the compact, fast-starting nature of lightweight frameworks. First introduced in 2016, MicroProfile is an initiative of the Eclipse Foundation. It defines a set of specifications for developing cloud-native microservices, including standards for CDI, RESTful APIs as well as JSON processing.

These specs are directly aligned with the JEE ecosystem, lowering the barrier to entry for JEE developers transitioning to microservices. This compatibility allows developers to reuse familiar libraries from the JEE world while adopting modern, cloud-native approaches. Even those who haven’t yet moved from JEE to microservices can use MicroProfile-based Microframeworks to take a direct step toward FaaS and serverless architectures.

The Showcase

To explore the practical use of various microframeworks, an ELIZA chatbot was used as a showcase project. This chatbot is integrated with Microsoft Teams as the frontend and implemented multiple times using a selection of different microframeworks.

ELIZA was one of the first chatbots, developed by Joseph Weizenbaum in 1969. It was created to explore the possibilities of natural language interaction between humans and machines, a concept closely related to the Turing Test.

Eliza Terminal-Chat

Each implementation delivers an ELIZA service built with one of the Microframeworks under examination. From the perspective of Microsoft Teams, however, these implementations are entirely interchangeable, as they expose identical REST APIs. The ELIZA services were structured according to the programming models of each framework. REST endpoints were implemented for both stateless and stateful interactions, and dependency injection where supported (omitted in the classic approach without a microframework) was used. To ensure consistency across implementations, each framework’s official Maven archetypes or equivalent project templates as a starting point was used.

This showcase allows us to directly compare the selected Microframeworks in terms of how they handle the same programming task.

Integration with Microsoft Teams

Microsoft Teams supports the integration of external services through Outgoing WebHooks. These webhooks are triggered by sending a chat message to a designated user, which is then forwarded to the corresponding backend service. With the showcase, an Outgoing WebHook for a virtual user named “Eliza” was created, which was being addressed in the Teams chat using the @Eliza keyword.

… The Outgoing Webhook acts as a bot and searches for messages in channels using @mention. It sends notifications to an external web service and responds with rich messages, which include cards and images. It helps to skip the process of creating bots through the Microsoft Bot Framework …6

When a user sends a chat message addressed to @Eliza, Microsoft Teams forwards the message to the configured ELIZA service endpoint for processing:

Integrating with Microsoft Teams

For the showcase, the ELIZA service was implemented using the following microframeworks:

  • Helidon: A lightweight Java framework by Oracle, offering reactive and MicroProfile based APIs
  • Micronaut: A modern JVM-based framework designed for fast startup, low memory usage
  • Quarkus: A Kubernetes native Java framework optimized for fast startup and low memory usage
  • Spring Boot: Included for reference and comparability, with version 3 it supports native images out of the box

In addition, a “classic” variant was implemented, composed of individual libraries without relying on any microframework, by solely using the REFCODES.ORG software toolkit.

Chatting with Microsoft Teams

To simulate a realistic production environment, each ELIZA service implementation is packaged into a Docker container by a CI (Continuous Integration) pipeline following the build process and then deployed to the target system. The containers are based on Alpine Linux and include a Java runtime, resulting in minimal Docker image sizes optimized for deployment.

While the setup reflects a typical microservices architecture, particular emphasis was placed on the feasibility of building native executables and evaluating their impact on startup times.

Comparing Frameworks in Practice

To illustrate the differences between the microframeworks and a widely used enterprise framework, the ELIZA service was not only with Helidon, Micronaut, and Quarkus implemented, but also with Spring Boot for reference.

Originally developed as an alternative to JEE, Spring Boot is a full-featured application framework that does not require a dedicated application server at runtime.

Helidon

Backed by Oracle, Helidon is a microframework introduced in 2018 and offered in two variants:

  • Helidon MP: Implements the MicroProfile specification and includes support for CDI (Contexts and Dependency Injection).
  • Helidon SE: A more minimal version focused on reactive programming and [non-blocking I/O}(https://en.wikipedia.org/wiki/Asynchronous_I/O), and does not include dependency injection.

Both variants are built on Netty, an asynchronous client-server framework that implements the non-blocking I/O (NIO) model. Netty enables Event-Driven interaction over network protocols like TCP or UDP.

Both Helidon SE and Helidon MP provide support for GraalVM native images, making them suitable for building FaaS style native executables.

Micronaut

Developed by Object Computing, Micronaut was launched in 2018 as a response to traditional application frameworks. Unlike Helidon, Micronaut does not follow the MicroProfile standard but instead adopts a programming model similar to that of Spring Boot. Crucially, Micronaut avoids reflection entirely at runtime being a major advantage when working with GraalVM native image compilation. Instead, it relies on compile-time dependency injection, supported by tight integration with the Java compiler.

Thanks to the consistent avoidance of reflection mechanisms, Micronaut applications are highly optimized for FaaS use cases where fast startup and native executables are essential.

Quarkus

Introduced by Red Hat in 2019, Quarkus embraces a container-first philosophy7, making it ideal for cloud-native deployments. It supports MicroProfile and offers excellent tooling for building native executables with GraalVM. At its core, Quarkus leverages the Vert.x toolkit, enabling components to communicate via an embedded event bus, promoting asynchronous, loosely coupled architectures.

A key strength of Quarkus is its extension system, which simplifies the use of GraalVM by automatically applying required configuration (e.g., for reflection or resource access) that would otherwise have to be set manually during development.

Classic (Toolkit-Based Approach)

When building FaaS functions with clearly defined, limited scope, one might question whether a full framework is necessary at all. Can similar results be achieved with a “classic” library-based approach? Following the motto “Don’t prevent the programmer from doing what needs to be done8, an ELIZA service using a manually curated set of libraries was also implemented: No framework involved! The project was configured entirely by hand, including:

  • Reading configuration files
  • Wiring up components
  • Publishing REST endpoints

While each step was supported by external libraries, nothing came pre-integrated. The software toolkit used was curated and consolidated by the author9.

This approach demands more effort but offers a clear, educational perspective on what a framework typically automates: Living up to the philosophy of “educational source codes”. It serves as a helpful contrast to the “batteries-included” nature of modern frameworks.

Insights

For all implementations, a Debian-based Linux build environment with GraalVM version 21.3.0 (Java 17) was used, the native-image10 extension, and a current version of Maven. The build configurations was kept minimal, relying largely on the default Maven plugin settings provided by each framework’s official archetype. Overall, the source code across Helidon, Micronaut, and Quarkus is strikingly similar, especially with Helidon and Quarkus, thanks to their shared use of MicroProfile, resulting in nearly byte-identical implementations. Only the classic toolkit-based implementation differs significantly. Build configuration for native image generation also varies slightly by framework.

Helidon

During the testing period, Helidon only introduced minor version updates11 (i.e., changes to the second digit in version numbers), none of which caused compatibility issues in the showcase.

Creating a native executable with Helidon was straightforward:

mvn package -Pnative-image

This uses the preconfigured native-image Maven profile provided by the archetype.

Micronaut

Micronaut introduced a major version update12 (first digit) during the evaluation, which brought some expected breaking changes:

  • Due to legal changes, the javax.* namespace was deprecated, javax.inject had to be replaced with jakarta.inject, easily handled using an IDE’s Organize Imports function.
  • A more subtle change affected REST controllers: URI paths are no longer implicitly inherited from class-level annotations. Each REST method now requires an explicit path declaration (e.g., via @Path), even if it’s just an empty string. This change caused some unit tests to fail after the upgrade.

To build a native executable, Micronaut now requires a tweak in the pom.xml: You must set the packaging type to native-image:

<packaging>native-image</packaging>

Once updated, a simple mvn package will produce a native image.

Quarkus

Quarkus was the only candidate to exhibit version-related quirks related to the Java version:

  • With JDK 17, the build failed due to incompatible class versions in older releases.
  • In the current release, it no longer fails outright but emits confusing error messages.

However, JDK 11 worked reliably without errors. The root cause seems to be related to JaCoCo code coverage instrumentation during unit testing, though this was not investigate any further.

Once resolved, upgrading to the latest major version of Quarkus13 worked without any further adjustments.

To build a native executable:

mvn package -Pnative

This uses the predefined native Maven profile.

Classic (Toolkit Approach)

The classic implementation, built from curated libraries, only required a hotfix version upgrade14 (third digit), which caused no issues and required no code changes. Interestingly, this version had the most compact codebase overall. However, this comes at the cost of manual setup for every aspect: configuration loading, component wiring, REST endpoints, and more. Unlike frameworks, this approach makes no assumptions and automates nothing.

That said, the classic toolkit approach is flexible and well-suited for use cases beyond microservices, such as CLI tools or background workers15.

To generate a native executable:

mvn package -Pnative-image

However, depending on the external libraries used, additional effort may be needed to configure JNI, reflection, resource inclusion, or serialization for GraalVM compatibility.

Deliverables and runtime characteristics

After implementing the ELIZA service with various microframeworks, the resulting deliverables were examined, that is, the files typically deployed in production. Of particular interest were the size of these deliverables and their startup times, comparing both traditional JAR files and native executables. While these measurements were performed on a laptop using the Windows Subsystem for Linux (WSL) and are therefore not representative of production performance, they provide a helpful relative comparison between the frameworks.

One particularly surprising finding was the significant variation in FatJAR sizes, that is, the self-contained JAR files including all dependencies:

  • As expected, the classic library-based implementation produced the smallest FatJAR.
  • Quarkus, on the other hand, generated the largest FatJAR, but redeemed itself with the smallest native executable among all candidates.

The difference in startup times between JARs and native executables was also dramatic. Although Helidon produced the largest native binary and had the “slowest” native startup, even this startup time was still very fast in absolute terms.

Framework JAR Size [KB]16 Native Image Size [KB]17 JAR Startup Time [s]18 Native Startup Time [s]18 Startup vs. Spring Boot [%]19 Relative Native Startup [%]20
Helidon 15,382 107,888 10.187 0.2348 2.15% 136.51%
Micronaut 14,108 61,480 4.89 0.2034 1.87% 118.26%
Quarkus 30,459 49,654 4.95 0.1720 1.58% 100.00%
Classic21 5,658 62,682 3.21 0.2284 2.10% 132.79%
Spring Boot 17,625 N/A22 10.900 N/A⁷ 100% 6,337.21%

On average, the native executables in the showcase achieved startup times of just around 2% compared to their JAR-based counterparts. With cold starts in the range of ~200 ms, this makes them a highly viable option for Function-as-a-Service (FaaS) scenarios.

According to Amazon AWS, typical cold starts for Lambda Functions range between 100ms and over 1 second and it’s worth noting that the measurements were taken on laptop hardware under WSL, not on optimized server infrastructure.

… According to an analysis of production Lambda workloads, cold starts typically occur in under 1% of invocations. The duration of a cold start varies from under 100 ms to over 1 second …23

Microframeworks compared with Google Trends

As the saying goes, competition fuels innovation, the microframeworks showcased here are actively competing for the attention of the developer community. According to Google Trends24, Quarkus currently leads in popularity, followed by Micronaut and Helidon. However, based on the showcase, a definitive recommendation cannot be made. The landscape is evolving rapidly, both in terms of microframework capabilities and ongoing developments around GraalVM. It’s an exciting space to watch, and the right choice often depends on the specific use case, team preferences, and ecosystem requirements.

Feature Comparison Overview

The Java ecosystem offers a broad range of frameworks and software toolkits for building microservices and FaaS applications. In the showcase, selected microframeworks were used hands-on for the according implementions: Helidon, Micronaut, and Quarkus, along with Spring Boot as a reference. To help put these frameworks in context, several additional frameworks and toolkits were evaluated, drawing on available data and documentation to create a feature-based comparison matrix.

The following frameworks were considered in the analysis: Akka, DropWizard, Eclipse Vert.x, Helidon, http4k, Javalin, Jooby, Ktor, Micronaut, Quarkus, Ratpack, Spring Boot, Spring Fu

Supported Languages and Build Tools

This table shows which programming languages and build tools are first-class citizens for each framework. This doesn’t mean other tools or languages can’t be used, but those listed are officially and natively supported.

Framework Java Kotlin Scala Groovy Maven Gradle
Akka    
DropWizard      
Eclipse Vert.x
Helidon      
http4k        
Javalin    
Jooby    
Ktor      
Micronaut  
Quarkus  
Ratpack      
Spring Boot      
Spring Fu    

Supported Standards

The following table shows which common Java standards and APIs are natively supported by each framework. If a standard is not listed, it might still be usable via extensions or third-party libraries.

Framework MicroProfile GraalVM JSR 250 JSR 330 JPA JAX-RS JWT OAuth2 Reactive Streams
Akka           25    
DropWizard          
Eclipse Vert.x            
Helidon  
http4k                
Javalin                
Jooby            
Ktor              
Micronaut  
Quarkus  
Ratpack                
Spring Boot    
Spring Fu                

Supported Web Containers

This matrix shows which web containers (HTTP servers) are supported by each framework. Some frameworks support multiple containers depending on configuration or plugins.

Framework Jetty Netty Tomcat Undertow
Akka        
DropWizard      
Eclipse Vert.x        
Helidon      
http4k  
Javalin    
Jooby  
Ktor  
Micronaut
Quarkus        
Ratpack      
Spring Boot
Spring Fu        

Final Thoughts

As developer coming from a Spring Boot background, it was surprisingly easy to get up to speed with the other frameworks. Since microframeworks are typically used to implement focused, self-contained functionality (e.g., FaaS), the source code across frameworks remains relatively uniform, as does the configuration of the build process and generated deliverables. This means that, as long as the business logic remains cohesive and modular, it’s often possible to switch microframeworks later on with relatively little effort. In most cases, choosing any of the current top 3 frameworks is unlikely to be a critical mistake.

To ensure alignment between your requirements and the capabilities of a given framework, consulting the decision matrix provided earlier is recommended alongside keeping an eye on ongoing developments in the ecosystem. While the overall support for native executables via GraalVM is still maturing, the GraalVM platform itself is quite stable. All frameworks in the showcase offered basic to good support for native image generation. However, external libraries must also provide GraalVM compatibility, for example, at the time of writing, using an in-memory database inside a native executable was still problematic.

That said, support for native images in enterprise-grade use cases is improving steadily:

  • Through new GraalVM releases and ongoing optimizations
  • Via library maintainers adapting** their code to GraalVM constraints
  • As the developer community becomes more familiar** with the GraalVM ecosystem

It’s worth revisiting GraalVM compatibility periodically, especially when beginning a new project or revising deployment strategies. If you’re considering working without a full framework, curated libraries like DropWizard may be a viable choice, although only being touched briefly here, as the focus of this article was on frameworks. For entirely different architectural models, frameworks like Akka or Vert.x offer powerful alternatives through their focus on the Actor Model and Event-Driven concurrency.

  1. ELIZA, developed 1969 by Joseph Weizenbaum, is one of the first chatbots 

  2. For an introduction to Archetypes, see Introduction to Archetypes – Maven 

  3. In turn, GraalVM compatibility implies support of native images 

  4. A Cloud-Event is a standardized message format used to describe events (such as file uploads, database changes, or user actions) across different cloud services and platforms. 

  5. See A Review of Serverless Use Cases and their Characteristics (PDF)  2 3

  6. See Create an Outgoing Webhook - Teams (Microsoft Learn) 

  7. See What is Quarkus (Red Hat) 

  8. See The Spirit of C (Andreas Zwinkau) 

  9. See REFCODES.ORG Maven-Artifacts (Maven Central) 

  10. If the GraalVM is in the path of the OS, the native-image extension is installed using gu install native-image 

  11. Helidon version 2.4.1 was used at the time of completion of the article 

  12. Micronaut version 3.2.5 was used at the time of completion of the article 

  13. Quarkus version 2.6.2 was used at the time of completion of the article 

  14. REFCODES.ORG Version 2.2.2was used at the time of completion of the article 

  15. Some examples can be found at https://www.metacodes.pro/downloads 

  16. The size of a FatJAR including all dependencies such as servlet containers etc. is measured here. 

  17. Status at the time of completion of the article 

  18. Measured using time <executable>, user-mode CPU time + kernel CPU time, as of Real, User and Sys process time statistics (Stack Overflow)  2

  19. % = 100 / slowest JAR startup * native startup under consideration 

  20. % = 100 / fastest native startup * native startup under consideration 

  21. Classic, here the REFCODES.ORG software toolkit 

  22. Only experimental support at the time of completion of the article with spring-native 

  23. Operating Lambda: Performance optimization (AWS Compute Blog) 

  24. Trend analysis such as these should be treated with caution, as they are only based on search terms that may be fuzzy 

  25. Akka only ever refers to the older JSR 311, see e.g. akka - rest