[{"data":1,"prerenderedAt":2007},["ShallowReactive",2],{"/blog/keeping-your-development-dry":3,"navigation-en-us":1209,"banner-en-us":1637,"footer-en-us":1647,"blog-post-authors-en-us-Noah Ing|Joe Randazzo":1890,"blog-related-posts-en-us-keeping-your-development-dry":1917,"blog-promotions-en-us":1943,"next-steps-en-us":1997},{"id":4,"title":5,"authors":6,"body":9,"category":1188,"date":1189,"description":1190,"extension":953,"externalUrl":1191,"faq":1191,"featured":1192,"heroImage":1193,"meta":1194,"navigation":178,"path":1195,"seo":1196,"slug":1200,"stem":1201,"tags":1202,"template":1207,"updatedDate":1191,"__hash__":1208},"blogPosts/en-us/blog/keeping-your-development-dry.md","DRY development: A cheatsheet on reusability throughout GitLab",[7,8],"Noah Ing","Joe Randazzo",{"type":10,"value":11,"toc":1171},"minimark",[12,23,26,35,40,44,60,133,137,144,293,296,308,454,458,467,734,738,745,859,866,870,877,928,932,936,949,1086,1090,1097,1101,1108,1112,1121,1124,1128,1153,1167],[13,14,15,16,22],"p",{},"More than 20 years ago, the book ",[17,18,21],"a",{"href":19,"rel":20},"https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/",[],"The Pragmatic Programmer"," brought attention to the DRY principle, or “Don’t Repeat Yourself.\" This principle is defined as every piece of knowledge must have a single, unambiguous, authoritative representation within a system.",[13,24,25],{},"The main problem to solve here is minimizing duplication. As a development project is bombarded with new requests or changing requirements, DevOps teams must balance between development of net-new features or maintaining existing code. The important part is how to reduce duplicate knowledge across projects.",[13,27,28,29,34],{},"This tutorial explores the mechanisms throughout GitLab that leverage the DRY principle to cut down on code duplication and standardize on knowledge. To see working examples of reusability in action, take a look at this ",[17,30,33],{"href":31,"rel":32},"https://gitlab.com/guided-explorations/gitlab-ci-yml-tips-tricks-and-hacks/dry-repository-a-cheatsheet",[],"repository",".",[36,37,39],"h2",{"id":38},"minimizing-duplication-in-cicd","Minimizing duplication in CI/CD",[41,42,43],"h3",{"id":43},"include",[13,45,46,53,54,59],{},[17,47,50],{"href":48,"rel":49},"https://docs.gitlab.com/ci/yaml/#include",[],[51,52,43],"code",{}," can be used to transform a single .gitlab-ci.yml file into multiple files to improve readability and minimize duplication. For example, testing, security, or deployment workflows can be broken out into separate templates. This also allows ",[17,55,58],{"href":56,"rel":57},"https://docs.gitlab.com/user/project/codeowners/",[],"ownership"," of the files.",[61,62,67],"pre",{"className":63,"code":64,"language":65,"meta":66,"style":66},"language-yaml shiki shiki-themes github-light","include:\n  - template: CI/Build.gitlab-ci.yml\n  - template: CI/Test.gitlab-ci.yml\n  - template: CI/Security.gitlab-ci.yml\n  - template: CD/Deploy.gitlab-ci.yml\n\n","yaml","",[51,68,69,81,97,109,121],{"__ignoreMap":66},[70,71,74,77],"span",{"class":72,"line":73},"line",1,[70,75,43],{"class":76},"shJU0",[70,78,80],{"class":79},"sgsFI",":\n",[70,82,84,87,90,93],{"class":72,"line":83},2,[70,85,86],{"class":79},"  - ",[70,88,89],{"class":76},"template",[70,91,92],{"class":79},": ",[70,94,96],{"class":95},"sYBdl","CI/Build.gitlab-ci.yml\n",[70,98,100,102,104,106],{"class":72,"line":99},3,[70,101,86],{"class":79},[70,103,89],{"class":76},[70,105,92],{"class":79},[70,107,108],{"class":95},"CI/Test.gitlab-ci.yml\n",[70,110,112,114,116,118],{"class":72,"line":111},4,[70,113,86],{"class":79},[70,115,89],{"class":76},[70,117,92],{"class":79},[70,119,120],{"class":95},"CI/Security.gitlab-ci.yml\n",[70,122,124,126,128,130],{"class":72,"line":123},5,[70,125,86],{"class":79},[70,127,89],{"class":76},[70,129,92],{"class":79},[70,131,132],{"class":95},"CD/Deploy.gitlab-ci.yml\n",[41,134,136],{"id":135},"yaml-anchors","YAML anchors",[13,138,139,143],{},[17,140,136],{"href":141,"rel":142},"https://docs.gitlab.com/ci/yaml/yaml_optimization/#anchors",[]," can be used to reduce repeat syntax and extend blocks of CI workflow, including jobs, variables, and scripts.",[61,145,147],{"className":63,"code":146,"language":65,"meta":66,"style":66},".test_template: &test_suite\n  image: ruby:2.6\n\nunit_test:\n  \u003C\u003C: *test_suite\n  script:\n    - echo \"Running a test here\"\n\nend_to_end_test:\n  \u003C\u003C: *test_suite\n  script:\n    - echo \"Running a test here\"\n\nsmoke_test:\n  \u003C\u003C: *test_suite\n  script:\n    - echo \"Running a test here\"\n\n",[51,148,149,164,174,180,187,200,208,217,222,230,241,248,255,260,268,279,286],{"__ignoreMap":66},[70,150,151,154,156,160],{"class":72,"line":73},[70,152,153],{"class":76},".test_template",[70,155,92],{"class":79},[70,157,159],{"class":158},"sD7c4","&",[70,161,163],{"class":162},"s7eDp","test_suite\n",[70,165,166,169,171],{"class":72,"line":83},[70,167,168],{"class":76},"  image",[70,170,92],{"class":79},[70,172,173],{"class":95},"ruby:2.6\n",[70,175,176],{"class":72,"line":99},[70,177,179],{"emptyLinePlaceholder":178},true,"\n",[70,181,182,185],{"class":72,"line":111},[70,183,184],{"class":76},"unit_test",[70,186,80],{"class":79},[70,188,189,193,195,198],{"class":72,"line":123},[70,190,192],{"class":191},"sYu0t","  \u003C\u003C",[70,194,92],{"class":79},[70,196,197],{"class":158},"*",[70,199,163],{"class":79},[70,201,203,206],{"class":72,"line":202},6,[70,204,205],{"class":76},"  script",[70,207,80],{"class":79},[70,209,211,214],{"class":72,"line":210},7,[70,212,213],{"class":79},"    - ",[70,215,216],{"class":95},"echo \"Running a test here\"\n",[70,218,220],{"class":72,"line":219},8,[70,221,179],{"emptyLinePlaceholder":178},[70,223,225,228],{"class":72,"line":224},9,[70,226,227],{"class":76},"end_to_end_test",[70,229,80],{"class":79},[70,231,233,235,237,239],{"class":72,"line":232},10,[70,234,192],{"class":191},[70,236,92],{"class":79},[70,238,197],{"class":158},[70,240,163],{"class":79},[70,242,244,246],{"class":72,"line":243},11,[70,245,205],{"class":76},[70,247,80],{"class":79},[70,249,251,253],{"class":72,"line":250},12,[70,252,213],{"class":79},[70,254,216],{"class":95},[70,256,258],{"class":72,"line":257},13,[70,259,179],{"emptyLinePlaceholder":178},[70,261,263,266],{"class":72,"line":262},14,[70,264,265],{"class":76},"smoke_test",[70,267,80],{"class":79},[70,269,271,273,275,277],{"class":72,"line":270},15,[70,272,192],{"class":191},[70,274,92],{"class":79},[70,276,197],{"class":158},[70,278,163],{"class":79},[70,280,282,284],{"class":72,"line":281},16,[70,283,205],{"class":76},[70,285,80],{"class":79},[70,287,289,291],{"class":72,"line":288},17,[70,290,213],{"class":79},[70,292,216],{"class":95},[41,294,295],{"id":295},"extends",[13,297,298,304,305,34],{},[17,299,302],{"href":300,"rel":301},"https://docs.gitlab.com/ci/yaml/#extends",[],[51,303,295],{}," is similar to anchors with additional flexibility and readability. The major difference is it can be used with ",[51,306,307],{},"includes",[61,309,311],{"className":63,"code":310,"language":65,"meta":66,"style":66},".prepare_deploy:\n  stage: deploy\n  script:\n    - echo \"I am preparing the deploy\"\n  only:\n    - main\n\ndeploy_to_dev:\n  extends: .prepare_deploy\n  script:\n    - echo \"Deploy to dev environment\"\n  environment: dev\n\ndeploy_to_production:\n  extends: .prepare_deploy\n  script:\n    - echo \"Deploy to production environment\"\n  when: manual\n  environment: production\n\n",[51,312,313,320,330,336,343,350,357,361,368,378,384,391,401,405,412,420,426,433,444],{"__ignoreMap":66},[70,314,315,318],{"class":72,"line":73},[70,316,317],{"class":76},".prepare_deploy",[70,319,80],{"class":79},[70,321,322,325,327],{"class":72,"line":83},[70,323,324],{"class":76},"  stage",[70,326,92],{"class":79},[70,328,329],{"class":95},"deploy\n",[70,331,332,334],{"class":72,"line":99},[70,333,205],{"class":76},[70,335,80],{"class":79},[70,337,338,340],{"class":72,"line":111},[70,339,213],{"class":79},[70,341,342],{"class":95},"echo \"I am preparing the deploy\"\n",[70,344,345,348],{"class":72,"line":123},[70,346,347],{"class":76},"  only",[70,349,80],{"class":79},[70,351,352,354],{"class":72,"line":202},[70,353,213],{"class":79},[70,355,356],{"class":95},"main\n",[70,358,359],{"class":72,"line":210},[70,360,179],{"emptyLinePlaceholder":178},[70,362,363,366],{"class":72,"line":219},[70,364,365],{"class":76},"deploy_to_dev",[70,367,80],{"class":79},[70,369,370,373,375],{"class":72,"line":224},[70,371,372],{"class":76},"  extends",[70,374,92],{"class":79},[70,376,377],{"class":95},".prepare_deploy\n",[70,379,380,382],{"class":72,"line":232},[70,381,205],{"class":76},[70,383,80],{"class":79},[70,385,386,388],{"class":72,"line":243},[70,387,213],{"class":79},[70,389,390],{"class":95},"echo \"Deploy to dev environment\"\n",[70,392,393,396,398],{"class":72,"line":250},[70,394,395],{"class":76},"  environment",[70,397,92],{"class":79},[70,399,400],{"class":95},"dev\n",[70,402,403],{"class":72,"line":257},[70,404,179],{"emptyLinePlaceholder":178},[70,406,407,410],{"class":72,"line":262},[70,408,409],{"class":76},"deploy_to_production",[70,411,80],{"class":79},[70,413,414,416,418],{"class":72,"line":270},[70,415,372],{"class":76},[70,417,92],{"class":79},[70,419,377],{"class":95},[70,421,422,424],{"class":72,"line":281},[70,423,205],{"class":76},[70,425,80],{"class":79},[70,427,428,430],{"class":72,"line":288},[70,429,213],{"class":79},[70,431,432],{"class":95},"echo \"Deploy to production environment\"\n",[70,434,436,439,441],{"class":72,"line":435},18,[70,437,438],{"class":76},"  when",[70,440,92],{"class":79},[70,442,443],{"class":95},"manual\n",[70,445,447,449,451],{"class":72,"line":446},19,[70,448,395],{"class":76},[70,450,92],{"class":79},[70,452,453],{"class":95},"production\n",[41,455,457],{"id":456},"reference","!reference",[13,459,460,466],{},[17,461,464],{"href":462,"rel":463},"https://docs.gitlab.com/ci/yaml/yaml_optimization/#reference-tags",[],[51,465,457],{}," enables the selection of keyword configuration from other job sections and reuse in the current session.",[61,468,470],{"className":63,"code":469,"language":65,"meta":66,"style":66},".vars:\n  variables:\n    DEV_URL: \"http://dev-url.com\"\n    STAGING_URL: \"http://staging-url.com\"\n\n.setup_env:\n  script:\n    - echo \"Creating Environment\"\n\n.teardown_env:\n  after_script:\n    - echo \"Deleting Environment\"\n\nintegration_test:\n  variables: !reference [.vars, variables, DEV_URL]\n  script:\n    - !reference [.setup_env, script]\n    - echo \"Run Test\"\n  after_script:\n    - !reference [.teardown_env, after_script]\n\nperformance_test:\n  variables: !reference [.vars, variables]\n  script:\n    - !reference [.setup_env, script]\n    - echo \"Run Test\"\n  after_script:\n    - !reference [.teardown_env, after_script]\n\n",[51,471,472,479,486,496,506,510,517,523,530,534,541,548,555,559,566,593,599,616,623,629,647,652,660,679,686,703,710,717],{"__ignoreMap":66},[70,473,474,477],{"class":72,"line":73},[70,475,476],{"class":76},".vars",[70,478,80],{"class":79},[70,480,481,484],{"class":72,"line":83},[70,482,483],{"class":76},"  variables",[70,485,80],{"class":79},[70,487,488,491,493],{"class":72,"line":99},[70,489,490],{"class":76},"    DEV_URL",[70,492,92],{"class":79},[70,494,495],{"class":95},"\"http://dev-url.com\"\n",[70,497,498,501,503],{"class":72,"line":111},[70,499,500],{"class":76},"    STAGING_URL",[70,502,92],{"class":79},[70,504,505],{"class":95},"\"http://staging-url.com\"\n",[70,507,508],{"class":72,"line":123},[70,509,179],{"emptyLinePlaceholder":178},[70,511,512,515],{"class":72,"line":202},[70,513,514],{"class":76},".setup_env",[70,516,80],{"class":79},[70,518,519,521],{"class":72,"line":210},[70,520,205],{"class":76},[70,522,80],{"class":79},[70,524,525,527],{"class":72,"line":219},[70,526,213],{"class":79},[70,528,529],{"class":95},"echo \"Creating Environment\"\n",[70,531,532],{"class":72,"line":224},[70,533,179],{"emptyLinePlaceholder":178},[70,535,536,539],{"class":72,"line":232},[70,537,538],{"class":76},".teardown_env",[70,540,80],{"class":79},[70,542,543,546],{"class":72,"line":243},[70,544,545],{"class":76},"  after_script",[70,547,80],{"class":79},[70,549,550,552],{"class":72,"line":250},[70,551,213],{"class":79},[70,553,554],{"class":95},"echo \"Deleting Environment\"\n",[70,556,557],{"class":72,"line":257},[70,558,179],{"emptyLinePlaceholder":178},[70,560,561,564],{"class":72,"line":262},[70,562,563],{"class":76},"integration_test",[70,565,80],{"class":79},[70,567,568,570,572,574,577,579,582,585,587,590],{"class":72,"line":270},[70,569,483],{"class":76},[70,571,92],{"class":79},[70,573,457],{"class":158},[70,575,576],{"class":79}," [",[70,578,476],{"class":95},[70,580,581],{"class":79},", ",[70,583,584],{"class":95},"variables",[70,586,581],{"class":79},[70,588,589],{"class":95},"DEV_URL",[70,591,592],{"class":79},"]\n",[70,594,595,597],{"class":72,"line":281},[70,596,205],{"class":76},[70,598,80],{"class":79},[70,600,601,603,605,607,609,611,614],{"class":72,"line":288},[70,602,213],{"class":79},[70,604,457],{"class":158},[70,606,576],{"class":79},[70,608,514],{"class":95},[70,610,581],{"class":79},[70,612,613],{"class":95},"script",[70,615,592],{"class":79},[70,617,618,620],{"class":72,"line":435},[70,619,213],{"class":79},[70,621,622],{"class":95},"echo \"Run Test\"\n",[70,624,625,627],{"class":72,"line":446},[70,626,545],{"class":76},[70,628,80],{"class":79},[70,630,632,634,636,638,640,642,645],{"class":72,"line":631},20,[70,633,213],{"class":79},[70,635,457],{"class":158},[70,637,576],{"class":79},[70,639,538],{"class":95},[70,641,581],{"class":79},[70,643,644],{"class":95},"after_script",[70,646,592],{"class":79},[70,648,650],{"class":72,"line":649},21,[70,651,179],{"emptyLinePlaceholder":178},[70,653,655,658],{"class":72,"line":654},22,[70,656,657],{"class":76},"performance_test",[70,659,80],{"class":79},[70,661,663,665,667,669,671,673,675,677],{"class":72,"line":662},23,[70,664,483],{"class":76},[70,666,92],{"class":79},[70,668,457],{"class":158},[70,670,576],{"class":79},[70,672,476],{"class":95},[70,674,581],{"class":79},[70,676,584],{"class":95},[70,678,592],{"class":79},[70,680,682,684],{"class":72,"line":681},24,[70,683,205],{"class":76},[70,685,80],{"class":79},[70,687,689,691,693,695,697,699,701],{"class":72,"line":688},25,[70,690,213],{"class":79},[70,692,457],{"class":158},[70,694,576],{"class":79},[70,696,514],{"class":95},[70,698,581],{"class":79},[70,700,613],{"class":95},[70,702,592],{"class":79},[70,704,706,708],{"class":72,"line":705},26,[70,707,213],{"class":79},[70,709,622],{"class":95},[70,711,713,715],{"class":72,"line":712},27,[70,714,545],{"class":76},[70,716,80],{"class":79},[70,718,720,722,724,726,728,730,732],{"class":72,"line":719},28,[70,721,213],{"class":79},[70,723,457],{"class":158},[70,725,576],{"class":79},[70,727,538],{"class":95},[70,729,581],{"class":79},[70,731,644],{"class":95},[70,733,592],{"class":79},[41,735,737],{"id":736},"downstream-pipelines","Downstream pipelines",[13,739,740,744],{},[17,741,737],{"href":742,"rel":743},"https://docs.gitlab.com/ci/pipelines/downstream_pipelines/",[]," enable the breakout of microservices and their pipelines. A .gitlab-ci.yml file can be used for each service, and when a file or directory is changed, only that pipeline needs to be triggered improving the awareness and readability of what’s deploying.",[61,746,748],{"className":63,"code":747,"language":65,"meta":66,"style":66},"ui:\n  trigger:\n    include: ui/.gitlab-ci.yml\n    strategy: depend\n  rules:\n    - changes: [ui/*]\n\nbackend:\n  trigger:\n    include: backend/.gitlab-ci.yml\n    strategy: depend\n  rules:\n    - changes: [backend/*]\n\n",[51,749,750,757,764,774,784,791,806,810,817,823,832,840,846],{"__ignoreMap":66},[70,751,752,755],{"class":72,"line":73},[70,753,754],{"class":76},"ui",[70,756,80],{"class":79},[70,758,759,762],{"class":72,"line":83},[70,760,761],{"class":76},"  trigger",[70,763,80],{"class":79},[70,765,766,769,771],{"class":72,"line":99},[70,767,768],{"class":76},"    include",[70,770,92],{"class":79},[70,772,773],{"class":95},"ui/.gitlab-ci.yml\n",[70,775,776,779,781],{"class":72,"line":111},[70,777,778],{"class":76},"    strategy",[70,780,92],{"class":79},[70,782,783],{"class":95},"depend\n",[70,785,786,789],{"class":72,"line":123},[70,787,788],{"class":76},"  rules",[70,790,80],{"class":79},[70,792,793,795,798,801,804],{"class":72,"line":202},[70,794,213],{"class":79},[70,796,797],{"class":76},"changes",[70,799,800],{"class":79},": [",[70,802,803],{"class":95},"ui/*",[70,805,592],{"class":79},[70,807,808],{"class":72,"line":210},[70,809,179],{"emptyLinePlaceholder":178},[70,811,812,815],{"class":72,"line":219},[70,813,814],{"class":76},"backend",[70,816,80],{"class":79},[70,818,819,821],{"class":72,"line":224},[70,820,761],{"class":76},[70,822,80],{"class":79},[70,824,825,827,829],{"class":72,"line":232},[70,826,768],{"class":76},[70,828,92],{"class":79},[70,830,831],{"class":95},"backend/.gitlab-ci.yml\n",[70,833,834,836,838],{"class":72,"line":243},[70,835,778],{"class":76},[70,837,92],{"class":79},[70,839,783],{"class":95},[70,841,842,844],{"class":72,"line":250},[70,843,788],{"class":76},[70,845,80],{"class":79},[70,847,848,850,852,854,857],{"class":72,"line":257},[70,849,213],{"class":79},[70,851,797],{"class":76},[70,853,800],{"class":79},[70,855,856],{"class":95},"backend/*",[70,858,592],{"class":79},[13,860,861],{},[862,863],"img",{"alt":864,"src":865},"Dynamic child pipeline","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397707/blog/Content%20Images/2022-02-01-parent-child-vs-multi-project-pipelines/parent-child.png",[41,867,869],{"id":868},"cicd-variables","CI/CD variables",[13,871,872,876],{},[17,873,869],{"href":874,"rel":875},"https://docs.gitlab.com/ci/variables/",[]," can be scoped to a specific level, including the project, group, instance level, or .gitlab-ci.yml level. The values can be stored and reused across a group for project inheritance or overwritten at the project level.",[61,878,880],{"className":63,"code":879,"language":65,"meta":66,"style":66},"variables:\n  PROJECT_LEVEL_VARIABLES: \"I am first in line in precedence\"\n  GROUP_LEVEL_VARIABLES: \"I am second in line\"\n  INSTANCE_LEVEL_VARIABLES: \"I am in third place\"\n  GITLAB_CI_YML_LEVEL_VARIABLES: \"I am last in line of precedence\"\n\n",[51,881,882,888,898,908,918],{"__ignoreMap":66},[70,883,884,886],{"class":72,"line":73},[70,885,584],{"class":76},[70,887,80],{"class":79},[70,889,890,893,895],{"class":72,"line":83},[70,891,892],{"class":76},"  PROJECT_LEVEL_VARIABLES",[70,894,92],{"class":79},[70,896,897],{"class":95},"\"I am first in line in precedence\"\n",[70,899,900,903,905],{"class":72,"line":99},[70,901,902],{"class":76},"  GROUP_LEVEL_VARIABLES",[70,904,92],{"class":79},[70,906,907],{"class":95},"\"I am second in line\"\n",[70,909,910,913,915],{"class":72,"line":111},[70,911,912],{"class":76},"  INSTANCE_LEVEL_VARIABLES",[70,914,92],{"class":79},[70,916,917],{"class":95},"\"I am in third place\"\n",[70,919,920,923,925],{"class":72,"line":123},[70,921,922],{"class":76},"  GITLAB_CI_YML_LEVEL_VARIABLES",[70,924,92],{"class":79},[70,926,927],{"class":95},"\"I am last in line of precedence\"\n",[36,929,931],{"id":930},"creating-consistent-code-reviews-across-multiple-teams","Creating consistent code reviews across multiple teams",[41,933,935],{"id":934},"description-templates","Description templates",[13,937,938,942,943,948],{},[17,939,935],{"href":940,"rel":941},"https://docs.gitlab.com/user/project/description_templates/",[]," enable teams to define a consistent workflow for issues or merge requests. For example, the MR template can define a checklist for rolling out to a feature to ensure it’s documented, quality tested, and reviewed by appropriate team members. Here are ",[17,944,947],{"href":945,"rel":946},"https://gitlab.com/gitlab-org/gitlab/-/tree/master/.gitlab/merge_request_templates",[],"MR templates"," that GitLab team members use daily.",[61,950,954],{"className":951,"code":952,"language":953,"meta":66,"style":66},"language-md shiki shiki-themes github-light","\u003C!-- These templates can be set at the instance or group level to share amongst the organization: https://docs.gitlab.com/user/project/description_templates/#set-instance-level-description-templates -->\n\n## What does this MR do?\n\n\u003C!-- Briefly describe what this MR is about. -->\n\n## Related issues\n\n\u003C!-- Link related issues below. -->\n\n## Create a checklist for the author or reviewer\n- [ ] Optional. Consider taking this writing course before publishing a change.\n- [ ] Follow the documentation process stated here.\n- [ ] Tag this user group if this applies.\n\n\n\u003C!-- Quick Actions - See https://docs.gitlab.com/user/project/quick_actions/#issues-merge-requests-and-epics for a list of all the quick actions available. -->\n\n\u003C!-- Add a label to assign a specific workflow using scoped labels -->\n/label ~documentation ~\"type::maintenance\" ~\"docs::improvement\" ~\"maintenance::refactor\"\n\n\u003C!-- Apply draft format automatically -->\n/draft\n\n\u003C!-- Assign myself or a usergroup -->\n/assign me\n","md",[51,955,956,962,966,972,976,981,985,990,994,999,1003,1008,1017,1024,1031,1035,1039,1044,1048,1053,1058,1062,1067,1072,1076,1081],{"__ignoreMap":66},[70,957,958],{"class":72,"line":73},[70,959,961],{"class":960},"sAwPA","\u003C!-- These templates can be set at the instance or group level to share amongst the organization: https://docs.gitlab.com/user/project/description_templates/#set-instance-level-description-templates -->\n",[70,963,964],{"class":72,"line":83},[70,965,179],{"emptyLinePlaceholder":178},[70,967,968],{"class":72,"line":99},[70,969,971],{"class":970},"surfw","## What does this MR do?\n",[70,973,974],{"class":72,"line":111},[70,975,179],{"emptyLinePlaceholder":178},[70,977,978],{"class":72,"line":123},[70,979,980],{"class":960},"\u003C!-- Briefly describe what this MR is about. -->\n",[70,982,983],{"class":72,"line":202},[70,984,179],{"emptyLinePlaceholder":178},[70,986,987],{"class":72,"line":210},[70,988,989],{"class":970},"## Related issues\n",[70,991,992],{"class":72,"line":219},[70,993,179],{"emptyLinePlaceholder":178},[70,995,996],{"class":72,"line":224},[70,997,998],{"class":960},"\u003C!-- Link related issues below. -->\n",[70,1000,1001],{"class":72,"line":232},[70,1002,179],{"emptyLinePlaceholder":178},[70,1004,1005],{"class":72,"line":243},[70,1006,1007],{"class":970},"## Create a checklist for the author or reviewer\n",[70,1009,1010,1014],{"class":72,"line":250},[70,1011,1013],{"class":1012},"sqxcx","-",[70,1015,1016],{"class":79}," [ ] Optional. Consider taking this writing course before publishing a change.\n",[70,1018,1019,1021],{"class":72,"line":257},[70,1020,1013],{"class":1012},[70,1022,1023],{"class":79}," [ ] Follow the documentation process stated here.\n",[70,1025,1026,1028],{"class":72,"line":262},[70,1027,1013],{"class":1012},[70,1029,1030],{"class":79}," [ ] Tag this user group if this applies.\n",[70,1032,1033],{"class":72,"line":270},[70,1034,179],{"emptyLinePlaceholder":178},[70,1036,1037],{"class":72,"line":281},[70,1038,179],{"emptyLinePlaceholder":178},[70,1040,1041],{"class":72,"line":288},[70,1042,1043],{"class":960},"\u003C!-- Quick Actions - See https://docs.gitlab.com/user/project/quick_actions/#issues-merge-requests-and-epics for a list of all the quick actions available. -->\n",[70,1045,1046],{"class":72,"line":435},[70,1047,179],{"emptyLinePlaceholder":178},[70,1049,1050],{"class":72,"line":446},[70,1051,1052],{"class":960},"\u003C!-- Add a label to assign a specific workflow using scoped labels -->\n",[70,1054,1055],{"class":72,"line":631},[70,1056,1057],{"class":79},"/label ~documentation ~\"type::maintenance\" ~\"docs::improvement\" ~\"maintenance::refactor\"\n",[70,1059,1060],{"class":72,"line":649},[70,1061,179],{"emptyLinePlaceholder":178},[70,1063,1064],{"class":72,"line":654},[70,1065,1066],{"class":960},"\u003C!-- Apply draft format automatically -->\n",[70,1068,1069],{"class":72,"line":662},[70,1070,1071],{"class":79},"/draft\n",[70,1073,1074],{"class":72,"line":681},[70,1075,179],{"emptyLinePlaceholder":178},[70,1077,1078],{"class":72,"line":688},[70,1079,1080],{"class":960},"\u003C!-- Assign myself or a usergroup -->\n",[70,1082,1083],{"class":72,"line":705},[70,1084,1085],{"class":79},"/assign me\n",[41,1087,1089],{"id":1088},"project-templates","Project templates",[13,1091,1092,1096],{},[17,1093,1089],{"href":1094,"rel":1095},"https://docs.gitlab.com/user/group/custom_project_templates/",[]," can be used to define an initial project structure for when new services are being developed. This gives a consistent starting point for projects that come equipped with the latest file configurations and defaults.",[41,1098,1100],{"id":1099},"file-templates","File templates",[13,1102,1103,1107],{},[17,1104,1100],{"href":1105,"rel":1106},"https://docs.gitlab.com/administration/settings/instance_template_repository/",[]," are similar to project templates but are default files to choose from when adding a new file to your repository. The team then can quickly choose from files that have best practices baked in and organization defaults.",[36,1109,1111],{"id":1110},"defining-a-pipeline-center-of-excellence-project-for-cicd-workflows","Defining a Pipeline Center of Excellence project for CI/CD workflows",[13,1113,1114,1115,1120],{},"As you 'productionize' your CI/CD workflows, it’s recommended to create a “Pipeline Center of Excellence” project that contains templates, containers, or other abstracted constructs that can be adopted throughout the organization. This project contains file or CI/CD templates that have the best practices or well-formed workflows defined for development teams to quickly adopt (includes) without recreating the wheel. To explore this in practice, visit ",[17,1116,1119],{"href":1117,"rel":1118},"https://gitlab-org.gitlab.io/professional-services-automation/pipelinecoe/pipeline-templates/#/",[],"Pipeline COE"," documentation written by the GitLab Professional Services team.",[13,1122,1123],{},"Have a reusable component to suggest or that we missed? Add a comment to this blog post or suggest a change to this file!",[36,1125,1127],{"id":1126},"related-posts","Related posts",[1129,1130,1131,1139,1146],"ul",{},[1132,1133,1134],"li",{},[17,1135,1138],{"href":1136,"rel":1137},"https://about.gitlab.com/blog/how-to-keep-up-with-ci-cd-best-practices/",[],"How to keep up with CI/CD best practices",[1132,1140,1141],{},[17,1142,1145],{"href":1143,"rel":1144},"https://about.gitlab.com/blog/how-to-become-more-productive-with-gitlab-ci/",[],"How to become more productive with GitLab CI",[1132,1147,1148],{},[17,1149,1152],{"href":1150,"rel":1151},"https://about.gitlab.com/blog/a-visual-guide-to-gitlab-ci-caching/",[],"A visual guide to GitLab CI/CD caching",[13,1154,1155,1156,1161,1162,34],{},"Cover image by ",[17,1157,1160],{"href":1158,"rel":1159},"https://unsplash.com/@federize?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Federico Beccari"," on ",[17,1163,1166],{"href":1164,"rel":1165},"https://www.unsplash.com",[],"Unsplash",[1168,1169,1170],"style",{},"html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .surfw, html code.shiki .surfw{--shiki-default:#005CC5;--shiki-default-font-weight:bold}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":66,"searchDepth":83,"depth":83,"links":1172},[1173,1181,1186,1187],{"id":38,"depth":83,"text":39,"children":1174},[1175,1176,1177,1178,1179,1180],{"id":43,"depth":99,"text":43},{"id":135,"depth":99,"text":136},{"id":295,"depth":99,"text":295},{"id":456,"depth":99,"text":457},{"id":736,"depth":99,"text":737},{"id":868,"depth":99,"text":869},{"id":930,"depth":83,"text":931,"children":1182},[1183,1184,1185],{"id":934,"depth":99,"text":935},{"id":1088,"depth":99,"text":1089},{"id":1099,"depth":99,"text":1100},{"id":1110,"depth":83,"text":1111},{"id":1126,"depth":83,"text":1127},"engineering","2023-01-03","How to follow the DevOps principle of 'don't repeat yourself' to optimize CI/CD.",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749683555/Blog/Hero%20Images/drylights.jpg",{},"/en-us/blog/keeping-your-development-dry",{"title":5,"description":1190,"ogTitle":5,"ogDescription":1190,"noIndex":1192,"ogImage":1193,"ogUrl":1197,"ogSiteName":1198,"ogType":1199,"canonicalUrls":1197},"https://about.gitlab.com/blog/keeping-your-development-dry","https://about.gitlab.com","article","keeping-your-development-dry","en-us/blog/keeping-your-development-dry",[1203,1204,1205,1206],"CI","CD","DevOps","DevSecOps","BlogPost","IzABIFzydKLtxyLFv9EiJAtPf5hiOqBkyD-oHIb2HY0",{"logo":1210,"freeTrial":1215,"sales":1220,"login":1225,"items":1230,"search":1557,"minimal":1588,"duo":1607,"switchNav":1616,"pricingDeployment":1627},{"config":1211},{"href":1212,"dataGaName":1213,"dataGaLocation":1214},"/","gitlab logo","header",{"text":1216,"config":1217},"Get free trial",{"href":1218,"dataGaName":1219,"dataGaLocation":1214},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":1221,"config":1222},"Request a demo",{"href":1223,"dataGaName":1224,"dataGaLocation":1214},"/sales/?contact-topic=request-demo","sales",{"text":1226,"config":1227},"Sign in",{"href":1228,"dataGaName":1229,"dataGaLocation":1214},"https://gitlab.com/users/sign_in/","sign in",[1231,1260,1360,1365,1479,1535],{"text":1232,"config":1233,"menu":1235},"Platform",{"dataNavLevelOne":1234},"platform",{"type":1236,"columns":1237},"cards",[1238,1244,1252],{"title":1232,"description":1239,"link":1240},"The intelligent orchestration platform for DevSecOps",{"text":1241,"config":1242},"Explore our Platform",{"href":1243,"dataGaName":1234,"dataGaLocation":1214},"/platform/",{"title":1245,"description":1246,"link":1247},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":1248,"config":1249},"Meet GitLab Duo",{"href":1250,"dataGaName":1251,"dataGaLocation":1214},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1253,"description":1254,"link":1255},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1256,"config":1257},"Learn more",{"href":1258,"dataGaName":1259,"dataGaLocation":1214},"/why-gitlab/","why gitlab",{"text":1261,"left":178,"config":1262,"menu":1264},"Product",{"dataNavLevelOne":1263},"solutions",{"type":1265,"link":1266,"columns":1270,"feature":1339},"lists",{"text":1267,"config":1268},"View all Solutions",{"href":1269,"dataGaName":1263,"dataGaLocation":1214},"/solutions/",[1271,1295,1318],{"title":1272,"description":1273,"link":1274,"items":1279},"Automation","CI/CD and automation to accelerate deployment",{"config":1275},{"icon":1276,"href":1277,"dataGaName":1278,"dataGaLocation":1214},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[1280,1284,1287,1291],{"text":1281,"config":1282},"CI/CD",{"href":1283,"dataGaLocation":1214,"dataGaName":1281},"/solutions/continuous-integration/",{"text":1245,"config":1285},{"href":1250,"dataGaLocation":1214,"dataGaName":1286},"gitlab duo agent platform - product menu",{"text":1288,"config":1289},"Source Code Management",{"href":1290,"dataGaLocation":1214,"dataGaName":1288},"/solutions/source-code-management/",{"text":1292,"config":1293},"Automated Software Delivery",{"href":1277,"dataGaLocation":1214,"dataGaName":1294},"Automated software delivery",{"title":1296,"description":1297,"link":1298,"items":1303},"Security","Deliver code faster without compromising security",{"config":1299},{"href":1300,"dataGaName":1301,"dataGaLocation":1214,"icon":1302},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1304,1308,1313],{"text":1305,"config":1306},"Application Security Testing",{"href":1300,"dataGaName":1307,"dataGaLocation":1214},"Application security testing",{"text":1309,"config":1310},"Software Supply Chain Security",{"href":1311,"dataGaLocation":1214,"dataGaName":1312},"/solutions/supply-chain/","Software supply chain security",{"text":1314,"config":1315},"Software Compliance",{"href":1316,"dataGaName":1317,"dataGaLocation":1214},"/solutions/software-compliance/","software compliance",{"title":1319,"link":1320,"items":1325},"Measurement",{"config":1321},{"icon":1322,"href":1323,"dataGaName":1324,"dataGaLocation":1214},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[1326,1330,1334],{"text":1327,"config":1328},"Visibility & Measurement",{"href":1323,"dataGaLocation":1214,"dataGaName":1329},"Visibility and Measurement",{"text":1331,"config":1332},"Value Stream Management",{"href":1333,"dataGaLocation":1214,"dataGaName":1331},"/solutions/value-stream-management/",{"text":1335,"config":1336},"Analytics & Insights",{"href":1337,"dataGaLocation":1214,"dataGaName":1338},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1340,"type":1265,"items":1341},"GitLab for",[1342,1348,1354],{"text":1343,"config":1344},"Enterprise",{"icon":1345,"href":1346,"dataGaLocation":1214,"dataGaName":1347},"Building","/enterprise/","enterprise",{"text":1349,"config":1350},"Small Business",{"icon":1351,"href":1352,"dataGaLocation":1214,"dataGaName":1353},"Work","/small-business/","small business",{"text":1355,"config":1356},"Public Sector",{"icon":1357,"href":1358,"dataGaLocation":1214,"dataGaName":1359},"Organization","/solutions/public-sector/","public sector",{"text":1361,"config":1362},"Pricing",{"href":1363,"dataGaName":1364,"dataGaLocation":1214,"dataNavLevelOne":1364},"/pricing/","pricing",{"text":1366,"config":1367,"menu":1369},"Resources",{"dataNavLevelOne":1368},"resources",{"type":1265,"link":1370,"columns":1374,"feature":1468},{"text":1371,"config":1372},"View all resources",{"href":1373,"dataGaName":1368,"dataGaLocation":1214},"/resources/",[1375,1408,1435],{"title":1376,"items":1377},"Getting started",[1378,1383,1388,1393,1398,1403],{"text":1379,"config":1380},"Install",{"href":1381,"dataGaName":1382,"dataGaLocation":1214},"/install/","install",{"text":1384,"config":1385},"Quick start guides",{"href":1386,"dataGaName":1387,"dataGaLocation":1214},"/get-started/","quick setup checklists",{"text":1389,"config":1390},"Learn",{"href":1391,"dataGaLocation":1214,"dataGaName":1392},"https://university.gitlab.com/","learn",{"text":1394,"config":1395},"Product documentation",{"href":1396,"dataGaName":1397,"dataGaLocation":1214},"https://docs.gitlab.com/","product documentation",{"text":1399,"config":1400},"Best practice videos",{"href":1401,"dataGaName":1402,"dataGaLocation":1214},"/getting-started-videos/","best practice videos",{"text":1404,"config":1405},"Integrations",{"href":1406,"dataGaName":1407,"dataGaLocation":1214},"/integrations/","integrations",{"title":1409,"items":1410},"Discover",[1411,1416,1421,1426,1430],{"text":1412,"config":1413},"Customer success stories",{"href":1414,"dataGaName":1415,"dataGaLocation":1214},"/customers/","customer success stories",{"text":1417,"config":1418},"Blog",{"href":1419,"dataGaName":1420,"dataGaLocation":1214},"/blog/","blog",{"text":1422,"config":1423},"Demo Hub",{"href":1424,"dataGaName":1425,"dataGaLocation":1214},"/demo-hub/","demo hub",{"text":1427,"config":1428},"The Source",{"href":1429,"dataGaName":1420,"dataGaLocation":1214},"/the-source/",{"text":1431,"config":1432},"Remote",{"href":1433,"dataGaName":1434,"dataGaLocation":1214},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1436,"items":1437},"Connect",[1438,1443,1448,1453,1458,1463],{"text":1439,"config":1440},"GitLab Services",{"href":1441,"dataGaName":1442,"dataGaLocation":1214},"/services/","services",{"text":1444,"config":1445},"Contribute",{"href":1446,"dataGaName":1447,"dataGaLocation":1214},"https://contributors.gitlab.com","contribute",{"text":1449,"config":1450},"Community",{"href":1451,"dataGaName":1452,"dataGaLocation":1214},"/community/","community",{"text":1454,"config":1455},"Forum",{"href":1456,"dataGaName":1457,"dataGaLocation":1214},"https://forum.gitlab.com/","forum",{"text":1459,"config":1460},"Events",{"href":1461,"dataGaName":1462,"dataGaLocation":1214},"/events/","events",{"text":1464,"config":1465},"Partners",{"href":1466,"dataGaName":1467,"dataGaLocation":1214},"/partners/","partners",{"config":1469,"title":1472,"text":1473,"link":1474},{"background":1470,"textColor":1471},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":1475,"config":1476},"Read the latest",{"href":1477,"dataGaName":1478,"dataGaLocation":1214},"/whats-new/","whats new",{"text":1480,"config":1481,"menu":1483},"Company",{"dataNavLevelOne":1482},"company",{"type":1265,"columns":1484},[1485],{"items":1486},[1487,1492,1498,1500,1505,1510,1515,1520,1525,1530],{"text":1488,"config":1489},"About",{"href":1490,"dataGaName":1491,"dataGaLocation":1214},"/company/","about",{"text":1493,"config":1494,"footerGa":1497},"Jobs",{"href":1495,"dataGaName":1496,"dataGaLocation":1214},"/jobs/","jobs",{"dataGaName":1496},{"text":1459,"config":1499},{"href":1461,"dataGaName":1462,"dataGaLocation":1214},{"text":1501,"config":1502},"Leadership",{"href":1503,"dataGaName":1504,"dataGaLocation":1214},"/company/team/e-group/","leadership",{"text":1506,"config":1507},"Handbook",{"href":1508,"dataGaName":1509,"dataGaLocation":1214},"https://handbook.gitlab.com/","handbook",{"text":1511,"config":1512},"Investor relations",{"href":1513,"dataGaName":1514,"dataGaLocation":1214},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1516,"config":1517},"Trust Center",{"href":1518,"dataGaName":1519,"dataGaLocation":1214},"/security/","trust center",{"text":1521,"config":1522},"AI Transparency Center",{"href":1523,"dataGaName":1524,"dataGaLocation":1214},"/ai-transparency-center/","ai transparency center",{"text":1526,"config":1527},"Newsletter",{"href":1528,"dataGaName":1529,"dataGaLocation":1214},"/company/contact/#contact-forms","newsletter",{"text":1531,"config":1532},"Press",{"href":1533,"dataGaName":1534,"dataGaLocation":1214},"/press/","press",{"text":1536,"config":1537,"menu":1538},"Contact us",{"dataNavLevelOne":1482},{"type":1265,"columns":1539},[1540],{"items":1541},[1542,1547,1552],{"text":1543,"config":1544},"Talk to sales",{"href":1545,"dataGaName":1546,"dataGaLocation":1214},"/sales/","talk to sales",{"text":1548,"config":1549},"Support portal",{"href":1550,"dataGaName":1551,"dataGaLocation":1214},"https://support.gitlab.com/hc/en-us","support portal",{"text":1553,"config":1554},"Customer portal",{"href":1555,"dataGaName":1556,"dataGaLocation":1214},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1558,"login":1559,"suggestions":1566},"Close",{"text":1560,"link":1561},"To search repositories and projects, login to",{"text":1562,"config":1563},"gitlab.com",{"href":1228,"dataGaName":1564,"dataGaLocation":1565},"search login","search",{"text":1567,"default":1568},"Suggestions",[1569,1571,1575,1577,1581,1585],{"text":1245,"config":1570},{"href":1250,"dataGaName":1245,"dataGaLocation":1565},{"text":1572,"config":1573},"Code Suggestions (AI)",{"href":1574,"dataGaName":1572,"dataGaLocation":1565},"/solutions/code-suggestions/",{"text":1281,"config":1576},{"href":1283,"dataGaName":1281,"dataGaLocation":1565},{"text":1578,"config":1579},"GitLab on AWS",{"href":1580,"dataGaName":1578,"dataGaLocation":1565},"/partners/technology-partners/aws/",{"text":1582,"config":1583},"GitLab on Google Cloud",{"href":1584,"dataGaName":1582,"dataGaLocation":1565},"/partners/technology-partners/google-cloud-platform/",{"text":1586,"config":1587},"Why GitLab?",{"href":1258,"dataGaName":1586,"dataGaLocation":1565},{"freeTrial":1589,"mobileIcon":1594,"desktopIcon":1599,"secondaryButton":1602},{"text":1590,"config":1591},"Start free trial",{"href":1592,"dataGaName":1219,"dataGaLocation":1593},"https://gitlab.com/-/trials/new/","nav",{"altText":1595,"config":1596},"Gitlab Icon",{"src":1597,"dataGaName":1598,"dataGaLocation":1593},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1595,"config":1600},{"src":1601,"dataGaName":1598,"dataGaLocation":1593},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1603,"config":1604},"Get Started",{"href":1605,"dataGaName":1606,"dataGaLocation":1593},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1608,"mobileIcon":1612,"desktopIcon":1614},{"text":1609,"config":1610},"Learn more about GitLab Duo",{"href":1250,"dataGaName":1611,"dataGaLocation":1593},"gitlab duo",{"altText":1595,"config":1613},{"src":1597,"dataGaName":1598,"dataGaLocation":1593},{"altText":1595,"config":1615},{"src":1601,"dataGaName":1598,"dataGaLocation":1593},{"button":1617,"mobileIcon":1622,"desktopIcon":1624},{"text":1618,"config":1619},"/switch",{"href":1620,"dataGaName":1621,"dataGaLocation":1593},"#contact","switch",{"altText":1595,"config":1623},{"src":1597,"dataGaName":1598,"dataGaLocation":1593},{"altText":1595,"config":1625},{"src":1626,"dataGaName":1598,"dataGaLocation":1593},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1628,"mobileIcon":1633,"desktopIcon":1635},{"text":1629,"config":1630},"Back to pricing",{"href":1363,"dataGaName":1631,"dataGaLocation":1593,"icon":1632},"back to pricing","GoBack",{"altText":1595,"config":1634},{"src":1597,"dataGaName":1598,"dataGaLocation":1593},{"altText":1595,"config":1636},{"src":1601,"dataGaName":1598,"dataGaLocation":1593},{"title":1638,"titleMobile":1639,"button":1640,"config":1645},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1256,"config":1641},{"href":1642,"dataGaName":1643,"dataGaLocation":1644},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1646,"disabled":1192},"release",{"data":1648},{"text":1649,"source":1650,"edit":1656,"contribute":1661,"config":1666,"items":1671,"minimal":1879},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1651,"config":1652},"View page source",{"href":1653,"dataGaName":1654,"dataGaLocation":1655},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1657,"config":1658},"Edit this page",{"href":1659,"dataGaName":1660,"dataGaLocation":1655},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1662,"config":1663},"Please contribute",{"href":1664,"dataGaName":1665,"dataGaLocation":1655},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1667,"facebook":1668,"youtube":1669,"linkedin":1670},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1672,1719,1771,1815,1847],{"title":1361,"links":1673,"subMenu":1688},[1674,1678,1683],{"text":1675,"config":1676},"View plans",{"href":1363,"dataGaName":1677,"dataGaLocation":1655},"view plans",{"text":1679,"config":1680},"Why Premium?",{"href":1681,"dataGaName":1682,"dataGaLocation":1655},"/pricing/premium/","why premium",{"text":1684,"config":1685},"Why Ultimate?",{"href":1686,"dataGaName":1687,"dataGaLocation":1655},"/pricing/ultimate/","why ultimate",[1689],{"title":1690,"links":1691},"Contact Us",[1692,1695,1697,1699,1704,1709,1714],{"text":1693,"config":1694},"Contact sales",{"href":1545,"dataGaName":1224,"dataGaLocation":1655},{"text":1548,"config":1696},{"href":1550,"dataGaName":1551,"dataGaLocation":1655},{"text":1553,"config":1698},{"href":1555,"dataGaName":1556,"dataGaLocation":1655},{"text":1700,"config":1701},"Status",{"href":1702,"dataGaName":1703,"dataGaLocation":1655},"https://status.gitlab.com/","status",{"text":1705,"config":1706},"Terms of use",{"href":1707,"dataGaName":1708,"dataGaLocation":1655},"/terms/","terms of use",{"text":1710,"config":1711},"Privacy statement",{"href":1712,"dataGaName":1713,"dataGaLocation":1655},"/privacy/","privacy statement",{"text":1715,"config":1716},"Cookie preferences",{"dataGaName":1717,"dataGaLocation":1655,"id":1718,"isOneTrustButton":178},"cookie preferences","ot-sdk-btn",{"title":1261,"links":1720,"subMenu":1729},[1721,1725],{"text":1722,"config":1723},"DevSecOps platform",{"href":1243,"dataGaName":1724,"dataGaLocation":1655},"devsecops platform",{"text":1726,"config":1727},"AI-Assisted Development",{"href":1250,"dataGaName":1728,"dataGaLocation":1655},"ai-assisted development",[1730],{"title":1731,"links":1732},"Topics",[1733,1738,1743,1747,1752,1756,1761,1766],{"text":1734,"config":1735},"CICD",{"href":1736,"dataGaName":1737,"dataGaLocation":1655},"/topics/ci-cd/","cicd",{"text":1739,"config":1740},"GitOps",{"href":1741,"dataGaName":1742,"dataGaLocation":1655},"/topics/gitops/","gitops",{"text":1205,"config":1744},{"href":1745,"dataGaName":1746,"dataGaLocation":1655},"/topics/devops/","devops",{"text":1748,"config":1749},"Version Control",{"href":1750,"dataGaName":1751,"dataGaLocation":1655},"/topics/version-control/","version control",{"text":1206,"config":1753},{"href":1754,"dataGaName":1755,"dataGaLocation":1655},"/topics/devsecops/","devsecops",{"text":1757,"config":1758},"Cloud Native",{"href":1759,"dataGaName":1760,"dataGaLocation":1655},"/topics/cloud-native/","cloud native",{"text":1762,"config":1763},"AI for Coding",{"href":1764,"dataGaName":1765,"dataGaLocation":1655},"/topics/devops/ai-for-coding/","ai for coding",{"text":1767,"config":1768},"Agentic AI",{"href":1769,"dataGaName":1770,"dataGaLocation":1655},"/topics/agentic-ai/","agentic ai",{"title":1772,"links":1773},"Solutions",[1774,1776,1778,1783,1787,1790,1794,1797,1799,1802,1805,1810],{"text":1305,"config":1775},{"href":1300,"dataGaName":1305,"dataGaLocation":1655},{"text":1294,"config":1777},{"href":1277,"dataGaName":1278,"dataGaLocation":1655},{"text":1779,"config":1780},"Agile development",{"href":1781,"dataGaName":1782,"dataGaLocation":1655},"/solutions/agile-delivery/","agile delivery",{"text":1784,"config":1785},"SCM",{"href":1290,"dataGaName":1786,"dataGaLocation":1655},"source code management",{"text":1734,"config":1788},{"href":1283,"dataGaName":1789,"dataGaLocation":1655},"continuous integration & delivery",{"text":1791,"config":1792},"Value stream management",{"href":1333,"dataGaName":1793,"dataGaLocation":1655},"value stream management",{"text":1739,"config":1795},{"href":1796,"dataGaName":1742,"dataGaLocation":1655},"/solutions/gitops/",{"text":1343,"config":1798},{"href":1346,"dataGaName":1347,"dataGaLocation":1655},{"text":1800,"config":1801},"Small business",{"href":1352,"dataGaName":1353,"dataGaLocation":1655},{"text":1803,"config":1804},"Public sector",{"href":1358,"dataGaName":1359,"dataGaLocation":1655},{"text":1806,"config":1807},"Education",{"href":1808,"dataGaName":1809,"dataGaLocation":1655},"/solutions/education/","education",{"text":1811,"config":1812},"Financial services",{"href":1813,"dataGaName":1814,"dataGaLocation":1655},"/solutions/finance/","financial services",{"title":1366,"links":1816},[1817,1819,1821,1823,1826,1828,1831,1833,1835,1837,1839,1841,1843,1845],{"text":1379,"config":1818},{"href":1381,"dataGaName":1382,"dataGaLocation":1655},{"text":1384,"config":1820},{"href":1386,"dataGaName":1387,"dataGaLocation":1655},{"text":1389,"config":1822},{"href":1391,"dataGaName":1392,"dataGaLocation":1655},{"text":1394,"config":1824},{"href":1396,"dataGaName":1825,"dataGaLocation":1655},"docs",{"text":1417,"config":1827},{"href":1419,"dataGaName":1420,"dataGaLocation":1655},{"text":1829,"config":1830},"What's new",{"href":1477,"dataGaName":1478,"dataGaLocation":1655},{"text":1412,"config":1832},{"href":1414,"dataGaName":1415,"dataGaLocation":1655},{"text":1431,"config":1834},{"href":1433,"dataGaName":1434,"dataGaLocation":1655},{"text":1439,"config":1836},{"href":1441,"dataGaName":1442,"dataGaLocation":1655},{"text":1444,"config":1838},{"href":1446,"dataGaName":1447,"dataGaLocation":1655},{"text":1449,"config":1840},{"href":1451,"dataGaName":1452,"dataGaLocation":1655},{"text":1454,"config":1842},{"href":1456,"dataGaName":1457,"dataGaLocation":1655},{"text":1459,"config":1844},{"href":1461,"dataGaName":1462,"dataGaLocation":1655},{"text":1464,"config":1846},{"href":1466,"dataGaName":1467,"dataGaLocation":1655},{"title":1480,"links":1848},[1849,1851,1853,1855,1857,1859,1863,1868,1870,1872,1874],{"text":1488,"config":1850},{"href":1490,"dataGaName":1482,"dataGaLocation":1655},{"text":1493,"config":1852},{"href":1495,"dataGaName":1496,"dataGaLocation":1655},{"text":1501,"config":1854},{"href":1503,"dataGaName":1504,"dataGaLocation":1655},{"text":1506,"config":1856},{"href":1508,"dataGaName":1509,"dataGaLocation":1655},{"text":1511,"config":1858},{"href":1513,"dataGaName":1514,"dataGaLocation":1655},{"text":1860,"config":1861},"Sustainability",{"href":1862,"dataGaName":1860,"dataGaLocation":1655},"/sustainability/",{"text":1864,"config":1865},"Diversity, inclusion and belonging (DIB)",{"href":1866,"dataGaName":1867,"dataGaLocation":1655},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1516,"config":1869},{"href":1518,"dataGaName":1519,"dataGaLocation":1655},{"text":1526,"config":1871},{"href":1528,"dataGaName":1529,"dataGaLocation":1655},{"text":1531,"config":1873},{"href":1533,"dataGaName":1534,"dataGaLocation":1655},{"text":1875,"config":1876},"Modern Slavery Transparency Statement",{"href":1877,"dataGaName":1878,"dataGaLocation":1655},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1880},[1881,1884,1887],{"text":1882,"config":1883},"Terms",{"href":1707,"dataGaName":1708,"dataGaLocation":1655},{"text":1885,"config":1886},"Cookies",{"dataGaName":1717,"dataGaLocation":1655,"id":1718,"isOneTrustButton":178},{"text":1888,"config":1889},"Privacy",{"href":1712,"dataGaName":1713,"dataGaLocation":1655},[1891,1905],{"id":1892,"title":7,"body":1191,"config":1893,"content":1895,"description":1191,"extension":1899,"meta":1900,"navigation":178,"path":1901,"seo":1902,"stem":1903,"__hash__":1904},"blogAuthors/en-us/blog/authors/noah-ing.yml",{"template":1894},"BlogAuthor",{"name":7,"config":1896},{"headshot":1897,"ctfId":1898},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664410/Blog/Author%20Headshots/noahing.png","noahing","yml",{},"/en-us/blog/authors/noah-ing",{},"en-us/blog/authors/noah-ing","NHgV_yTX8kHX01mE2SSxC6hcOMw3BCWiblr8m6csLkw",{"id":1906,"title":8,"body":1191,"config":1907,"content":1908,"description":1191,"extension":1899,"meta":1912,"navigation":178,"path":1913,"seo":1914,"stem":1915,"__hash__":1916},"blogAuthors/en-us/blog/authors/joe-randazzo.yml",{"template":1894},{"name":8,"config":1909},{"headshot":1910,"ctfId":1911},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664711/Blog/Author%20Headshots/randazzo.jpg","5DxpEbIVcwN2ukwiEMsHlH",{},"/en-us/blog/authors/joe-randazzo",{},"en-us/blog/authors/joe-randazzo","Kg6ZOiBKQgjjlIpsfIAWHsug3iGMaAK1trTdXg4BaFI",[1918,1927,1935],{"title":1919,"description":1920,"heroImage":1921,"category":1188,"date":1922,"authors":1923,"slug":1926,"externalUrl":1191},"Confidential AI for GitLab Self-Hosted","Give developers AI coding agents in GitLab Duo without source code leaving a hardware-encrypted boundary — no GPUs needed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[1924,1925],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1928,"description":1929,"heroImage":1930,"category":1188,"date":1931,"authors":1932,"slug":1934,"externalUrl":1191},"Green DevOps: Why carbon measurement belongs in your CI/CD pipeline","CI/CD pipelines have a hidden carbon cost. Here's why measuring it matters, and how you can get started with Eco CI and Carmen in GitLab.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[1933],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1936,"description":1937,"heroImage":1938,"category":1188,"date":1939,"authors":1940,"slug":1942,"externalUrl":1191},"How to build CI/CD observability at scale","This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[1941],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1944},[1945,1959,1971,1983],{"id":1946,"categories":1947,"header":1949,"text":1950,"button":1951,"image":1956},"ai-modernization",[1948],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1952,"config":1953},"Get your AI maturity score",{"href":1954,"dataGaName":1955,"dataGaLocation":1420},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1957},{"src":1958},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1960,"categories":1961,"header":1963,"text":1950,"button":1964,"image":1968},"devops-modernization",[1962,1755],"product","Are you just managing tools or shipping innovation?",{"text":1965,"config":1966},"Get your DevOps maturity score",{"href":1967,"dataGaName":1955,"dataGaLocation":1420},"/assessments/devops-modernization-assessment/",{"config":1969},{"src":1970},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1972,"categories":1973,"header":1975,"text":1950,"button":1976,"image":1980},"security-modernization",[1974],"security","Are you trading speed for security?",{"text":1977,"config":1978},"Get your security maturity score",{"href":1979,"dataGaName":1955,"dataGaLocation":1420},"/assessments/security-modernization-assessment/",{"config":1981},{"src":1982},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1984,"paths":1985,"header":1988,"text":1989,"button":1990,"image":1995},"github-azure-migration",[1986,1987],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1991,"config":1992},"See how GitLab compares to GitHub",{"href":1993,"dataGaName":1994,"dataGaLocation":1420},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1996},{"src":1970},{"header":1998,"blurb":1999,"button":2000,"secondaryButton":2005},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":2001,"config":2002},"Get your free trial",{"href":2003,"dataGaName":1219,"dataGaLocation":2004},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1693,"config":2006},{"href":1545,"dataGaName":1224,"dataGaLocation":2004},1786803752747]