The evolution of software might be told as a story of innovation in delivery channels - the mainframe to the personal computer, hardware-specific applications to cross-architecture compilation, desktop to mobile, on-premise to cloud. Looking closer, each of these new delivery methods represented a new opportunity for developers to reach more users with the same application. The benefits are obvious: write once, make available anywhere. We might use the word portability, then, in very general terms, as a characteristic of software: highly portable software can be written once and deployed anywhere.
Portable applications require less development and operational effort even as they are exposed to more potential users. This same value, though, also applies to the internal operations of software teams. In modern microservice architectures developers also play the role of consumers- consuming the services and APIs created by other teams both inside and outside of their organization. In this way, application portability matters greatly to the internal operations of software companies.
In this article, we'll shed light on what portability means in the context of cloud software and why it's important for both your customers and your team members.
Software teams generally use the word "environment" to describe the context in which an application runs. It's a broad term- you might use it to refer to a specific machine, an OS, or an entire network. Importantly, the word captures a fundamental concept with a serious implication: context differs across environments, and if your software depends on context then its behavior will differ across environments! This distinction is often the culprit of poorly functioning software: it seemingly works in one place, but is buggy when you run it somewhere else.
Picking apart the logic of your software from the characteristics of the environment is a central skill in developing any software application, and it's a skill that strives toward the above prize: portability. A developer who has isolated their software from its environment finds themselves with an elegant bundle of business logic that will behave the same regardless of where it is run: their own machine, the company QA environment, their production cloud; even their customer's cloud!
A smart business will limit their hard dependencies if given the chance. Vendor lock-in introduces a central point of failure that exposes a company both to disruptions in service and the pricing whims of the vendor. Horizontal application portability is characterized by minimizing environment switching costs such that an IT department can avoid vendor lock-in. If you can run your application just as easily on GCP or AWS you avoid pinning your company to the service-uptime and pricing of one particular cloud provider.
It is not uncommon for environments to multiply rapidly in even small software teams: developers need to run the application locally, quality engineers in a test environment, sales reps in a demo environment, and operators of course run the application in staging and production environments.
The develop/test/demo/deploy lifecycle has a cost that is directly correlated to the portability of the application. Software that requires much environment-related configuration and tuning will cost time and effort as new versions move through the lifecycle. Portability saves time and mental overhead for anyone involved in moving new versions of the software across environments.
For security reasons, many potential customers prefer to run vendor software on their own premises. If a business makes rigid software- requiring specific operating systems, cloud providers, embedded security, and extensive environment configuration- the business is inadvertently limiting their addressable market to those customers that satisfy these conditions. A company that ships portable software, on the other hand, removes these restrictions on their addressable market.
Building software that is portable actually encourages patterns that support a host of other worthwhile properties. If you make it easier for your software to be run here or there, it follows that it is easier to run it here and there: supporting replication within and across environments and enabling engineers across teams and orgs to operate the software themselves. In the same way, an application that provides full portability and is easy for developers to run is easier to build on top of: an application with great portability lends itself to great extensibility.
So how do we know if our apps and services are portable? Can they be portable in some ways and not others? In order to determine this for ourselves, let's evaluate three different dimensions in which our application can be portable:
The first dimension of portability is crucial to operating cloud applications at scale - scaling and replication. The ability for your service to maintain multiple running instances that work as a cohesive unit is paramount to it's ability to support concurrent users at scale. Consistent packaging mechanics, like virtual machine images and containers, are often the key to automating the replication of services in cloud environments to better manage scale, but this replication also demands consistent methods for load balancing and distributing incoming traffic. By combining packaging consistency with API gateways, service meshes, and other load balancing solutions, teams can easily achieve deep application portability.
The second dimension of portability is the one that many first picture when they think of cloud portability - cloud migration and/or multi-cloud deployments. The ability for your application to be run on multiple platforms is a great defensive strategy to ensure that cloud apps can remain cost effective and protected from outages. Designing applications to be run on commodity infrastructure (e.g. linux vs. windows) or on multiple cloud providers (e.g. AWS vs. Azure vs. GCP) enables teams to run in multiple locations concurrently, or swap out providers should pricing prove beneficial.
The third dimension of portability is often the most overlooked despite being far more impactful than horizontal mobility - portability through the software development release cycle. As we noted earlier, software developers are constantly building or modifying services inside an application stack, and as such find themselves with ample need to test early and often in environments that they can be sure will match production. The consistency of the application context from local development, through test/QA/staging, and finally to production environments, is crucial to ensuring trust, maintaining a strong development flow, and ensuring that product features get in front of customers quickly and safely.
The yearning for portable software is not new. The development of the Java Virtual Machine in the early 90s is among the most successful software portability innovations to date: a single compiled .jar file can be run with identical behavior on any machine and any OS (so long as it has a JVM installed...). Docker took this a step further: an entire OS can be shipped as a lightweight artifact and run anywhere (so long as there is a Docker daemon installed...).
Note the caveats, and note the seeming inconsistencies with the entire concept of portability! If java needs a JVM installed, isn't that a hard violation of everything we've discussed? Alas, it seems so. Here lies another, related- even inverse- concept to portability: the platform. Portable software still needs to be executed by something; a platform, an OS: an environment. So as developers struggle to make their applications more portable, companies struggle to make the "universal platform" on which all portable applications might be run: Microsoft tried it with an OS, Oracle with a narrow VM, Docker with a more general VM, and most recently Kubernetes with an open-source hardware abstraction and Terraform/Cloudformation with reproducible infrastructure-as-code templates.
So are we done? Are applications truly portable? The best way to answer that is to look at our own applications. Are my applications portable? Can I share my application with other developers and have them run or access it using their own tools and hardware?
We've made enormous strides toward allowing cloud software to become portable, but with each innovation comes a new opportunity for software architecture to push the boundaries even further. Yesterday I could make a portable monolithic application by putting it in an AMI or Docker image, but today my app contains multiple images that run separately yet still need to connect together. The pursuit of portability is an ever ongoing effort, but the value of the pursuit always remains.