Last week, we updated the core Stormpath product – our REST API – to Spring Boot. This is a major architectural upgrade for our codebase and it simplified application development and deployment for our whole team, for both software engineering and operations.
And it was shockingly easy.
This blog post will cover the entire migration, from our initial decision-making process to configuration, architecture, ecosystem specifics, and even how Spring Boot simplified our deployment. We hope this will allow other Java teams to benefit from what we learned.
Our application was a traditional Spring app secured by Apache Shiro and configured with both XML and Java Config. When we added Spring Boot, it didn’t replace Spring of course (since Spring Boot is built on top of Spring), but it simplified much of our architecture by adding a layer that helps automate configuration and deployment while making it easier to implement features, as well as prime our architecture for modular microservices (more on that later).
Because we have thousands of customers in production and are constantly developing new features, we did extensive testing to make sure everything still worked as expected. We also built some custom Spring Boot Starters, including a nifty one for real-time stream messaging with Apache Samza. And even though our software stack is a few years old and involves a lot of business edge cases and intricate code paths, we were able to make the transition in just three weeks.
In the process, we tried to use as much of the Spring Boot and Spring Cloud ecosystem to remove as much custom code as possible. Spring Boot allows us to do that in a clean, plugin-oriented way. Here’s what we did:
Much of what makes Spring Boot so convenient starts with the way the Spring team decided to handle configuration. The configuration set-up is simple, consistent, and best of all, mostly automated, which stabilizes the development environment and also expedites deployment.
Traditional Spring framework apps used XML for configuration, and software engineers use XML to declare dependencies and object graphs. With Spring Boot, instead of a long XML file to represent our product stack, our config is now ‘Bootiful’ type-safe Java.
Java-based config allows our IDEs to spot problems and give us warnings when our configurations don’t line up. (XML also did that, but in Java, the relationships are a little cleaner and easier to see.) This allows our team to start the product very quickly, with no need to see the entire Spring config, and no time-consuming trips down dependency rabbit holes.
The Spring Boot team took an opinionated approach to where the config files reside. This reduces the guesswork and effort around overriding default settings when the product moves to production (e.g. a password in prod is not the same one your developers have access to).
We had a custom-built config mechanism that managed this, but the Boot implementation is more idiomatic, so everyone on the team has consistent expectations about where config resides. It’s easier to understand and manage once you learn the convention.
Spring Boot also relies on an auto-configuration mechanism, so the many starters in the Spring Boot ecosystem are set to sensible defaults. All we have to do is override defaults with values relevant to our production infrastructure – relatively minor configuration changes only.
The Spring Boot Starter architecture and ecosystem reduced the amount of custom code we had to maintain, particularly around common functions and services. Because our underlying infrastructure is service-oriented, it supported an easier transition to a microservice-based architecture that combines starters from the eco-system with ones we built ourselves.
The Spring Boot Starter ecosystem gives us a huge amount of out-of-the-box functionality that traditionally we used to build ourselves. Instead of defining beans and wiring them ourselves, we’re using Spring Boot Starters wherever possible. This allows us to build nimbly using a modular architecture, gathering starters as we need to support various services, and then assembling them together to make the final application with simple config settings.
We use the ecosystem integrations for Zookeeper, Kafka, Cassandra, JMS messaging, SMTP mail servers and many others. Historically, we’ve had to build, integrate and configure these services ourselves, but now we just drop in the necessary starter dependency, and boom: the starter is enabled and auto-configured!
We started building Spring Boot starters for our own product. A few months ago, we launched the Stormpath Spring Boot Starter, with the idea that we would soon use it in other applications we wrote for our own needs. It handles a lot of user management features, and we eat our own dog food.
More recently, we developed a Spring Boot Starter for Apache Samza, which we open-sourced under the Apache 2.0 license. Samza is a real-time streaming product that traditionally requires YARN, a complex infrastructure to launch JVMs and manage memory across them. I was frustrated by how complicated it is to deploy Samza, so we reverse engineered the parts that launch a Samza container, and turned it into a Spring Boot Starter.
Now, anytime we have a Samza microservice that consumes Kafka events, we can skip the complex deployment process (unzipping a tarball, running a shell script to launch the process and connecting to a YARN environment, etc.). It’s now just a simple Spring Boot application that can be launched from the command line, and it changed our entire microservices architecture in a week’s worth of work.
The most impressive upgrade in the transition was in how it simplified deployment, for both Operations and Engineering teams. This came through a number of smart features:
Before Spring Boot, our application deployed as a WAR file inside of a Tomcat web application container. This is pretty standard, and certainly beats many legacy options, but with Spring Boot, Tomcat (or Jetty, Undertow, etc.) is embedded in the framework.
The big benefit is that our software engineers can easily configure, update, and manage Tomcat at a development level via a set of properties without forcing our ops team to do it. Whenever we deploy a new version, they don’t have to worry much about whether a configuration setting needs to be changed or what version of Tomcat we’re on. It’s one less piece of server infrastructure to configure, maintain, and update manually, leaving Ops free to rely on the software engineering team, as well as our Continuous Integration pipeline to upgrade processes.
It’s a lot cleaner and simpler for Operations, and a light lift for the engineers as well.
Spring Boot bundles your application into one JAR that can be distributed and executed on an operating system. To start the application, you just run a simple command to start up that JAR file.
From a distribution management perspective, once that artifact is built and put into an artifact server or a binary repository, Operations only has to pull one file to deploy into the production infrastructure. This naturally limits the amount of coordination required and simplifies our CI pipeline. Once a Spring Boot artifact is built, it’s immediately made release candidate that can be tested in the CI environment and promoted to production if everything goes smoothly.
One great Spring Boot feature is that our core product and all our microservices are launched the exact same way, using one command. Once you know how to launch one app, you know how to launch all of them. No more learning half a dozen options to launch a product.
When you deploy a complex application to the cloud, your configuration management, messaging options, service discovery, load balancing, and routing, etc all need to play nicely with each other, and traditionally, it’s a lot of work to coordinate and debug.
Spring Cloud is an ecosystem on top of Spring Boot that gives out-of-the-box, opinionated approaches of configuration management, service discovery, intelligent routing, load balancing, and more common infrastructure services.
The Spring team has started to automate these common SaaS patterns in the form of Spring Cloud projects, each of which is a group of Spring Boot starters. We would have had to build all this automation – now we can configure a good chunk of all of these critical services as simple application name/value properties.
Although everyone on the Stormpath engineering team is very familiar with the Spring stack, (and some of us since 2002 before it was even called Spring), Spring Boot doesn’t require any prior Spring knowledge. Our Spring Boot migration did require researching lots of Spring Boot Starters to see which ones were valuable to us, however. I believe the effort was well worth the time it has saved us in new feature development and deployment simplicity and speed. Across the team, we’re thrilled with the upgrade.
We welcome your input and pull requests on our open-source Spring Boot Starters, which are released under the Apache license:
Thanks for reading and let us know what you think in the comments section below!