Rapid deployment and continuous integration on modern application development platforms
The pace at which software is developed and deployed in today’s world has rapidly increased due to the shortened iterations, automated testing and models of continuous integration implemented to various degrees throughout the stages of the development cycle. In such environments a source repository itself often becomes the package where its version would be either a tag or a even a hash of the latest commit. The metadata about such a “package” could be stored in a plain text file at some location within a repo.
One of the application development platforms that follow this model closely is Node.js. While one is still able to create node packages in conventional sense, it is also possible to provide a dependency in the form of a Github repository URL with a
package.json file sitting at the repository root, serving as a simple metadata about the package. It has a simple JSON format where the only required fields are the
version of the package. Optional, but advisable are the fields like
description, repository location and
license. Within the
files section one can easily specify the only files that are going to be “included” in the package:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Now running an
npm install on for a Node.js app that has a dependency specified in its
package.json like follows:
1 2 3 4 5 6
will pull down the
filename2 (along with the
README.md and its own
package.json file) into the
node_modules/package-name subdirectory of the Node.js app being installed.
npm install runs the
version field in the dependency’s
package.json will determine whether its files will be re-downloaded from the repo or not. This mechanism proves to be quite useful in maintaining the software installation and deployment process.
Sharing responsibility of the deployment process
Software deployment is a borderline between the development and operations, and it is highly desirable that the Devs are aware of the process and even better – empowered to control it through the tools provided to them by the Ops.
It is not uncommon to have an environment where a majority of the apps have a very similar deployment process that could vary only in some little bits like creation of the files, ensuring particular users exist on a system, requesting authentication credentials from the existing running services and so on. By creating a reusable library of simple functions serving as a middle-layer between system level operations and the Devs trying to set up an environment for a successful deployment of their apps, one can separate the domains of environment specification from the its actual implementation. Or, in the layman language – the “what needs to be there” from the “how it is going to be done”. Accordingly, the app’s environment – dictated by the Devs – could now be configured by them directly in their installation scripts using the building blocks from the deployment toolset, in this case the library functions.
To minimize the maintenance burden and to avoid code duplication across the multitude of repositories, storing the deployment library separately from the apps themselves sounds like a good idea. In order to make use of the library employing its functions in an install script one would obviously need to pull it down from the repo, may be even as one of the first steps in the script itself. In case of the Node.js app that action will be done automatically by the
npm install command, but there is nothing complex in replicating that same behavior for any non-Node.js app using just a
git pull and a simple JSON parser.
Now the Devs can use the library functions to compose the installation scripts for their apps, while the Ops will gradually improve and extend the library over time. Feels like a win-win scenario? Well it is important to make sure that the same scripts are being used for app deployments in all environments, starting from the development one.