Other Resources
Supplementary materials and guidance
Finally, we’ll explore an assortment of resources, designed to provide further context and support for working with the Result library.
Topics include hassle-free dependency management, performance benchmarks, demo projects that demonstrate integration with popular frameworks, and details on the library’s licensing terms.
Bill of Materials
How to declare dependencies without having to worry about version numbers
Tracking multiple add-on versions for your project can quickly become cumbersome. In that situation, you can use the convenient Result Library Bill of Materials to centralize and align their versions. This ensures compatibility and simplifies dependency maintenance.
![]() |
Maven’s Bill of Materials POMs are special POM files that group dependency versions known to be valid and tested to work together, reducing the chances of having version mismatches. |
The basic idea is that instead of specifying a version number for each Result library in your project, you can use this BOM to get a complete set of consistent versions.
How to Use this Add-On
Add this Maven dependency to your build:
| Group ID | Artifact ID | Version |
|---|---|---|
com.leakyabstractions |
result-bom |
1.0.0.0 |
You can find the latest version of the BOM in Maven Central.
Maven
To import the BOM using Maven, use the following:
<!-- Import the BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.leakyabstractions</groupId>
<artifactId>result-bom</artifactId>
<version>1.0.0.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Define dependencies without version numbers -->
<dependencies>
<dependency>
<groupId>com.leakyabstractions</groupId>
<artifactId>result</artifactId>
</dependency>
<dependency>
<groupId>com.leakyabstractions</groupId>
<artifactId>result-assertj</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Gradle
To import the BOM using Gradle, use the following:
dependencies {
// Import the BOM
implementation platform("com.leakyabstractions:result-bom:1.0.0.0")
// Define dependencies without version numbers
implementation("com.leakyabstractions:result")
testImplementation("com.leakyabstractions:result-assertj")
}
Conclusion
We discussed the benefits of using the Bill of Materials for managing dependencies in your project. With the BOM, you can eliminate the hassle of manually specifying version numbers, ensuring consistency and compatibility across all Result libraries.
Benchmarks
Measuring performance to find out how fast Results are
Throughout these guides, we have mentioned that throwing Java exceptions is slow. But… how slow? According to our benchmarks, throwing an exception is several orders of magnitude slower than returning a failed result. This proves that using exceptional logic just to control normal program flow is a bad idea.
We should throw exceptions sparingly, even more so when developing performance-critical applications.
Benchmarking Result Library
This library comes with a set of benchmarks that compare performance when using results versus when using exceptions.
Simple Scenarios
The first scenarios compare the most basic usage: a method that returns a String or fails, depending on a given int parameter:
Using Exceptions
1 public String usingExceptions(int number) throws SimpleException {
2 if (number < 0) throw new SimpleException(number);
3 return "ok";
4 }
Using Results
1 public Result<String, SimpleFailure> usingResults(int number) {
2 if (number < 0) return Results.failure(new SimpleFailure(number));
3 return Results.success("ok");
4 }
Complex Scenarios
The next scenarios do something a little bit more elaborate: a method invokes the previous method to retrieve a String; if successful, then converts it to upper case; otherwise transforms the “simple” error into a “complex” error.
Using Exceptions
1 public String usingExceptions(int number) throws ComplexException {
2 try {
3 return simple.usingExceptions(number).toUpperCase();
4 } catch (SimpleException e) {
5 throw new ComplexException(e);
6 }
7 }
Using Results
1 public Result<String, ComplexFailure> usingResults(int number) {
2 return simple.usingResults(number).map(String::toUpperCase, ComplexFailure::new);
3 }
Conclusion
We provided insights into the Result library’s performance through benchmarking. While our metrics corroborate that most codebases could benefit from using this library instead of throwing exceptions, its main goal is to help promote best practices and implement proper error handling.
![]() |
To address performance concerns, benchmark your applications to gain reusable insights. These should guide your decisions on selecting frameworks and libraries. |
Demo Projects
Check out some REST APIs that consume and produce Result objects
To help you become familiar with this library, you can explore two demo projects that showcase how to handle and serialize Result objects within popular frameworks like Spring Boot and Micronaut. Each project provides a working example of a “pet store” web service that exposes a REST API for managing pets. They are based on Swagger Petstore Sample and you can interact with them using Swagger-UI.
![]() |
|
These projects illustrate how to develop powerful APIs using Result objects. Follow the examples to create resilient web services that elegantly handle success and failure scenarios.
Spring Boot Demo Project
Take a look at a Spring Boot-based REST API leveraging Result objects
This demo project demonstrates how to handle and serialize Result objects within a Spring Boot application. It provides a working example of a “pet store” web service that exposes a REST API for managing pets.
Generating the Project
The project was generated via Spring Initializr including features: web and cloud-feign.
Adding Serialization Support
Then Jackson datatype module for Result objects was manually added as a dependency to serialize and deserialize Result objects.
dependencies {
// ...
implementation platform('com.leakyabstractions:result-bom:1.0.0.0')
implementation 'com.leakyabstractions:result'
implementation 'com.leakyabstractions:result-jackson'
}
We use a @Bean to register the datatype module.
1 @Configuration
2 public class JacksonConfig {
3 @Bean
4 public Module registerResultModule() {
5 return new ResultModule();
6 }
7 }
API Responses
API responses contain a Result field, encapsulating the outcome of the requested operation.
1 public class ApiResponse<S> {
2
3 @JsonProperty String version;
4 @JsonProperty Instant generatedOn;
5 @JsonProperty Result<S, ApiError> result;
6 }
Results have different success types, depending on the specific endpoint. Failures will be encapsulated as instances of ApiError.
Controllers
Controllers return instances of ApiResponse that will be serialized to JSON by Spring Boot.
1 @RestController
2 public class PetController {
3 // ...
4 @GetMapping("/pet")
5 ApiResponse<Collection<Pet>> list(@RequestHeader("X-Type") RepositoryType type) {
6 log.info("List all pets in {} pet store", type);
7 return response(locate(type)
8 .flatMapSuccess(PetRepository::listPets)
9 .ifSuccess(x -> log.info("Listed {} pet(s) in {}", x.size(), type))
10 .ifFailure(this::logError));
11 }
12 }
Since failures are expressed as ApiError objects, endpoints invariably return HTTP status 200.
Running the Application
The application can be built and run with Gradle.
./gradlew bootRun
This will start a stand-alone server on port 8080.
Testing the Server
Once started, you can interact with the API.
curl -s -H 'x-type: local' http://localhost:8080/pet/0
You should see a JSON response like this:
{
"version": "1.0",
"result": {
"success":{
"id": 0,
"name": "Rocky",
"status": "AVAILABLE"
}
}
}
Using Swagger-UI
You can navigate to http://localhost:8080/ to inspect the API using an interactive UI
Micronaut Demo Project
Take a look at a Micronaut-based REST API leveraging Result objects
This demo project demonstrates how to handle and serialize Result objects within a Micronaut application. It provides a working example of a “pet store” web service that exposes a REST API for managing pets.
Generating the Project
The project was generated via Micronaut Launch including features: annotation-api, http-client, openapi, serialization-jackson, swagger-ui, toml, and validation.
Adding Serialization Support
Then Micronaut Serialization for Result objects was manually added as a dependency to serialize and deserialize Result objects.
1 dependencies {
2 // ...
3 implementation(platform("com.leakyabstractions:result-bom:1.0.0.0"))
4 implementation("com.leakyabstractions:result")
5 implementation("com.leakyabstractions:result-micronaut-serde")
6 }
That’s all we need to do to make Micronaut treat results as Serdeable.
API Responses
API responses contain a Result field, encapsulating the outcome of the requested operation.
1 @Serdeable
2 public class ApiResponse<S> {
3
4 @JsonProperty String version;
5 @JsonProperty Instant generatedOn;
6 @JsonProperty Result<S, ApiError> result;
7 }
Results have different success types, depending on the specific endpoint. Failures will be encapsulated as instances of ApiError.
Controllers
Controllers return instances of ApiResponse that will be serialized to JSON by Micronaut:
1 @Controller
2 public class PetController {
3 // ...
4 @Get("/pet")
5 ApiResponse<Collection<Pet>> list(@Header("X-Type") RepositoryType type) {
6 log.info("List all pets in {} pet store", type);
7 return response(locate(type)
8 .flatMapSuccess(PetRepository::listPets)
9 .ifSuccess(x -> log.info("Listed {} pet(s) in {}", x.size(), type))
10 .ifFailure(this::logError));
11 }
12 }
Since failures are expressed as ApiError objects, endpoints invariably return HTTP status 200.
Running the Application
The application can be built and run with Gradle.
./gradlew run
This will start a stand-alone server on port 8080.
Testing the Server
Once started, you can interact with the API.
curl -s -H 'x-type: local' http://localhost:8080/pet/0
You should see a JSON response like this:
{
"version": "1.0",
"result": {
"success":{
"id": 0,
"name": "Rocky",
"status": "AVAILABLE"
}
}
}
Using Swagger-UI
You can navigate to http://localhost:8080/ to inspect the API using an interactive UI.
License
Feel free to tweak and share — no strings attached
This library is licensed under the Apache License, Version 2.0 (the “License”); you may not use it except in compliance with the License.
You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
Permitted
- Commercial Use: You may use this library and derivatives for commercial purposes.
- Modification: You may modify this library.
- Distribution: You may distribute this library.
- Patent Use: This license provides an express grant of patent rights from contributors.
- Private Use: You may use and modify this library without distributing it.
Required
- License and Copyright Notice: If you distribute this library you must include a copy of the license and copyright notice.
- State Changes: If you modify and distribute this library you must document changes made to this library.
Forbidden
- Trademark use: This license does not grant any trademark rights.
- Liability: The library author cannot be held liable for damages.
- Warranty: This library is provided without any warranty.
