Continuous Integration (CI), which spans practices, processes and tools to drive continuous development and integration of software code, is a key building block of an organization’s DevOps methodology. An important CI component is the software build process. The software build process traditionally has been a second-class citizen of the software development world, relegated to the background as organizations spend limited resources on customer facing and project management functions. Software development and delivery is inherently fragile, but one of the most fragile parts is the software build process because development managers have traditionally lacked clear visibility and control of the build process. Too often, software builds break easily, are difficult to change, and resource intensive to troubleshoot. With increasing pace of business change and higher delivery pressures, however, every link in the software development and delivery chain will need to be streamlined, and one of the key areas organizations will need to focus on as part of their CI journey is the software build process.
Building is the process of compiling raw software code, assembling and linking various program components, loading of external/third party libraries, testing to ensure that build has executed successfully, and packaging the code into a deployable package. While this may seem simple and straightforward, building in a big program is complex enough that an overall build architecture is usually required to be defined first along with a dedicated team and infrastructure for ongoing management. Building usually happens at multiple levels for different consumers: development builds focused on single component development and testing, integration builds across multiple components, system builds for all the system components, and release builds focusing on building customer releases. Build architecture deals with making specific choices around what to build, how often to build, and how to build it. Inefficient builds that take too long to finish, challenges with isolating shipped product bugs and issues and replicating them in the development environment, or challenges integrating multiple code streams into release builds efficiently are all symptoms of an inefficient build architecture. Architecting the build process starts with identifying what to build in a software configuration management system. Code branching and merging, dependency management, management of environment configuration and property files, and versioning of third party libraries, are all critical components of software configuration management that are closely tied to identifying what software components are needed in a build. “How often to build” involves defining the build schedules for development, integration, system and release builds, depending upon a number of factors such as number of development work streams, the frequency of releases required, and the capacity of the project team to name a few. The build schedule clearly identifies how the builds are promoted through the development pipeline, through successive stages of development, testing, quality assurance and deployment. Last but not the least, having appropriate build process tooling and infrastructure allows building to be declaratively controlled, automated, and efficiently executed. Build script automation, build parallelization and tracking and reporting of build metrics are all usually managed by a modern build management platform.
With so many moving parts to the build process, it is easy to see where and how things can go haywire. While there may be multiple factors contributing to inefficient and slow software development and delivery, ineffective building almost always is one of the contributing factors. Traditional build management suffers from a number of usual-suspect issues, and most of them are process issues, not tool issues. One of the most common approaches to building has been the big-bang style of executing integration builds, where independent development work streams bring together work stream codebases together as part of an infrequent big bang event. Compounding the problem is the tendency of development teams to throw code over-the-wall to the build team which is then painstakingly tasked with assembling sources, data, and other inputs to begin the building process. The big-bang integration brings a big-bang of integration issues and broken builds, a.k.a. “integration hell” in the industry parlance. Cultural and management issues play into this as well: build teams are not empowered to exercise and implement build discipline with development teams, which in turn do not always action on feedback from build team on broken builds in a timely manner, leading to lengthened cycle time for builds. Lack of exhaustive pre-commit testing in the development phase, either because development teams are not incentivized to unit test or because effective unit testing harnesses are not available in the first place, leads to “bad commits” and downstream integration issues, putting pressure on the build team. Many build issues can be traced to just poor build process management. For example, complex source code branching and componentization schemes complicate the build tasks and make the build process error-prone. Management of dependencies and build configuration frequently lacks sophistication which leads to challenges in implementing incremental builds, leading to build issues. Inadequate build automation and infrastructure can lead to a host of issues as well. For example, manual environment setup and data management complicate the build tasks, making building error-prone and lengthening cycle times. Build infrastructure often times is not adequate to execute complex from-the-scratch builds, which can take hours to complete, thus lengthening build cycle times.
Build management as part of CI aims to get around these challenges to streamline and turbo charge the build process, and ultimately improve the development process overall. And it begins by fundamentally changing the traditional development and delivery mindset. Whereas the usual approach involves “software craftsmen” working independently to create perfect fully functional modules that are then integrated over time, building in a CI environment espouses a much more agile approach in which team members come together to develop base product functionality as quickly as possible, incrementally building to deliver the full product over time. Cultural change to drive close integration between development, testing/QA and build teams is key: in a continuous building environment, development team works hand in hand with the build and testing teams, and a “buildmeister” has the authority to direct development teams to ensure successful build outcomes. Successful continuous building starts with overhauling the development effort, enacting such practices as test-driven development, and other precepts of Extreme Programming such as feedback loops that encourage development team to take ownership of ensuring successful testing and building. Development teams check-in and build often, sometimes frequently in a day. And they follow strict practices around check-in, bug fixing and addressing broken builds. A continuous building environment is characterized by the presence of a “build pipeline” – a conceptual structure which holds a series of builds, spanning the life cycle of the build development process, beginning with a developer’s private build all the way to a fully tested release build, each ready to be pulled by any team as needed. To enable this, a CI server is used to automate and manage the build management process. The CI server is a daemon process that continually monitors the source code repository for any updates and automatically processes builds to keep the build pipeline going. The CI server allows builds to be pulled out of the pipeline by individual teams and for builds to be advanced through the pipeline as build promotion steps are successfully completed. With each promotion step, the build becomes more robust and complete, and thus moves closer to being shipped to the customer.
To achieve success with continuous building, practitioners recommend a set of best practices across build management, processes and tools. A few key ones are related to software configuration and environment management, specifically, that development be managed from one “global” source code branch (or reduce branching) and that all build components, including property and configuration files, be managed in a versioning system. Then there are process-related best practices, which deal with development teams following pre-commit testing and actioning on feedback for broken builds on a priority basis. Automation is a key aspect of continuous building best practices as well: a CI server that manages builds and the build pipeline is a key component of the build automation infrastructure and is central to implementing continuous building.
Build management is a key part of CI, and one where a number of issues with traditional software development and delivery methodologies lie. Achieving full CI, however, requires change to other key parts as well, for example, testing and deployment management. In the future DevOps related posts, we will look at these other aspects of CI.