Develop Once, Deploy Anywhere
By Tomas Holmberg
Modern development methodologies keep evolving, with methodologies such as Agile, SCRUM, CI/CD, DevOps, etc. The idea is often to develop faster with better quality. Speed can be measured in how fast a new application is developed, how quickly you can release an updated version after finding a bug, etc. Quality can include more secure code and safer code, but it can also mean something else, depending on an organization’s goals. The idea of Develop Once, Deploy Anywhere focuses on reducing waste by avoiding writing the same application twice independent of the deployed target.
The concept to minimize development efforts and make sure the result works on almost anything is not new. “Write Once, Run Anywhere" was already established in 1995. Java was used to create cross-platform code.
There are also discussions about Build Once, Deploy Anywhere, and sometimes Anywhere is replaced by Everywhere, but the concept is the same. Build Once often refers to building something which does not require rebuilding. In that instance, a technology such as containers can help by creating a microservice that runs on many systems and does not have any external library dependencies. Note that a container still has dependencies on the kernel, but this is often ignored.
Why should you only Build Once? Developers today do not press a build button to get an application release candidate to be deployed to the target environment. There are automatic build and test automation systems that manage that part. Therefore, it is no longer important to build just once.
There are fundamental problems with building once. How can you make sure that the application does not need to be upgraded or bug fixed? Even if you write the perfect application it is depending on the toolchain used to build it or libraries dynamically or statically linked to it. What happens if there is a bug in the compiler making your application unsafe? What happens if there is a bug in a library which the application depends on. Libraries often depend on other libraries making it difficult to state that an application is bug-free. The delivery is no longer perfect, you might need to update the application eventually.
Develop Once is about developing something; it could be code, configuration scripts, or declarative specification. It is not essential if the final result is a container or if it is an application. Depending on the use case, the same development artifacts can be used to create the optimal delivery for where it is to be deployed.
A rebuild of the delivery could be triggered by bug fixes in any of the dependencies. Even a minor change in a configuration can trigger an intermediate bug.
The Yocto Project, a Linux distribution builder, is a good example of making it possible to develop code without defining how to deploy or what to deploy to. The developer defines his/her own layer with one or more recipes. The recipe contains all the information required to build the artifacts. Now it is up to the developer to write as good code as possible to avoid rewriting the code later.
There are multiple ways to avoid rewriting code. One key element is to write as little code as possible. Zero lines of code has zero bugs. One idea is to make sure the code is highly modularized. Where each part has just one purpose. The code is optimized for that function and limits the amount of code.
Programming languages can also help avoid rewrites. Writing code in a high-level declarative language will result in a minimal amount of code. Libraries and tools will parse the code to machine code. Bugs will not exist in the original code snipped but there can still be bugs in the dependencies. Another solution would be to use a memory-safe language such as Rust can help make sure the application is memory safe.
Deploy Anywhere means that an application can be deployed anywhere. Anywhere has a different meaning depending on your perspective. Some think anywhere is any classical x86 system running Linux. But a broader view would be on any hardware architecture and any sized system, from a big server to a small embedded device.
Yocto supports cross-architecture builds for several architectures such as Power PC, ARM, and x86. There is a rich support for different instruction sets and hardware skews within those major architectures. The application developer does not need to care about these details as it is managed by the build system. The build system will generate an artifact per architecture. The artifact, your application, and dependencies are optimized for your specific target.
There are many tools for creating artifacts, but they often depend on prebuilt images and libraries, which are built to work on most hardware of a specific type. The result is not optimized for the features of your specific hardware, in contrast to Yocto.
Yocto build system separates the application build from the packaging and creation of deployable artifacts. The build system creates deb, rpm or ipm packages which can either be deployed directly or deployed via other medium. The build system can generate several image types, SDK, system container, or application container. Yocto includes standard recipes, and you can write your own recipes for creating new kinds of deployments. The result is deployed to anything from a small ARM embedded device just running a lean Linux installation to a full-blown Kubernetes system in the cloud.
Developing software today is a big effort. By writing high-level applications and leaving the build, package, and deploy to tools, you gain agility and speed. Yocto can help developers achieve this goal by separating the development and creation of the final artifact, optimized for your specific deployment independent of architecture. The developer can focus on the development of the application rather than caring about the final artifact.