Source Code Repository Patterns and Practices
This document captures the current best practices, recommendations and supported patterns for source code repository in Azure DevOps.
Supported Azure Repo Type
Azure DevOps Repos supports both Git and Team Foundation Version Control (TFVC) for version control. Git is the only supported version control system because of below reasons:
- Git is most used version control system today and is quickly becoming the standard for version control
- Git is a distributed version control system, making it easier to work offline or remotely for active development.
- Git is cross platform compatible
- Git branches are lightweight and are easy to merge. This facilitates isolated development in feature branches.
- TFVC only allows one repository per Azure DevOps Project, which limits our ability to apply security
- Git has more tooling support across multiple CI/CD products/tools
Recommendation for Monolithic Repo vs Multiple Repos
Software Development Org is adopting microservices design pattern to facilitate the development and deployment/release of individual microservices. In order to support microservices design pattern it is recommended to create multiple repos to promote clear ownership, facilitate easier code management (less merges, easy cloning, less coordination), manage independent development, build and deployment.
It is recommended to have a separate repo for code that will generate an artifact that needs to be deployed independently or shared with other repos. Generally speaking it is better to have smaller, independent repos and that applies well into the scope and scale of development of tools and utilities.
Notes: - Git is best suited for smaller repos. Therefore, it is not advised to have a very large single repo. - Even if you do not want one repo per shared artifact, we recommend using a hybrid approach instead of one single monolithic repo for the product. - It is also recommended to keep large files (such as binary files, which should be stored in Azure Artifacts or blob storage) outside of Git repos. - It is up to the team to determine how text based files are stored. On the Energizers team we use markdown docs, rather than MS Office .doc or .docx files and keep them in a pubs folder or a docs folder. - In general, MS Office docs should be stored in Teams, SharePoint or OneDrive.
Recommended Repository Naming Patterns
Repository names should be standardized to enable the end users to easily identify Platform, Product, Type of output/artifact and purpose of the repository. This will also enable DevOps team to apply templates and patterns over time while building automation to create and manage repositories. Given this goal, it is recommended to create repository with below naming convention:
{Platform Name}-{Product Name}-{Optional Component Name}-{Type}
- Platform Name is the Platform on which the Product will be released e.g. CareLink, SmartSync, Monitors etc. Please refer TBD link for complete list of platforms
- Product Name is the name of the product being developed on the platform e.g. "iam", "mclr", "mclh" etc.
- Component Name is Optional, but may be needed in cases where there are components within the product that need its own repo. An example would be a library that is used by a Product on the platform e.g. a logging library for MCLH or MCLR has its own logging library.
-
Type should mostly reflect the artifact and deployment model needs of the repo. Below is the list of recommended values for now:
- "library" should be used when a common library for the platform/product needs to be generated and published to the Artifact Feed for consumption by other repos
- "service" should be used for a REST service that will be deployed and will need the client libraries to be auto generated along with a swagger documentation to be published.
- "poc" for any proof of concept to be carried out
- "app" to reflect that the final output is a mobile or desktop application
- "website" should be used when final output deployed is a website with user interface
- "firmware" should be used when final output is a firmware for the device or monitor
- "tool/utility" should be used for any tool/utility for the platform/product
-
"infra" should be used for any infrastructure code and configuration only repository
In addition to above, below additional types are recommended for specific use cases: * "template" should be used to create a sample repo structure and should be used only by DevOps * "subsystem" to hold legacy codebases that contain multiple projects * "tests" should contain automated test scripts/procedures that are across Platforms and Products
Note: This list will grow as more Platforms and Products are onboard, but intent is to keep it limited to small finite number so that automation can be used over time for repo creation, pipelines creation** and maintenance.
All repo names should be lowercase, alphanumeric and use dashes as separators with no spaces to make it easier to work on multiple platforms (Windows will be case insensitive but Linux will be case sensitive). Additionally repo names should not change once in use. (We could consider a name change early in the use of or prior to any use if there is a misspelling or other mistake in the naming of the repository.) This is true even if the scope of the repository is expanded or updated. It is more important to keep the same name so that developers and automations can "find" the code rather than have a repo that is named too specifically for the current use case. If there is a strong need to update the name of the repo, it should be forked into a new repo.
Recommended Repository Folder Structure
As Git repo will be used to store more than code e.g. functional tests, documents, infrastructure code, configurations, build scripts etc, the top level of a repo should give an overview, and should be sparse. In addition, a consistent folder structure in a repo of a certain type will also support standardization and automation of aspects like Pipelines, auto reviewers etc.
Based on this, below is the recommended top level folder structure for the repo:
- root should contain limited files. Below files are recommended:
- a "README.md" file that provides an overview of the repo. The README shall be well documented and is subjected to auditing by org admin to validate that it depicts a clear interpretation of the repo. This will allow the organization to better catalog and share assets across projects through tooling.
- a ".gitignore" file for the repo
- a "pull_ request_template.md" is the default Pull Request template file that contains markdown text that is added to your pull request description when the pull request is created
- a changelog.md to document the release history details
- a "MDT_METADATA.json" to document component ownership of the repo
- an "azure-pipelines.yml" to orchestrate YAML based pipeline activity
-
/docs folder should contain all the required documentation for the repo and will be published to the wiki. Wiki's can be organized and auto-generated to publish determined by team settings.
-
/src folder should contain all the source code and unit tests for the repo
- /rest-generated-client folder is only needed for the REST service repos to allow for auto generation of the service client libraries. It should contain one sub-folder for each language in which the client library for service should be generated. Each folder contains the configuration files needed for the specific language. For example, if you need a client in dotnet and java then two sub-folders with names "dotnet" and "java" should exist and should contain the required configuration files for respective languages.
-
/infra folder should contain Infra-As-Code (IAC) files and configuration files required to complete the build and deployment of the artifact from the repo. This does not include the shared infrastructure for the platform or the product e.g. Kubernetes Cluster, Shared DB or Hbase Environment setup IAC files. Typical examples would include Docker file to generate Docker image, helm charts and related configurations, any environment specific configurations. This folder should contain following sub-folders based on the needs of the repo:
- config: this folder should be used when there is a need to overwrite certain application configurations in non-containerized environment for build and deployment and the configurations are
- helm: for helm related files such as templates, values yaml files etc.
- docker: for dockerfile, any entry point shell scripts, any configuration files, docker compose files etc.
- developer: for local developer environment setup scripts
It is recommended to maintain any environment specific configuration files in these respective sub-folders folders with the environment names in the filename e.g. for helm values.yaml there should be values-dev.yaml, values-qa.yaml and so on depending upon the environments needs of the product.
Note: Do not maintain any secrets in the source code repository. Refer best practices on Secret Management on how to manage the secrets.
-
/test folder should contain all testing related code and artifacts for functional tests such as UI driven Selenium tests and API Tests; and nonfunctional tests such as performance tests and security tests.
Supported Branching Strategy
This section describes the supported branching strategy for managing source code branches in AzDo Git repositories. The goal is to have a branching strategy that that ensures:
- developer can develop without impacting others i.e. supports parallel isolated development
- quality check can be applied before developer code is brought into a shared branch
- maintain a high quality, up-to-date main branch
- support code isolation for release during the final testing and release preparation while new development for next releases can start
- supports releasing hotfixes to the production
- multiple long development projects with their own release cycles can be supported, on need basis until the product can support feature flags
Based on above goals, a modified Gitflow Workflow branching strategy is recommended. Following branches are used in this workflow:
- main: There is only one "main" (formerly "master") branch and it reflects the code in production ready stage. This branch will be used to build the production deliverable artifacts. This branch will store the release history of the repo.
- develop: There is only one "develop" branch and it serves as it has the latest delivered development complete code for the next release. "develop" branch serves as central collaboration branch.
- project: "project" branches are optional. When feature toggles are not supported, then long term project branches will allow features for project to be integrated before pushing into "develop" with Pull Request (PR). Once "project" branch is merged into "develop" via PRs and is no longer needed, it should be deleted.
- feature: For each new User Story/Task that requires code change a new feature branch should be created from "develop"/"project". When the Task/User Story is complete the "feature" branch gets merged back into the "develop"/"project" using a Pull Request (PR). "feature" branches are short lived, once PR is complete "feature" branch is deleted.
- release: "release" branches are optional. The purpose of "release" branch is to help polish and finish the release while development for next release can get started in parallel. "release" branch is created from "develop" branch. Any fixes required in the ""release" branch can be done by creating a "feature" branch with "release" as parent and merging via PR. Once the release is ready then it gets merged into "develop" and "main" via PRs; and "release" branch is deleted.
- hotfix: "hotfix" branches are used to quickly patch the production release. "hotfix" branch is created from "main". This is the only other branch other than "develop" that is created from "main". Once hotfix is complete, the "hotfix" branch is merged into "develop" and "main" via PRs; and finally deleted.
Below diagram shows the recommended branching strategy and workflow
Below are the naming conventions for the branches:
- feature branches should follow a pattern of "feature/UserStory#-Task#-BriefDescription" e.g. feature/123-234-login
- hotfix branches should follow the pattern of "hotfix/description" e.g. hotfix/cachingbugfix
- project branches should follow a pattern of "project/projectname" e.g. project/orion
- release branches should follow a pattern or "release/releasename" e.g. release/linqII
Below are the general rules that apply while creating the branches
- Contributors and Project administrators should not create branches just with names 'project', 'feature', 'release' or 'hotfix'
- This will restrict creation of branches under project/, feature/, release/ and hotfix/, even though such branches are deleted
- In-case such branches are created by mistake, please delete those branches and raise a support ticket with the Energizers Team to refresh the branch policies
- Users can go-ahead with branch creations under project/, feature/, release/ and hotfix/ after the policies are refreshed
- 'project', 'feature', 'release' and 'hotfix' should be considered as folder names, not branch names
Recommended Branch Security and Polices
Branch security and policies are intended to protect important branches. Policies enforce code quality and change management standards to be applied consistently.
Recommended Branch Policies
Following policies are recommended for the branches to maintain the quality:
- No direct check-in should be allowed into the non "feature" branches. All code should be added to these branches as a result of a PR from another branch.
- Every PR should be approved by at least 2 reviewers other than the requestor of the PR
- For the PR, prohibit the most recent pusher from approving their own changes
- For the PR, reset all approvals after a new change is pushed
- Every PR should have at least one linked work item
- All review comments on PR should be Closed before PR can be merged
- PR validation builds should be triggered automatically and pass for PR to be completed
- PR validation builds should run the tests (at minimum unit tests), all tests should pass, and code coverage reports should be published. It is recommended that quality management plan for the Platform should be followed
- PR validation builds should run Static Code Analysis. Any issues found should be resolved for the PR to be completed
- Squash and Rebase merge should not be allowed for develop and main branches
- "develop" should be the default and compare branch
Recommended Branch Security Settings
Along with branch policies it is also recommended to use the branch security to protect non "feature" branches:
- Security setting should be applied to ensure that branch names follow allowed naming patterns
- Prevent bypassing of the policies when completing a PR.
- Prevent bypassing policies when pushing
- Allow anyone to create a tag
- Allow only a few restricted roles to
- Force push i.e. rewrite history, delete branches and tags
- Remove others' locks
- Edit branch policies
Other Recommended Settings
In addition to above below are recommended settings for the repos:
- Allow for automatic linking of work items using commit comment.
- Until we have the final review process for work items defined, do not allow mentions in commit comments to close work items.
TODO / Open Items List
- Updates Platforms, Product names once finalized at Org level
- Based on pipeline and deployment strategy, discuss and update if "cicd" should be split into build and deploy and infra merged into these folders.
- Make edits once one AzDo Project vs multiple AzDo Projects decision is made
Reference Links
-
Branching Strategy
- [Gitflow Basics] (https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow).
- Microsoft recommendation on Git branching strategy
-
Mono vs Multiple Repos
- https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b
- https://docs.microsoft.com/en-us/azure/architecture/microservices/ci-cd
- https://hackernoon.com/mono-repo-vs-multi-repo-vs-hybrid-whats-the-right-approach-dv1a3ugn
- https://github.com/IcaliaLabs/guides/wiki/Monolithic-vs-Micro-Repos
-
Git Repo Type
- Git vs TFVC feature comparison for more details.
- [Benefits of using Git] (https://www.atlassian.com/git/tutorials/why-git#:~:text=One%20of%20the%20biggest%20advantages,every%20change%20to%20your%20codebase.).
- Branch Policies
- [Protecting your Git branches with branch policies] (https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies?toc=%2Fazure%2Fdevops%2Forganizations%2Ftoc.json&bc=%2Fazure%2Fdevops%2Forganizations%2Fbreadcrumb%2Ftoc.json&view=azure-devops)