Continuous Delivery Assessment
Report based on workshop and interviews
sample
https://codemamo.lakruzz.com
November 24, 2016
Author: Lars Kruse
Co-authors:
Table of Contents
Context
Your Awesome Company and the CoDe assessment
This report summarizes the findings of a series of workshop conducted in relation to the CoDeMeMo performed for Your Awesome Company on 2024-04-07 00:00:00 +0000.
Here follows a short intro to Your Awesome Company
The Continuous Delivery Assessment
The interviews and workshops related to the CoDeMeMo has the purpose of getting a complete overview of the organization with specific focus on software development, the tool stack that surrounds and supports it and the processes used to develop, verify, deploy, test and release software.
The goal of the interviews and workshops is two-fold:
- To raise awareness in the software development team of some of the best practice methods, tools and processes available for improving efficiency and producing higher quality software.
- To outline a roadmap as a practical guide to achieving this goal.
Awareness is achieved by conducting a considerable part of the interviews and workshops and facilitated discussions with the developers in the software team.
The roadmap is manifested in the report you are currently reading.
This report
The report is structured around the Continuous Delivery Metric Model which was presented to all participants during the workshop. The chapters in the report reflect the focus areas in the model.
Throughout the report, findings and observations are given their own section header and paragraphs under each section are marked with an up-arrow indicating that it contains a recommended action mitigating an issue.
Mitigation’s are tagged with a prioritization (M, S, C or W in parentheses).
These letters represent the authors opinionated recommendation of priority for the suggested actions.
The lettering refers to the MoSCoW principle for prioritization:
- Must have
A condition that must be satisfied in the final solution for the solution to be considered a success.
- Should have
A condition that should be included in the solution if it is possible. A critical condition indeed but not discriminating for success if left out.
- Could have
A condition that could be included if time and resources permit. It is considered desirable but not strictly necessary for success.
- Won’t have (not used in this report)
A condition that is not currently relevant and which will not be implemented but could potentially become relevant in a future scope.
The prioritization that we have come up with is meant to be a catalyst for your internal discussions - you may not agree with our priorities in which case you should obviously go with your own.
Each section is designed as a standalone piece so that the suggested remedies and improvements can be applied independently.
This approach should enable you to copy most sections marked with directly into JIRA and prioritize them.
All you need to do hereafter is to groom the task, estimate the work, and put it on your backlog.
Summary
Read this if you don't have time to read the entire report
Note: the following is a sample summary from an anonymous client
In general we were quite impressed with your enthusiasm and commitment at Universal Robots.
Such level of engagement is a rare sight, and it’s obvious to us that you take pride in your craft.
We found that you have a very high level of technical proficiency and continuously challenge each other, but do so in a very respectful and professional manner. Although it’s quite a challenge running so many workshops in such a short time span, this made running them with you very enjoyable and fruitful.
Some of the things you showcased during the workshops were also rather impressive. The fact that you have implemented quite a battery of automation and improvements as part of everyday work, also shows us that this is something you care for.
In the next chapter, we will go into detail on every observation we’ve made and noted. As not all of you will want to read the entire report cover to cover, this summary is presented as a narrative compilation of recommendations that scored a clean Must Have.
Culture and Organization
Some of your highly engaged teams have already identified many improvements and areas of technical debt. You’ve already got the mindset for the task and had already identified many of the common issues we usually encounter during these workshops. “It’s like an aircraft engine. You need to do the maintenance to keep it in the air” was an apt analogy you brought up. This is a great start, you understand the need to continuously take care of your asset.
However, your current approach was also described as an ad-hoc “boy scout” approach. Meaning you do the right thing by making improvements as you identify them. It creates hidden effort and can actually create more debt than it fixes.
The approach to maintaining your asset needs to go to the shop, it needs to be analyzed and planned. Create them as issues in Jira, theme them, commit to doing them as part of your sprints and measure your progress. You have technical leads for the main subsystems. They should be driving this in a planned and organized manner.
There is also a certain level of acceptance towards manual testing from a cultural perspective. This means that tacking quality on is, to a degree, accepted. We think you should start with promoting testing to a first class citizen in the development process.
There will always be some form of manual validation, but you want to ingrain testing as an automated process into your culture, as manual testing is inefficient. You can only do this if you implement some form of test management and define a strategy that will enable effective, flexible and scalable tests. This test strategy needs to be embedded into your culture and organization.
Code reviews should not be used for verification and should only be done after all automated verification has run. The best code reviews are those that check code semantics, often involving people with specific domain knowledge. Consider if pair programming is an option, which can be easily planned up front and helps creating cross-functional teams.
The last thing we’d like to highlight is the existence of team subcultures. Some of it may be domain pride, which is quite okay, but it seems to be partially driven misaligned processes across the teams, causing friction. Your branching strategies are a good example of this. How you develop software affects how you interact with each other. Aligning your important processes, like branching, versioning and test approach will give you a lot of value. At the end of the day you are delivering one product to the customer.
Tests
First and foremost, you need to get your tests under control. As it stands, they are really slowing you down and causing unwanted side effects such as long feedback loops, unstable pipelines, etc. The result is that the value of your pipelines are degrading, eventually leading to red builds being ignored.
We suggest you start by stabilizing the automated tests you already have. Valuable tests need to be fixed, while redundant tests need to be removed.
Focus on defining and implementing a test strategy and building up an inventory of tests. Follow best practice as far as the test pyramid goes. Rotating your pyramid 90° clockwise will give you a solid idea of when to run which tests in your pipeline.
Currently, your functional tests, also referred to as E2E, are being executed prior to the aggregated UR upgrade package. This is what you actually want to run such a high level of tests on. As functional tests are inherently more complex, they also affect the feedback loop, so run them after the first level of feedback is given to developers. In addition, consider running the tests in parallel where possible.
Version Control System
You need to move to Git. Subversion is simply too bulky and inflexible for a real CI/CD approach. It doesn’t do branching and merging very well, which you will be doing a lot in an automated release train workflow.
This is one thing we really feel you should focus on. You need to start defining your migration strategy.
You also need a defined branching strategy that supports a CI/CD and release train approach. This branching strategy should be the same across all the teams. This will enable automation and CI/CD by doing automated verification as a toll gate for entering the integration (main) branch. You want logical commits, which are automatically verified on branches and automatically merged into the integration branch.
Architecture
Large monolithic code bases, which take a long time to build and validate do not play well with CI/CD, regardless of the VCS or branching strategy chosen. A lot of the perceived problems are rooted in this: the complex build system, implicit dependencies, slow builds, etc. This is by far your biggest and most daunting task, but it is the one that will give the most value. We’re pretty sure we are preaching to the choir here. You want a more modular, loosely architecture.
PolyScope has some monolithic tendencies. Notable, there’s a single JAR that’s composed of 80% of the PolyScope code. You should investigate a more modular approach.
There is a tight coupling between PolyScope and the Controller. This can be loosened by splitting the dependency out into its own versioned and controlled artifact. An added benefit is that you’ll be able to manage the public API explicitly.
Breaking down your architecture into modules and creating individual pipelines for them implies you’ll need a structured and cohesive approach to dependency management and releases. Though we sadly didn’t have the time to dig deeply into this area, we know you are aggregating your dependencies in the firmware subsystem and the UR upgrade package. This means that you are already doing dependency management, you just need to make sure it is a cohesive approach where you can automate the creation of a versioned release from a branch and identify the dependency from the version. This is what the version, release and branching strategies will give you. The ability to automate the process.
Builds
One of your biggest pains right now is your unstable integration branch (trunk).
The main drivers behind this are the lack of a cohesive branching strategy, unreliable tests and the absence of an automated toll gate.
Start by setting up a toll gate to your integration branch.
Define what makes code good enough to share with colleagues, e.g. can compile and run unit tests, and set this up as a barrier into the mainline.
Once development is done solely on topic branches and the automated toll gate is the only way into the mainline, you will have a stable trunk.
The build script looks to be a point of friction as it’s an attempt to combine some of the subsystem builds due to dependencies.
The shell script is a pre-step, followed by Maven and SCons to build the subsystems.
This is in reality just a reaction to the coupling between PolyScope and the Controller.
The result is a slow feedback loop. This pre-step should be handled as a dependency instead of a build requirement, so you won’t have to build needlessly.
At least once, during the workshop, it was stated that not all of the build setup was found in scripts, but it was located somewhere in Jenkins.
We have extrapolated this to mean the pipeline definitions. Your pipeline definitions are separate from the code.
As a result, it is difficult for developers to get an overview of what they need to control.
You need to build a better separation of concerns here.
The DevOps team should provide the pipeline framework while the domain specific configuration should live in the repositories.
The developers should be able to control what to build, which suite of tests to execute, etc. while the framework should just ensure that the definition of done is executed.
You need to push your pipeline into Front Office.
The tests that are being done, or triggered manually, on the UR upgrade package should be part of a pipeline and the final image given to the customer needs to be generated as well.
In reality your final automated tests should be done on the generated image.
In general there are too many manual steps in the Front End release process.
You should focus on automating these to the extent possible.
DevOps
It is quite evident that this has been an area of focus, a lot of work has been put into it and quite a lot of benefits have already been reaped.
However, you have a low bus factor here. Therefore we would recommend that some effort be put into opening up the information bottleneck.
DevOps, obviously, wants and needs a certain amount of control. But there is a fine balance between control and flexibility.
There are a couple of findings that have to do with resource management, both hardware and human resources.
We recommend you take a closer look at how you manage both of these.
Ask yourself what the best strategy for scalability from a hardware perspective is.
Is it really fewer, more powerful, machines, or does the problem lay somewhere else?
The one Windows build machine is an obvious bottleneck but you should make sure you identify the core problem.
Should the DevOps team really be maintaining the Jira and Confluence instances?
We recommend that you shift such tasks away from the DevOps team and have them focus on feedback times, pipeline structure, automation, access to more production like environments, etc.
The DevOps team should still own the ‘how’, i.e. the process flows, but shouldn’t have to deal with the installations.
Get the last pieces in place as far as your present infrastructure is concerned.
You’re utilizing Docker images, which is great. Many organizations are far from containerization.
But the base images are being built constantly. This means that they could move under your feet.
Consider bringing Docker Registry into the picture.
Your environments should be managed as code.
Start to make this a reality with a CMS like Ansible, Chef, Puppet, or whichever you choose.
Use these clean environments as much as possible. Why could they not be the basis for developers environments?
A high level road map
For a high level road map of our recommendations, we’d suggest the following:
- Move to Git and start using a branching strategy that supports a release train workflow.
- Define and implement your toll gate into the integration branch.
- Stabilize your integration branch and react when it goes unstable.
- Define and implement your test strategy.
- Start chewing away at that elephant! Break the monolith down with an iterative approach. It’s too big to eat in one sitting.
- Automate Front Office’s manual processes and put them into a pipeline.
Scores
The cards scores based on the workshop exercise
During the individual workshops for each area, we do an exercise where the participants evaluate and score the five cards that belong to the area.
Through six workshops over two days we ended up scoring all 30 cards in the model.
The participants were split into smaller groups of 3-4 people, and each group was given one card to score.
The exercises was to discuss the best-practice described on each card and score them in terms of the four
gauges throughput, feedback, payback and simplicity.
After scoring the cards each group presented their scoring briefly, and the rest of the group had the option to affect or discuss it, if they found a need for it.
Throughput, feedback and payback are added together and multiplied by simplicity to calculate the total score on each card.
All the cards that got a score are listed in the following:
Note: the following is a sample analysis from an anonymous client
Analysis
The majority of the cards received some type of score in the value gauges. With the exception of the following
- Automated release notes - received zero on payback
- Test in production - received zero on throughput
- Commits are tied to tasks - received zero on payback
- Version numbers - received zero on throughput
It could be beneficial to revisit the cards, which you scored a zero in one of the value gauges, after reading this report.
It might have given you inspiration to find the benefits from a holistic view.
Low hanging fruits
The scoring algorithm favors the simplicity gauge, putting easy-to-implement solutions higher up on the list.
This is a deliberate choice, as it helps identify the aptly named “low-hanging fruits” - ideas that should provide easy wins with high value.
These are perfect issues to get started with as the quick gains often help you down the road and help motivate the organization to tackle its bigger, harder-to-solve pains.
Looking at your self-assessment, a few cards stood out:
- Buy-in from management
- Pristine integration branch
- Release train branching strategy
Buy-in from management scored a 24 in the Organization and Culture category. You have evaluated this quite high in throughput and feedback.
You gave it two stars for payback and feel it is simple to implement. We agree with this self assessment.
We feel this is directly related to how you are going about paying off technical debt and implementing tasks to improve the way you work.
Not so much as to whether you are allowed to. More about the approach of making it visible, organizing it better and planning it.
This score is only a few points from being the ultimate low hanging fruit.
Pristine integration branch scored a 21 in the Version Control category. You have evaluated this as something that
is easy to implement but gives fairly high value in feedback and throughput while affecting the architecture quite heavily.
This is a good view, and to some extent, will not be the most difficult task of the results from this assessment.
It might be a little more difficult than you anticipate. You need to align the definition of done across the teams and
get the tooling, a DVCS for example, in place before this can be achieved effectively. Keep in mind that you want to make
sure you do not impede the pace of the team with the automated toll gate. This could be slightly more complex than at
first glance due to build and architectural dependencies.
Release train branching strategy scored an 18 in the Version Control category. This was assessed as being easy to implement,
while giving a high value of throughput and fairly high value on feedback. It is interesting is that you gave it only one
star for Payback. If you keep in mind the requirements a release train will impose on test-ability, trace-ability and
maintain-ability, you may find that the value return is bigger here than you initially believe.
Falling back to the cards that score high with two stars in simplicity we get:
- Code metrics - scored a 16 in Architecture and Design category.
- Dependencies are managed - scored a 16 in Architecture and Design category.
The code metrics are a sure win and, in reality, should be part of your toll gate to get in to the integration branch.
It can, generally, be kept fast and give definitive feedback.
The dependency management, on the other hand, will be pretty heavily related to what you already have implemented in this area.
We know you have some dependency management from Maven. But what about the other technologies in play? How about an aligned
version strategy that will support dependency management of releases in a good way? There are some rather hard relations
to the architecture across the technologies here. This might, in reality, be a two in simplicity instead of a one?
Biggest blockers
Another thing we look for are your major pain points.
Judging the self-assessment scores, we now look further down the list for cards that were judged as very hard to implement
but giving huge benefits when done.
These are cards that score high in throughput, feedback and payback but only a one in simplicity - indicating
that they are difficult to implement.
There are three cards that stand out here.
- Explicit knowledge transfer
- Delivery Pipeline
- Testable code
The Explicit knowledge transfer scored a 9 in Organization and Culture category. A full three stars in Throughput, Feedback and Payback.
But is perceived to be very difficult. This assessment is probably due to the hero factor which is exhibited at UR.
There is also a bit of a low bus factor in the DevOps team, but our clear impression is the difficulty lays with the system complexity and architecture.
Meaning it is hard to get an overview of the dependencies and how changes may affect the system.
But the same may also be said about the Delivery Pipeline self assessment. This card scored a 9 in Build category.
Again a full three stars across the value gauges but is perceived to very difficult to implement.
It is, most likely, also rooted in the system complexity, architecture and dependencies.
Your self assessment on Testable code scored an 8 in Architecture and Design category. Only a two in the Throughput
but a full three in the other two value gauges, while still being perceived as difficult to implement.
This is a bit harder to analyze without really looking into the automated tests and the code itself.
Looking at the three blockers that stand out.
We could imagine, that we see a red thread leading back to the system complexity, architecture and dependencies. What do you think?
Outliers
There was one self assessment that we wanted to highlight which did not fit into either low hanging fruits or blockers.
Use distributed VCS scored a 6 in the Version Control category. You scored it a three for Throughput, a two for Feedback and a one for Payback. You perceived it as being difficult to implement. The result is that it does not end as either a low hanging fruit nor a blocker. However, we feel, you will find it difficult to implement an effective branching strategy that will help you in achieving a pristine integration branch in a CI/CD environment without moving to a DVCS. We think the Payback may be higher than a one.
Our picks for you
If we look at where our focus has been, and where you have self identified the most findings with high priority it falls in the following 7 cards, minus the Explicit knowledge transfer.
- Buy-in from management
- Pristine integration branch
- Release train branching strategy
- Code metrics
- Dependencies are managed
- Delivery Pipeline
- Testable code
Your own picks regarding a release train branching strategy, and a pristine integration branch really fit well with where we could see you putting your focus to start. With the addition of:
Dependencies are managed and a Delivery pipeline also fit well with what we would recommend starting with. Mainly because we think it will give a good starting point for eating the elephant as we like to call it. We would also like to point out that the move to a distributed VCS could also help in this regard. It could give you the opportunity to discover implicit dependencies. See our solution Split them with an automated approach under the finding regarding Current VCS.
We left out the Explicit knowledge transfer because we feel it is a very difficult blocker to deal with presently. But also because we think that it will be less difficult as you make the, planned, incremental improvements. As you eat the elephant, institute a release train branching strategy, etc, there will be less of a need for hero’s.
Findings
The findings and observations, including their mitigation, recommendations and prioritization
The findings and recommendations have mainly been picked up throughout the interviews and workshops. We conducted this part as targeted group interviews focusing on the 6 main areas of the metric model.
On the first day, after the initial Continuous Delivery presentation, we had an exercise where all attendees together were asked to illustrate the process from the point where a requirement or a change request arrives until the implemented result can be accessed by the end-users.
The participants were also equipped with stickers with 6 different pictograms representing: Wait state, unplanned work, manual work, conflicts, queued work and anything that needs repair.
The result was discussed during the workshop and then used as a reference point during the various discussions on the following day.
Build
Note:
The following is a curated mix of illustrative findings from various anonymized reports
It builds on my machine
Builds and tests run on individual developer’s self-maintained machines and workspaces, as no standard image for this exists.
This results in an undesirable “Well, it works on my machine” scenario. One developer can have a hard time redoing another developer’s work. The team suffers from a lot of false negatives. Builds fail often. Not because the code is broken, but because the build environment is.
Use a clean standardized image for the toll gate job
Code changes should be built and tested on a standard and clean image. The image should be maintained by the team.
This prevents local configuration from tampering with the builds, making them deterministic and consistent.
Developers use reference image
Ideally, there should be no difference between a developer’s environment and that of the toll gate.
This is achieved by making it easy for developers to spin up the standard image to run their builds, allowing them to easily produce the same result as the toll gate job.
Final delivery to customer is not automated
The image given to the customer is generated before final test in a non-reproducible way.
It is built manually based on an already running device with an other, older image.
It is modified by manipulating files within the device before a final image snapshot.
Conceptually the image could be broken during the creation process.
Automate image creation
Any release artifact should always be possible to reproduce without any doubt.
It should be scripted 100%. Programmable and immutable.
Make the image generation stateless
A product to a customer should always be generated from scratch in order not to propagate error prone states.
It should also always be reproducible through a declarative pipeline.
Automatically test the image
The automatically generated image is tested as part of a pipeline.
You want to extend the pipeline as far down the release process as possible.
Release process is, to a large extent, manual
The majority of the release process located in the Front Office is a manual one, which is costly, lengthy and error prone, e.g. the image creation, release notes, support site updates, etc.
Design the release process for automation
It is important that each step is designed for automation.
Make sure the physical resource are available and enabled for automatic deployment.
It relates to servers, but also devices that they can be claimed, started and the execution evaluated.
Script each single step to enable automatic execution
Identify the process steps that are manual, entirely or even partially so.
Automate step by step and integrate them into the pipeline.
Build script is an plaster on the monolith
The build script is created to handle the monolith by plastering each of the subsystem builds into a single step.
The different subsystems have different build technologies (e.g. Maven and Scons), but in order to make it all work as a monolith it is wrapped with shell script.
Split the monolith and add dependency management of the artifacts
Splitting the monolith into subsystems and implementing true dependency management makes the build script obsolete.
It simply vanishes. Please refer to the architecture cards for more information.
Localization files are introduced late in the release process, leading to unexpected UI changes
Localized text in the UI can cause the UI controls to change in some unexpected manner, e.g. resizing of buttons due to translated texts being longer than their original.
Commit-stop is the point where no further features, and thus no new text, gets added.
Translation can take up to one month and arrives fairly late in the release process.
Create requirements for localization subcontractor
Create criteria or a framework that helps these text translators adhere to font type, font size, etc.
This gives them the opportunity to react early and discuss with you any problematic translations.
This will minimize, or even completely avoid, nasty surprises near the end of the release process.
Manual process to create a buildable workspace
It is not possible to start building from a clean checkout.
You have to checkout two repositories and create a symlink in order to be able to build the software.
It is a hidden dependency.
Make the ‘link’ permanent by using a explicit dependency
Create a ‘thin shell’ repository which contains the dependency between these repositories.
In SVN it is called an external, in Git it is called submodules.
You could also pull the dependencies from Artifact Management system if the repositories have their own pipeline and definition of done.
The implementation depends on the technology. The important point is the dependencies are managed and versioned.
The pipeline scripts contain domain specific logic
Your pipeline scripts are bloated with domain specific logic and setup.
Much of the script relates to setting up environments and requirements to build the software.
The pipeline scripts become large and unmanageable.
Keep domain logic out of pipeline scripts
Consider your pipeline definition as configuration rather than a script to help keep it lean.
Avoid building logic into the pipeline script that has nothing to do with either CI system’s interface, execution or reporting.
Remember that all the logic you put in these scripts is also something developers would need to run at their desk, should they want to recreate runs locally.
The pipeline script makes for a poor place to document these setup steps
Note:
The following is a curated mix of illustrative findings from various anonymized reports
Artifact management system configuration not optimal
The setup used in Artifact management is not as the tool is designed and intended for.
The intended root structure is projects and on top of that a semantic structure with package and semantic version below.
The current setup abuses the root structure as it is both the project and version which means that newcomers are confused and the root structure is polluted and grows as more releases are produced. The structure you’ve set up requires advanced logic in your build scripts rather than just simple SemVer logic.
Implement best practice structure
Store the artifacts in a hierarchical tree structure.
It should also be done for non-Java artifacts if there is no explicit reason to not do so.
Always respect the intent of a tool. Read the manual again, follow some of the tutorials for the tool.
It will make the automation of the dependency management less complex if there is a consistent structure for addressing the artifacts.
Note:
The following is a curated mix of illustrative findings from various anonymized reports
Release Notes are manually created
The release notes are handled in Confluence. It is a manual process and it is time consuming. The Jira issues are extracted and post-processed for the final release note. It is glue-on quality - not built-in quality. The release note cannot be generated based on a meta-system like Jira alone. It does not have the source revision time record. Jira issues and releases in Jira do not understand relationship of changes in the SCM system.
It is important to configure you schema in Jira, so it stores all needed information about the logical change. Add a status which tells if it should be listed in the release note. Not every task should be listed in a release note that the end user sees. Add a field for end-user description which can be extracted if public.
Generate different release note for different stakeholders
The different stakeholders will have different needs. At least it makes sense to have two generated release notes. One for R&D which is used for meta data quality checks and used to monitor ‘release-readiness’. The second is a public release note which is reporting what external stakeholders need to know. Meaning, which features have been implemented and which publicly known errors have been corrected.
Create your release note as a record of your SCM system
The only correct place to initialize your release note is from your SCM system as it is ‘the’ one and only truth about this particular revision compared to previous revision/releases.
Use a release note generator based on SCM system (M)
Given that the systems and process are designed for automation the report can be generated. Any changes to data for the release note is changed/added/removed at the root source of the information. This means that the release note can be regenerate based on the updated data.
Add the generator to the pipeline
Add the release note generation to the pipeline. It then becomes artifact as any other product generated as part of the pipeline. It will be an important artifact for the tester and product owner as it shows exact progress of the software for later promotion decisions on this particular revision.
Use the R&D release note as a consistency check (S)
As part of the release gate it is possible to monitor the release readiness where the release DoD is programmed/implemented. This is to harvest traceability and use it as a consistency check.
The consistency checks could look something like the following
- Do all SCM change-sets have a related Jira issue
- Are all the Jira issues in a correct status
- Do we have all the changes related to all the Jira issues for this release
- Do the change sets, based on the Jira issues, have the needed reviews in the correct status
Security review process and documentation is manual
The security review is needed for the safety reaction of the device. Changes to these algorithms are related to a special and mandatory review that needs to be documented and signed-off as part of the release process. The review is not tool-aided and is currently documented in the Jira issue. It is a manual process to gather the reviews to create the document that is signed by the security-officer.
Implement a solid review process and automatically generate the documentation
It is advised to implement Crucible/FishEye(already available) as the front-end for the review. It handles the statuses of the reviews. Given the data and status is available in the Atlassian suite it is possible to auto generate the review report ready for signature.
An option is to use PAC as a foundation, it’s an open source tool that can be extended to also generate the review report.
Test
Version Control
DevOps
Architecture & Design
Organization & Culture