What is a Modern Application?
If you ask people what constitutes a modern application, you will get all sorts of opinions. Many will stress a particular runtime environment rather than the application itself. Some will define a modern application as something that runs in a container or even on a particular PaaS.
Of course, we Habitat folks have our own viewpoint, and it's unapologetically app-centric. We don't limit ourselves to any particular runtime. We care about the characteristics of the application itself. For us, a truly modern application has these characteristics:
- It's isolated from external dependencies.
- It's immutable once built, with that immutable artifact used in all environments and deployment scenarios.
- Its deployment artifacts can be rebuilt from source with the same outcome every time.
- It's agnostic to its operating environment; it runs on bare metal as easily as in a container or a PaaS.
- It provides external entities with a clear API for runtime configurability that exposes all of its configurable elements.
- Its packaging and deployment mechanisms are easy to use and not tied to any particular language or execution environment.
- It supports multiple deployment patterns using the same package.
These attributes of modern applications make developers' lives easier by reducing complexity and enabling scale.
Isolation and immutability
People have been dealing with the problem of application isolation for a long time. It's a question of dependencies. If, for example, you need to patch your version of the JDK, maybe because of a security problem, you can end up impacting a whole slew of applications. You don't really have isolation when patches to one component in the stack affect multiple apps at the same time. A modern application is isolated. Changing it doesn't have unintended effects on other applications.
Immutability means that you can verify that the contents of a package match what you expect (and what you've tested against in your deployment pipeline). Someone can't just open it up and mess with it. Again, just as with isolation, immutability has been with us for decades. Back in 1995, Sun would have told Java developers to build a .war or .ear file once and move that artifact to different environments and never touch it again. The problem was that there was no way to enforce that immutability. It was entirely possible for someone to unzip the file, change it, zip it back up and send it around.
While an immutable package doesn't ever change, it can expose an interface to allow the application to be dynamically configured at runtime. It is essential to be able to adapt application behavior for a particular environment, such as QA or production. This means being able to tune the application, maybe to work with a particular database or to have certain performance values, such as the appropriate number of CPUs to support some number of threads. As an application developer, you want to make it clear to outside entities which elements of your application are configurable, and define how it should respond to those configuration changes.
Can be rebuilt from source
Comprehensive version control of source code underlies all modern application development practices, including DevOps, the Agile process, and continuous delivery. It's also important for modern change management practices, governance, and auditing of business-critical systems. The source code for modern applications—including the automation that puts them into production—is tracked by version control, and the deployed artifacts can be recreated from the source code.
Independence from runtime environments
Another desirable kind of isolation is between the application and its runtime environment. A failure to separate the concerns of the app from the concerns of the environment is a major cause of complexity. Furthermore, runtime environments such as containers, PaaS offerings, and the cloud are rapidly evolving and changing. Any application tailored to a particular environment is liable to quickly become legacy software. Finally, the focus of these environments is increasingly on workload placement rather than on application frameworks. For all these reasons, the most robust way to develop modern applications is to make them independent of the particular technology that will be used once the workload is deployed. Portability across environments is key.
API for runtime configurability
Modern application architectures are service-oriented, and those of us who work with Habitat believe that the deployment, configuration, and management aspects of modern applications should be service-oriented as well. Application components should present an API that lets external entities understand which parts of the application are configurable or tunable. Defining this interface is critically important to clarify the respective responsibilities of the app itself and its environment. Such an interface is essential if the application is to be independent from the runtime environment.
Language agnostic packaging
Many packaging formats for deployable artifacts are tied to particular languages. This is the case, for example, with Java .war and .ear files. We believe that the packaging of the application should be independent of the implementation language of the application.
Support for multiple deployment patterns
Modern applications need to support multiple deployment patterns. We'll talk about just two of these here: peer-based scaling and application update strategies.
Peer-based scaling. Today, applications are composed of many services or microservices. There's a good reason for this approach. If you can deploy small components quickly, you can iterate on smaller pieces of your business and run experiments and try things out. You can innovate faster.
The strategy for scaling the services of a modern application is often peer-based. For example, a database system might be quite distributed and have a number of peers that talk to one another and have complex relationships with each other. There's often a leader that receives all the database write requests and a number of followers where applications do their reads.
The chosen deployment pattern for peer-based relationships should be independent of the immutable artifact because you want to be able to deploy that same artifact to all sorts of environments, from development to QA to production. While the workload remains the same, the connections between running instances of a service could be different. For development, you probably want something straightforward, like a standalone deployment of a component. In production, you probably want a more sophisticated topology, perhaps with a leader and multiple followers.
Application update strategies. Scaling behavior is not the only reason for multiple deployment patterns. Another is that an application that supports multiple deployment patterns can be aware of its own update strategy. There might be one policy to deploy the latest version of the application in testing and another policy for production.
We who work on Habitat think that there's a handful of patterns for deploying updated versions of application services. You might deploy a component so that only one piece is down at a time, or a percentage of pieces. Perhaps you have a canary, where you start up just one updated service instance and make sure that it passes its smoke tests before you deploy the update more broadly. We're investigating other patterns and we're sure we'll learn more as time goes on.
Developers should be spending their time creating features that delight customers and move the business forward. Instead, because they're forced to commit to particular technology choices early in the development process, they're limited in what they can do with the application and writing low-level code that's tightly bound to the choices of infrastructure, runtime environments and other technologies used in modern applications.
Here is a highly abbreviated list of some of the questions developers currently have to ask themselves. For infrastructure, do I want to run on bare metal or do I want virtualization? On the other hand, maybe I should use the cloud and IaaS. But there are so many providers. Which one is best?
Of course, perhaps I should use a PaaS product. On the other hand, containers are really great. Of course, then I need to use a discovery service. I don't know much about them. I better investigate. Do I need an orchestration tool on top of it? There are a few out there. What's the difference between them?
Habitat provides much of the standard "plumbing" that developers need for their applications. With Habitat, developers can defer choices about specific infrastructure and runtime requirements until late in the development cycle, which means those environments won't dictate the design of the application. Instead, developers can spend their time actually writing applications and let value to the business guide their design choices. With Habitat, the application comes first.