Designing a CI/CD Pipeline that Maximizes Developer Productivity

    While a lot of attention has been focused on continuous integration and continuous delivery (CI/CD) pipelines, too often, a critical consideration is being missed: Developer productivity. This post examines how teams can establish CI/CD pipelines that boost developer productivity.

    Boosting Developer Productivity Matters

    Within today’s development organizations, CI/CD pipelines are familiar topics, and, as the adoption of DevOps continues to grow more widespread, these approaches are becoming an increasingly integral part of software development processes. However, while there is a lot of theory and a lot of tooling available on the market, there’s one key facet that gets very little attention: CI/CD pipelines need to be designed for developer productivity.

    Why is this topic given so little attention? One theory is that development organizations are struggling to get these concepts in place. Alternatively, it could be that many view implementing CI/CD pipelines primarily as a task consisting of implementing and integrating tools.

    However, if you think about why we even want to have a proper CI/CD pipeline in place, it is to empower developers to deliver customer value as quickly and reliably as possible.

    Designing a CI/CD pipeline for developer productivity boosts our ability to deliver customer value, emphasizing fast feedback, repeatability, and predictability. Ultimately, we want to enable teams to come up with an idea first thing in the morning, ship it during the day, and receive data on that shipment at the end of the day. The heart of this vision is a well-designed CI/CD pipeline.

    What’s Wrong with Existing CI/CD Pipelines

    Designing a CI/CD pipeline isn’t just about selecting tools that will do the job and getting them hooked up. It is essential to design feedback loops that put developers at the center. If we take a closer look at a typical CI/CD pipeline, especially the required testing steps, we get a picture that looks like this:

    Designing a CI/CD Pipeline that Maximizes Developer Productivity Image 1

     

    Such a pipeline covers all the necessary parts and steps to successfully achieve a working CI/CD pipeline. However, upon closer inspection, it becomes clear that there are two major parts missing from this diagram:

    • Duration. How long should it take to execute each step or stage?
    • Feedback. What is the feedback loop for developers, and how can they be equipped to react quickly?

    Since our vision is to create customer value as fast as possible, the time it takes to run through the pipeline and deliver valuable insight to the developer is a critical consideration. This process of giving developers feedback on whether their implementation is on the right track is what we refer to as a feedback loop.

    Following the agile principle of failing fast, it is not a good idea to run through the complete chain on every single commit. Additionally, as part of a modern development process, we need to consider that, beside automation, any change should run through a code review process.

    A Step-by-Step Approach to Pipeline Design

    In order to design a CI/CD pipeline that fulfills our goal of boosting developer productivity and speeding the delivery of customer value, we need to divide the whole process into major stages, following the flow of code and developers’ work:

    • Post commit
    • Pre merge
    • Post merge
    • Pre deployment
    • Post deployment

    The important part here is not only to group certain steps of a pipeline into these stages, but to ensure that each stage comes with a clearly defined scope in order to enable fast feedback. Remember, we want to enable a pipeline that allows us to run through the whole process, from implementing a fresh idea, thoroughly testing and reviewing the implementation, to gathering feedback about it from the production system. Therefore, a pipeline could look like this:

    Designing a CI/CD Pipeline that Maximizes Developer Productivity Image 2

    There are two key aspects of this design of the pipeline:

    • First, we include the important step of code review (which is in most cases still a manual step) in our pipeline. Also, we make sure that each stage contains its valuable feedback loop to the developer, taking into consideration some potential timing scenarios.
    • Second, we clearly divide our test suites to run in a targeted manner.

    After code completion and before the actual code review, it is not yet necessary to execute a full system test. We can limit the number of tests running by defining system-critical smoke tests and running tests only on features that could have been affected by our changes. This should help us avoid cases in which we detect potential side-effects of our changes in a later stage.

    We can also package additional steps into this post-commit stage, including static code analysis and first license checks, to ensure compliance early on. Since these are not time-consuming tasks, at this stage, we can generate a first round of feedback for the developer, while they enjoy their well-deserved cup of coffee.

    In the next, pre-merge stage, we pack the whole review process. This includes a manual code review and the resulting merge to the master branch. Code reviews should be conducted as peer reviews. Consequently, this process could take up to a couple of hours, including some waiting time, considering that reviewers will need to make time for the assessment.

    Peer reviews can be heavily disputed, as they often come down to personal preferences. However, given our goal of providing fast feedback, this feedback is still best done in direct, one-to-one conversations. These direct conversations save us from time-consuming offline discussions via comments on tickets or merge requests.

    Fast Feedback on the Way to Production

    Once we pass pre-merge and enter the post-merge stage, the full test suite is now executed. Depending on the complexity of testing suites, this can take a bit longer. We also included acceptance testing in this stage. Once full integration tests have been passed and the feature is ready in a test environment, it is preferable to have acceptance testing done by an approver. An approver can be a product owner, product manager, or some other team member who can effectively assess whether a feature fulfills customer requirements and all acceptance criteria.

    Here we are at the cusp of pushing code out to production or putting a feature into operation. Based on our goal of boosting developer productivity and delivering more customer value, just pushing code to production for the sake of having it there, is not really a value-add.

    Instead, even if it might take us a bit longer, we want to make sure that we only put things in production that actually increase customer value. Some might argue this could be done differently, for example, by using feature flags or toggles, and indeed these could be viable alternatives. However, the problem with feature flags is that not everything can be flagged or toggled. Further, even if everything could be flagged, this approach increases the complexity of the code in a way that could decrease developer productivity in the long run. Given that, we advocate an approach in which only something that increases the overall value of the solution is pushed into production.

    Of course, there might be cases in which we actually just want to push code, for example, as part of a code refactoring or technology upgrade. However, we can view these types of efforts as benefiting developer productivity, which can ultimately translate to improving the value of the product as well.

    After the post-merge stage, we move into the pre-deployment phase, when we take these quick steps: scheduling our deployment and actually deploying to production. Often, these are steps that can be automated. We believe that scheduling a deployment first is a necessity, especially in large enterprise environments. This is due to the fact that, because of compliance and auditing requirements, deployments often have to be limited to certain timeframes. These restrictions should be factored into the design of the CI/CD pipeline.

    Conclusion

    By taking a more developer-centric approach to designing a CI/CD pipeline, your teams can deliver more customer value into production. While this requires change, the benefits are well worth the effort. Watch this space for future blog posts that will offer more details on each of these stages.