The build stage is crucial as it compiles code, manages dependencies, verifies code, ensures consistency, manages artifacts, enables faster feedback loops, and integrates with testing. It enhances code quality, reduces risks, and establishes a solid foundation for the rest of the CI/CD pipeline.
Implementing best practices for the build stage in CI/CD guarantees high code quality, stability, consistency, efficiency, scalability, security, and collaboration.
This article delves into the recommended best practices for the build stage in CI/CD pipelines.
Utilizing short-lived branches for individual features.
To enable precise quality control and gating between features, a pipeline should possess the capability to veto specific features and selectively choose only a subset of them for deployment to production. One effective approach to achieving this is by adopting the feature-per-branch methodology, where each short-lived feature, typically within a single development sprint, corresponds to its dedicated branch in the source control system. This methodology ensures that the pipeline can effectively manage and deploy features with granularity and control.
This approach simplifies the pipeline design by focusing on individual features. Each feature becomes the central point, with the pipeline running test suites specifically targeting the new functionality. Additionally, conducting security scans on a branch helps identify any issues or vulnerabilities associated with the new feature.
Project stakeholders gain the ability to deploy or roll back individual features independently. They can also exercise control by preventing entire branches from being merged into the mainline code if necessary. This provides flexibility and governance over the release process.
Storing/Caching Your Dependencies.
In any significant codebase, dependencies in the form of libraries or associated tools are commonplace. While your code should always be stored in Git, it is equally important to maintain a repository for external libraries. This artifact repository serves as a central location for storing and managing these dependencies.
Take the time to gather and comprehensively understand your dependencies, including their sources. In addition to code libraries, it’s crucial to consider other less apparent components that are essential for a complete build, such as base Docker images or command-line utilities required during the build process. Paying attention to these aspects ensures that all necessary elements are accounted for and properly integrated into your development workflow.
A reliable method to assess the stability of your build is to create an environment on your build servers that mimics an air-gapped setup by completely disabling internet access. This approach involves initiating a pipeline build while ensuring internal services such as Git, databases, artifact storage, and container registry remain accessible, while blocking any connections to the public internet. By observing the outcome of this restricted environment, you can evaluate the resilience and functionality of your build process.
Hints to accelerate the speed of build stage.
Efficiency is crucial for builds, both within the CI platform and on local workstations. Since multiple features are often simultaneously attempting to merge into the mainline code, it is important to ensure that builds are fast. Lengthy build times can overwhelm the CI server, potentially causing bottlenecks and delays in the development process. Therefore, optimizing build speed is essential to maintain a smooth and efficient workflow.
Operators also gain huge benefits from fast builds. Pushing hot fixes in production or rolling back to previous releases are always a stressful experience. The shorter this experience is the better. Rollbacks that take 30 minutes are much more difficult to work with than those that take three minutes.
For builds taking longer than 10 minutes, investigate and shorten the duration. Modern build systems offer effective caching mechanisms to aid in this optimization.
- Library dependencies should be fetched from an internal proxy repository instead of the internet.
- Avoid the use of code generators unless otherwise needed.
- Separate your unit tests (fast) from integration tests (slow) and utilize only unit tests for the fundamental build.
- Optimize your container images to maximize the benefits of Docker’s layer caching mechanism.