[{"data":1,"prerenderedAt":2089},["ShallowReactive",2],{"/blog/how-gitlab-duo-agent-platform-transforms-dataops":3,"navigation-en-us":1303,"banner-en-us":1730,"footer-en-us":1741,"blog-post-authors-en-us-Dennis van Rooijen":1986,"blog-related-posts-en-us-how-gitlab-duo-agent-platform-transforms-dataops":2002,"blog-promotions-en-us":2027,"next-steps-en-us":2079},{"id":4,"title":5,"authors":6,"body":8,"category":1286,"date":1287,"description":1288,"extension":1289,"externalUrl":1290,"faq":1290,"featured":995,"heroImage":1291,"meta":1292,"navigation":995,"path":1293,"seo":1294,"slug":1295,"stem":1296,"tags":1297,"template":1301,"updatedDate":1290,"__hash__":1302},"blogPosts/en-us/blog/how-gitlab-duo-agent-platform-transforms-dataops.md","How GitLab Duo Agent Platform transforms DataOps",[7],"Dennis van Rooijen",{"type":9,"value":10,"toc":1275},"minimark",[11,22,27,36,67,89,93,102,237,240,261,265,274,286,290,293,301,304,324,328,331,384,388,391,437,443,448,458,696,701,915,920,1055,1060,1107,1111,1120,1126,1129,1134,1271],[12,13,14,15,21],"p",{},"Creating dbt models manually is a tedious process that can consume hours of a data engineer's time. Especially when no (big) business transformations are made, it is not the most attractive part of an engineer's work with data.\nBut what if you could automate this entire process? In this walkthrough, I'll show you exactly how ",[16,17,20],"a",{"href":18,"rel":19},"https://about.gitlab.com/gitlab-duo-agent-platform/",[],"GitLab Duo Agent Platform"," can generate comprehensive dbt models in just minutes, complete with proper structure, tests, and documentation.",[23,24,26],"h2",{"id":25},"what-were-building","What we're building",[12,28,29,30,35],{},"Our marketing team wants to effectively manage and optimize advertising investments. One of the advertising platforms is Reddit, so, therefore, we are extracting data from the Reddit Ads API to our enterprise ",[16,31,34],{"href":32,"rel":33},"https://handbook.gitlab.com/handbook/enterprise-data/platform/",[],"Data Platform"," Snowflake. At GitLab, we have three layers of storage:",[37,38,39],"ol",{},[40,41,42,46,47,50,51,54,55,60,61,63,64,66],"li",{},[43,44,45],"code",{},"raw"," layer - first landing point for unprocessed data from external sources; not ready for business use 2. ",[43,48,49],{},"prep"," layer - first transformation layer with source models; still not ready for general business use 3. ",[43,52,53],{},"prod"," layer - final transformed data ready for business use and Tableau reporting\n",[56,57],"img",{"alt":58,"src":59},"Chart of storage layers","https://res.cloudinary.com/about-gitlab-com/image/upload/v1758030995/zo7vespktzfdtdtiauz7.png","\nFor this walkthrough, data has already landed in the raw layer by our extraction solution Fivetran, and we'll generate dbt models that handle the data through the ",[43,62,49],{}," layer to the ",[43,65,53],{}," layer.\nWithout having to write a single line of dbt code ourselves, by the end of the walkthrough we will have:",[68,69,70],"ul",{},[40,71,72,76,77,80,81,84,85,88],{},[73,74,75],"strong",{},"Source models"," in the prep layer - ",[73,78,79],{},"Workspace models"," in the prod layer - ",[73,82,83],{},"Complete dbt configurations"," for all 13 tables (which includes 112 columns) in the Reddit Ads dataset - ",[73,86,87],{},"Test queries"," to validate the outcomes\nThe entire process will take less than 10 minutes, compared to the hours it would typically require manually. Here are the steps to follow:",[23,90,92],{"id":91},"_1-prepare-the-data-structure","1. Prepare the data structure",[12,94,95,96,101],{},"Before GitLab Duo can generate our models, it needs to understand the complete table structure. The key is running a query against Snowflake's information schema, because we are currently investigating how to connect GitLab Duo via Model Context Protocol (",[16,97,100],{"href":98,"rel":99},"https://about.gitlab.com/topics/ai/model-context-protocol/",[],"MCP",") to our Snowflake instance:",[103,104,109],"pre",{"className":105,"code":106,"language":107,"meta":108,"style":108},"language-sql shiki shiki-themes github-light","SELECT \n    table_name,\n    column_name,\n    data_type,\n    is_nullable,\n    CASE \n        WHEN is_nullable = 'NO' THEN 'PRIMARY_KEY'\n        ELSE NULL \n    END as key_type\nFROM raw.information_schema.columns WHERE table_schema = 'REDDIT_ADS' ORDER BY table_name, ordinal_position;\n","sql","",[43,110,111,124,130,136,142,148,156,178,189,201],{"__ignoreMap":108},[112,113,116,120],"span",{"class":114,"line":115},"line",1,[112,117,119],{"class":118},"sD7c4","SELECT",[112,121,123],{"class":122},"sgsFI"," \n",[112,125,127],{"class":114,"line":126},2,[112,128,129],{"class":122},"    table_name,\n",[112,131,133],{"class":114,"line":132},3,[112,134,135],{"class":122},"    column_name,\n",[112,137,139],{"class":114,"line":138},4,[112,140,141],{"class":122},"    data_type,\n",[112,143,145],{"class":114,"line":144},5,[112,146,147],{"class":122},"    is_nullable,\n",[112,149,151,154],{"class":114,"line":150},6,[112,152,153],{"class":118},"    CASE",[112,155,123],{"class":122},[112,157,159,162,165,168,172,175],{"class":114,"line":158},7,[112,160,161],{"class":118},"        WHEN",[112,163,164],{"class":122}," is_nullable ",[112,166,167],{"class":118},"=",[112,169,171],{"class":170},"sYBdl"," 'NO'",[112,173,174],{"class":118}," THEN",[112,176,177],{"class":170}," 'PRIMARY_KEY'\n",[112,179,181,184,187],{"class":114,"line":180},8,[112,182,183],{"class":118},"        ELSE",[112,185,186],{"class":118}," NULL",[112,188,123],{"class":122},[112,190,192,195,198],{"class":114,"line":191},9,[112,193,194],{"class":118},"    END",[112,196,197],{"class":118}," as",[112,199,200],{"class":122}," key_type\n",[112,202,204,207,211,214,217,220,223,226,228,231,234],{"class":114,"line":203},10,[112,205,206],{"class":118},"FROM",[112,208,210],{"class":209},"sYu0t"," raw",[112,212,213],{"class":122},".",[112,215,216],{"class":209},"information_schema",[112,218,219],{"class":122},".columns ",[112,221,222],{"class":118},"WHERE",[112,224,225],{"class":122}," table_schema ",[112,227,167],{"class":118},[112,229,230],{"class":170}," 'REDDIT_ADS'",[112,232,233],{"class":118}," ORDER BY",[112,235,236],{"class":122}," table_name, ordinal_position;\n",[12,238,239],{},"This query captures:",[68,241,242],{},[40,243,244,245,248,249,252,253,256,257,260],{},"All table and column names - Data types for proper model structure - Nullable constraints - Primary key identification (non-nullable columns in this dataset)\n",[73,246,247],{},"Pro tip:"," In the Reddit Ads dataset, all non-nullable columns serve as primary keys — a pattern. I validated by checking tables like ",[43,250,251],{},"ad_group",", which has two non-nullable columns (",[43,254,255],{},"account_id"," and ",[43,258,259],{},"id",") that are both marked as primary keys. Running this query returned 112 rows of metadata that I exported as a CSV file for model generation. While this manual step works well today, we're investigating a direct GitLab Duo integration with our Data Platform via MCP to automate this process entirely.",[23,262,264],{"id":263},"_2-set-up-gitlab-duo","2. Set up GitLab Duo",[12,266,267,268,273],{},"There are two ways to interact with ",[16,269,272],{"href":270,"rel":271},"https://docs.gitlab.com/user/get_started/getting_started_gitlab_duo/",[],"GitLab Duo",":",[37,275,276],{},[40,277,278,281,282,285],{},[73,279,280],{},"Web UI chat function"," 2. ",[73,283,284],{},"Visual Studio Code plugin","\nI chose the VS Code plugin because I can run the dbt models locally to test them.",[23,287,289],{"id":288},"_3-enter-the-magic-prompt","3. Enter the 'magic' prompt",[12,291,292],{},"Here's the exact prompt I used to generate all the dbt code:",[103,294,299],{"className":295,"code":297,"language":298,"meta":108},[296],"language-text","Create dbt models for all the tables in the file structure.csv.\nI want to have the source models created, with a filter that dedupes the data based on the primary key. Create these in a new folder reddit_ads. I want to have workspace models created and store these in the workspace_marketing schema.\nTake this MR as example: [I've referenced to previous source implementation]. Here is the same done for Source A, but now it needs to be done for Reddit Ads. \nPlease check the dbt style guide when creating the code: https://handbook.gitlab.com/handbook/enterprise-data/platform/dbt-guide/\n","text",[43,300,297],{"__ignoreMap":108},[12,302,303],{},"Key elements that made this prompt effective:",[68,305,306],{},[40,307,308,311,312,315,316,319,320,323],{},[73,309,310],{},"Clear specifications"," for both source and workspace models. - ",[73,313,314],{},"Reference example"," from a previous similar merge request. - ",[73,317,318],{},"Style guide reference"," to ensure code quality and consistency. - ",[73,321,322],{},"Specific schema targeting"," for proper organization.",[23,325,327],{"id":326},"_4-gitlab-duos-process","4. GitLab Duo's process",[12,329,330],{},"After submitting the prompt, GitLab Duo got to work. The entire generation process took a few minutes, during which GitLab Duo:",[37,332,333],{},[40,334,335,338,339,342,343,346,347,350,351,354,355,358,359,362,363],{},[73,336,337],{},"Read and analyzed"," the CSV input file. 2. ",[73,340,341],{},"Examined table structures"," from the metadata. 3. ",[73,344,345],{},"Referenced our dbt style guide"," for coding standards. 4. ",[73,348,349],{},"Took similar merge request into account"," to properly structure. 5. ",[73,352,353],{},"Generated source models"," for all 13 tables. 6. ",[73,356,357],{},"Created workspace models"," for all 13 tables. 7. ",[73,360,361],{},"Generated supporting dbt files",":\n",[68,364,365,371,377],{},[40,366,367,370],{},[43,368,369],{},"sources.yml"," configuration.",[40,372,373,376],{},[43,374,375],{},"schema.yml"," files with tests and documentation.",[40,378,379,380,383],{},"Updated ",[43,381,382],{},"dbt_project.yml"," with schema references.",[23,385,387],{"id":386},"the-results","The results",[12,389,390],{},"The output was remarkable:",[68,392,393,415],{},[40,394,395,398,399,402],{},[73,396,397],{},"1 modified file:"," dbt_project.yml (added reddit_ads schema configuration) - ",[73,400,401],{},"29 new files:",[68,403,404,410],{},[40,405,406,409],{},[73,407,408],{},"26 dbt models"," (13 source + 13 workspace)",[40,411,412],{},[73,413,414],{},"3 YAML files",[40,416,417,420,421,424,425,428,429,432,433,436],{},[73,418,419],{},"Nearly 900 lines of code"," generated automatically - ",[73,422,423],{},"Built-in data tests,"," including unique constraints on primary key columns - ",[73,426,427],{},"Generic descriptions"," for all models and columns - ",[73,430,431],{},"Proper deduplication logic"," in source models - ",[73,434,435],{},"Clean, consistent code structure"," following the GitLab dbt style guide",[103,438,441],{"className":439,"code":440,"language":298,"meta":108},[296],"transform/snowflake-dbt/ ├── dbt_project.yml                                                    [MODIFIED] └── models/\n    ├── sources/\n    │   └── reddit_ads/\n    │       ├── reddit_ads_ad_group_source.sql                        [NEW]\n    │       ├── reddit_ads_ad_source.sql                              [NEW]\n    │       ├── reddit_ads_business_account_source.sql                [NEW]\n    │       ├── reddit_ads_campaign_source.sql                        [NEW]\n    │       ├── reddit_ads_custom_audience_history_source.sql         [NEW]\n    │       ├── reddit_ads_geolocation_source.sql                     [NEW]\n    │       ├── reddit_ads_interest_source.sql                        [NEW]\n    │       ├── reddit_ads_targeting_community_source.sql             [NEW]\n    │       ├── reddit_ads_targeting_custom_audience_source.sql       [NEW]\n    │       ├── reddit_ads_targeting_device_source.sql                [NEW]\n    │       ├── reddit_ads_targeting_geolocation_source.sql           [NEW]\n    │       ├── reddit_ads_targeting_interest_source.sql              [NEW]\n    │       ├── reddit_ads_time_zone_source.sql                       [NEW]\n    │       ├── schema.yml                                            [NEW]\n    │       └── sources.yml                                           [NEW]\n    └── workspaces/\n        └── workspace_marketing/\n            └── reddit_ads/\n                ├── schema.yml                                        [NEW]\n                ├── wk_reddit_ads_ad.sql                              [NEW]\n                ├── wk_reddit_ads_ad_group.sql                        [NEW]\n                ├── wk_reddit_ads_business_account.sql                [NEW]\n                ├── wk_reddit_ads_campaign.sql                        [NEW]\n                ├── wk_reddit_ads_custom_audience_history.sql         [NEW]\n                ├── wk_reddit_ads_geolocation.sql                     [NEW]\n                ├── wk_reddit_ads_interest.sql                        [NEW]\n                ├── wk_reddit_ads_targeting_community.sql             [NEW]\n                ├── wk_reddit_ads_targeting_custom_audience.sql       [NEW]\n                ├── wk_reddit_ads_targeting_device.sql                [NEW]\n                ├── wk_reddit_ads_targeting_geolocation.sql           [NEW]\n                ├── wk_reddit_ads_targeting_interest.sql              [NEW]\n                └── wk_reddit_ads_time_zone.sql                       [NEW]\n\n",[43,442,440],{"__ignoreMap":108},[444,445,447],"h3",{"id":446},"sample-generated-code","Sample generated code",[12,449,450,451,454,455],{},"Here's an example of the generated code quality. For the ",[43,452,453],{},"time_zone"," table, GitLab Duo created:\n",[73,456,457],{},"Prep Layer Source Model",[103,459,461],{"className":105,"code":460,"language":107,"meta":108,"style":108},"WITH source AS (\n  SELECT *\n  FROM {{ source('reddit_ads','time_zone') }}\n  QUALIFY ROW_NUMBER() OVER (PARTITION BY id ORDER BY _fivetran_synced DESC) = 1\n),\nrenamed AS (\n  SELECT\n    id::VARCHAR                               AS time_zone_id,\n    code::VARCHAR                             AS time_zone_code,\n    dst_offset::NUMBER                        AS time_zone_dst_offset,\n    is_dst_active::BOOLEAN                    AS is_time_zone_dst_active,\n    name::VARCHAR                             AS time_zone_name,\n    offset::NUMBER                            AS time_zone_offset,\n    _fivetran_synced::TIMESTAMP               AS fivetran_synced_at\n  FROM source\n)\nSELECT * FROM renamed\n",[43,462,463,477,485,505,548,553,562,567,581,594,608,623,639,653,668,676,682],{"__ignoreMap":108},[112,464,465,468,471,474],{"class":114,"line":115},[112,466,467],{"class":118},"WITH",[112,469,470],{"class":122}," source ",[112,472,473],{"class":118},"AS",[112,475,476],{"class":122}," (\n",[112,478,479,482],{"class":114,"line":126},[112,480,481],{"class":118},"  SELECT",[112,483,484],{"class":118}," *\n",[112,486,487,490,493,496,499,502],{"class":114,"line":132},[112,488,489],{"class":118},"  FROM",[112,491,492],{"class":122}," {{ source(",[112,494,495],{"class":170},"'reddit_ads'",[112,497,498],{"class":122},",",[112,500,501],{"class":170},"'time_zone'",[112,503,504],{"class":122},") }}\n",[112,506,507,510,513,516,519,522,525,528,531,534,537,540,543,545],{"class":114,"line":138},[112,508,509],{"class":122},"  QUALIFY ",[112,511,512],{"class":209},"ROW_NUMBER",[112,514,515],{"class":122},"() ",[112,517,518],{"class":118},"OVER",[112,520,521],{"class":122}," (",[112,523,524],{"class":118},"PARTITION",[112,526,527],{"class":118}," BY",[112,529,530],{"class":122}," id ",[112,532,533],{"class":118},"ORDER BY",[112,535,536],{"class":122}," _fivetran_synced ",[112,538,539],{"class":118},"DESC",[112,541,542],{"class":122},") ",[112,544,167],{"class":118},[112,546,547],{"class":209}," 1\n",[112,549,550],{"class":114,"line":144},[112,551,552],{"class":122},"),\n",[112,554,555,558,560],{"class":114,"line":150},[112,556,557],{"class":122},"renamed ",[112,559,473],{"class":118},[112,561,476],{"class":122},[112,563,564],{"class":114,"line":158},[112,565,566],{"class":118},"  SELECT\n",[112,568,569,572,575,578],{"class":114,"line":180},[112,570,571],{"class":122},"    id::",[112,573,574],{"class":118},"VARCHAR",[112,576,577],{"class":118},"                               AS",[112,579,580],{"class":122}," time_zone_id,\n",[112,582,583,586,588,591],{"class":114,"line":191},[112,584,585],{"class":122},"    code::",[112,587,574],{"class":118},[112,589,590],{"class":118},"                             AS",[112,592,593],{"class":122}," time_zone_code,\n",[112,595,596,599,602,605],{"class":114,"line":203},[112,597,598],{"class":122},"    dst_offset::",[112,600,601],{"class":118},"NUMBER",[112,603,604],{"class":118},"                        AS",[112,606,607],{"class":122}," time_zone_dst_offset,\n",[112,609,611,614,617,620],{"class":114,"line":610},11,[112,612,613],{"class":122},"    is_dst_active::",[112,615,616],{"class":118},"BOOLEAN",[112,618,619],{"class":118},"                    AS",[112,621,622],{"class":122}," is_time_zone_dst_active,\n",[112,624,626,629,632,634,636],{"class":114,"line":625},12,[112,627,628],{"class":118},"    name",[112,630,631],{"class":122},"::",[112,633,574],{"class":118},[112,635,590],{"class":118},[112,637,638],{"class":122}," time_zone_name,\n",[112,640,642,645,647,650],{"class":114,"line":641},13,[112,643,644],{"class":122},"    offset::",[112,646,601],{"class":118},[112,648,649],{"class":118},"                            AS",[112,651,652],{"class":122}," time_zone_offset,\n",[112,654,656,659,662,665],{"class":114,"line":655},14,[112,657,658],{"class":122},"    _fivetran_synced::",[112,660,661],{"class":118},"TIMESTAMP",[112,663,664],{"class":118},"               AS",[112,666,667],{"class":122}," fivetran_synced_at\n",[112,669,671,673],{"class":114,"line":670},15,[112,672,489],{"class":118},[112,674,675],{"class":122}," source\n",[112,677,679],{"class":114,"line":678},16,[112,680,681],{"class":122},")\n",[112,683,685,687,690,693],{"class":114,"line":684},17,[112,686,119],{"class":118},[112,688,689],{"class":118}," *",[112,691,692],{"class":118}," FROM",[112,694,695],{"class":122}," renamed\n",[12,697,698],{},[73,699,700],{},"Schema.yml",[103,702,706],{"className":703,"code":704,"language":705,"meta":108,"style":108},"language-yaml shiki shiki-themes github-light","models:\n  - name: reddit_ads_time_zone_source\n    description: Time zone data from Reddit Ads system\n    columns:\n      - name: time_zone_id\n        description: Unique identifier for time zone records\n        data_tests:\n          - unique\n          - not_null\n      - name: time_zone_code\n        description: Code for the time zone\n      - name: time_zone_dst_offset\n        description: Daylight saving time offset for the time zone\n      - name: is_time_zone_dst_active\n        description: Flag indicating if daylight saving time is active\n      - name: time_zone_name\n        description: Name of the time zone\n      - name: time_zone_offset\n        description: Offset for the time zone\n      - name: fivetran_synced_at\n        description: Timestamp when the record was last synced by Fivetran\n\n","yaml",[43,707,708,716,730,740,747,759,769,776,784,791,802,811,822,831,842,851,862,871,883,893,905],{"__ignoreMap":108},[112,709,710,714],{"class":114,"line":115},[112,711,713],{"class":712},"shJU0","models",[112,715,362],{"class":122},[112,717,718,721,724,727],{"class":114,"line":126},[112,719,720],{"class":122},"  - ",[112,722,723],{"class":712},"name",[112,725,726],{"class":122},": ",[112,728,729],{"class":170},"reddit_ads_time_zone_source\n",[112,731,732,735,737],{"class":114,"line":132},[112,733,734],{"class":712},"    description",[112,736,726],{"class":122},[112,738,739],{"class":170},"Time zone data from Reddit Ads system\n",[112,741,742,745],{"class":114,"line":138},[112,743,744],{"class":712},"    columns",[112,746,362],{"class":122},[112,748,749,752,754,756],{"class":114,"line":144},[112,750,751],{"class":122},"      - ",[112,753,723],{"class":712},[112,755,726],{"class":122},[112,757,758],{"class":170},"time_zone_id\n",[112,760,761,764,766],{"class":114,"line":150},[112,762,763],{"class":712},"        description",[112,765,726],{"class":122},[112,767,768],{"class":170},"Unique identifier for time zone records\n",[112,770,771,774],{"class":114,"line":158},[112,772,773],{"class":712},"        data_tests",[112,775,362],{"class":122},[112,777,778,781],{"class":114,"line":180},[112,779,780],{"class":122},"          - ",[112,782,783],{"class":170},"unique\n",[112,785,786,788],{"class":114,"line":191},[112,787,780],{"class":122},[112,789,790],{"class":170},"not_null\n",[112,792,793,795,797,799],{"class":114,"line":203},[112,794,751],{"class":122},[112,796,723],{"class":712},[112,798,726],{"class":122},[112,800,801],{"class":170},"time_zone_code\n",[112,803,804,806,808],{"class":114,"line":610},[112,805,763],{"class":712},[112,807,726],{"class":122},[112,809,810],{"class":170},"Code for the time zone\n",[112,812,813,815,817,819],{"class":114,"line":625},[112,814,751],{"class":122},[112,816,723],{"class":712},[112,818,726],{"class":122},[112,820,821],{"class":170},"time_zone_dst_offset\n",[112,823,824,826,828],{"class":114,"line":641},[112,825,763],{"class":712},[112,827,726],{"class":122},[112,829,830],{"class":170},"Daylight saving time offset for the time zone\n",[112,832,833,835,837,839],{"class":114,"line":655},[112,834,751],{"class":122},[112,836,723],{"class":712},[112,838,726],{"class":122},[112,840,841],{"class":170},"is_time_zone_dst_active\n",[112,843,844,846,848],{"class":114,"line":670},[112,845,763],{"class":712},[112,847,726],{"class":122},[112,849,850],{"class":170},"Flag indicating if daylight saving time is active\n",[112,852,853,855,857,859],{"class":114,"line":678},[112,854,751],{"class":122},[112,856,723],{"class":712},[112,858,726],{"class":122},[112,860,861],{"class":170},"time_zone_name\n",[112,863,864,866,868],{"class":114,"line":684},[112,865,763],{"class":712},[112,867,726],{"class":122},[112,869,870],{"class":170},"Name of the time zone\n",[112,872,874,876,878,880],{"class":114,"line":873},18,[112,875,751],{"class":122},[112,877,723],{"class":712},[112,879,726],{"class":122},[112,881,882],{"class":170},"time_zone_offset\n",[112,884,886,888,890],{"class":114,"line":885},19,[112,887,763],{"class":712},[112,889,726],{"class":122},[112,891,892],{"class":170},"Offset for the time zone\n",[112,894,896,898,900,902],{"class":114,"line":895},20,[112,897,751],{"class":122},[112,899,723],{"class":712},[112,901,726],{"class":122},[112,903,904],{"class":170},"fivetran_synced_at\n",[112,906,908,910,912],{"class":114,"line":907},21,[112,909,763],{"class":712},[112,911,726],{"class":122},[112,913,914],{"class":170},"Timestamp when the record was last synced by Fivetran\n",[12,916,917],{},[73,918,919],{},"Source.yml",[103,921,923],{"className":703,"code":922,"language":705,"meta":108,"style":108},"sources:\n  - name: reddit_ads\n    database: RAW\n    schema: reddit_ads\n    loaded_at_field: _fivetran_synced\n    loader: fivetran\n    description: Reddit Ads data\n\n    quoting:\n      database: true\n      schema: false\n      identifier: false\n\n    tables:\n      - name: time_zone\n\n",[43,924,925,932,943,953,962,972,982,991,997,1004,1014,1024,1033,1037,1044],{"__ignoreMap":108},[112,926,927,930],{"class":114,"line":115},[112,928,929],{"class":712},"sources",[112,931,362],{"class":122},[112,933,934,936,938,940],{"class":114,"line":126},[112,935,720],{"class":122},[112,937,723],{"class":712},[112,939,726],{"class":122},[112,941,942],{"class":170},"reddit_ads\n",[112,944,945,948,950],{"class":114,"line":132},[112,946,947],{"class":712},"    database",[112,949,726],{"class":122},[112,951,952],{"class":170},"RAW\n",[112,954,955,958,960],{"class":114,"line":138},[112,956,957],{"class":712},"    schema",[112,959,726],{"class":122},[112,961,942],{"class":170},[112,963,964,967,969],{"class":114,"line":144},[112,965,966],{"class":712},"    loaded_at_field",[112,968,726],{"class":122},[112,970,971],{"class":170},"_fivetran_synced\n",[112,973,974,977,979],{"class":114,"line":150},[112,975,976],{"class":712},"    loader",[112,978,726],{"class":122},[112,980,981],{"class":170},"fivetran\n",[112,983,984,986,988],{"class":114,"line":158},[112,985,734],{"class":712},[112,987,726],{"class":122},[112,989,990],{"class":170},"Reddit Ads data\n",[112,992,993],{"class":114,"line":180},[112,994,996],{"emptyLinePlaceholder":995},true,"\n",[112,998,999,1002],{"class":114,"line":191},[112,1000,1001],{"class":712},"    quoting",[112,1003,362],{"class":122},[112,1005,1006,1009,1011],{"class":114,"line":203},[112,1007,1008],{"class":712},"      database",[112,1010,726],{"class":122},[112,1012,1013],{"class":209},"true\n",[112,1015,1016,1019,1021],{"class":114,"line":610},[112,1017,1018],{"class":712},"      schema",[112,1020,726],{"class":122},[112,1022,1023],{"class":209},"false\n",[112,1025,1026,1029,1031],{"class":114,"line":625},[112,1027,1028],{"class":712},"      identifier",[112,1030,726],{"class":122},[112,1032,1023],{"class":209},[112,1034,1035],{"class":114,"line":641},[112,1036,996],{"emptyLinePlaceholder":995},[112,1038,1039,1042],{"class":114,"line":655},[112,1040,1041],{"class":712},"    tables",[112,1043,362],{"class":122},[112,1045,1046,1048,1050,1052],{"class":114,"line":670},[112,1047,751],{"class":122},[112,1049,723],{"class":712},[112,1051,726],{"class":122},[112,1053,1054],{"class":170},"time_zone\n",[12,1056,1057],{},[73,1058,1059],{},"Workspace Model",[103,1061,1063],{"className":105,"code":1062,"language":107,"meta":108,"style":108},"WITH source AS (\n  SELECT *\n  FROM {{ ref('reddit_ads_time_zone_source') }}\n)\nSELECT * FROM source\n",[43,1064,1065,1075,1081,1093,1097],{"__ignoreMap":108},[112,1066,1067,1069,1071,1073],{"class":114,"line":115},[112,1068,467],{"class":118},[112,1070,470],{"class":122},[112,1072,473],{"class":118},[112,1074,476],{"class":122},[112,1076,1077,1079],{"class":114,"line":126},[112,1078,481],{"class":118},[112,1080,484],{"class":118},[112,1082,1083,1085,1088,1091],{"class":114,"line":132},[112,1084,489],{"class":118},[112,1086,1087],{"class":122}," {{ ref(",[112,1089,1090],{"class":170},"'reddit_ads_time_zone_source'",[112,1092,504],{"class":122},[112,1094,1095],{"class":114,"line":138},[112,1096,681],{"class":122},[112,1098,1099,1101,1103,1105],{"class":114,"line":144},[112,1100,119],{"class":118},[112,1102,689],{"class":118},[112,1104,692],{"class":118},[112,1106,675],{"class":122},[23,1108,1110],{"id":1109},"_5-quality-validation","5. Quality validation",[12,1112,1113,1114,1119],{},"Now that the code looks good, I pushed it to the MR and executed ",[16,1115,1118],{"href":1116,"rel":1117},"https://handbook.gitlab.com/handbook/enterprise-data/platform/ci-jobs/#build_changes",[],"CI test pipeline"," to test the code and validate the outcome. I asked GitLab Duo to create a validation query:",[103,1121,1124],{"className":1122,"code":1123,"language":298,"meta":108},[296],"Create a test query to test the row counts between the raw layer and the workspace layer. Keep in mind that we do deduplication, so we can compare both using distinct on the primary keys.\n",[43,1125,1123],{"__ignoreMap":108},[12,1127,1128],{},"The AI generated a comprehensive validation query that:",[68,1130,1131],{},[40,1132,1133],{},"Compared row counts between raw and workspace layers. - Accounted for deduplication logic. - Tested all 13 tables. - Calculated data retention percentages.",[1135,1136,1137,1138,1142,1143,1146,1149,1151,1154,1156,1159,1161,1164,1166,1169,1171,1174,1177,1180,1182,1185,1187,1190,1192,1195,1197,1200,1202,1205,1207,1210,1212,1215,1217,1220,1222,1225,1227,1230,1232,1235,1237,1240,1242,1245,1247,1250,1252,1255,1257,1260,1262,1265],"details",{}," ",[1139,1140,1141],"summary",{},"Generated SQL Test query","\n```sql\n-- Reddit Ads Row Count Validation Test -- Compares distinct counts between RAW layer and WORKSPACE_MARKETING layer -- Accounts for deduplication logic in source models\nWITH raw_counts AS (\n  -- Single primary key tables\n  SELECT 'ad' AS table_name, COUNT(DISTINCT id) AS raw_count\n  FROM RAW.REDDIT_ADS.AD\n",[12,1144,1145],{},"UNION ALL",[12,1147,1148],{},"SELECT 'business_account' AS table_name, COUNT(DISTINCT id) AS raw_count\nFROM RAW.REDDIT_ADS.BUSINESS_ACCOUNT",[12,1150,1145],{},[12,1152,1153],{},"SELECT 'campaign' AS table_name, COUNT(DISTINCT id) AS raw_count\nFROM RAW.REDDIT_ADS.CAMPAIGN",[12,1155,1145],{},[12,1157,1158],{},"SELECT 'custom_audience_history' AS table_name, COUNT(DISTINCT id) AS raw_count\nFROM RAW.REDDIT_ADS.CUSTOM_AUDIENCE_HISTORY",[12,1160,1145],{},[12,1162,1163],{},"SELECT 'geolocation' AS table_name, COUNT(DISTINCT id) AS raw_count\nFROM RAW.REDDIT_ADS.GEOLOCATION",[12,1165,1145],{},[12,1167,1168],{},"SELECT 'interest' AS table_name, COUNT(DISTINCT id) AS raw_count\nFROM RAW.REDDIT_ADS.INTEREST",[12,1170,1145],{},[12,1172,1173],{},"SELECT 'time_zone' AS table_name, COUNT(DISTINCT id) AS raw_count\nFROM RAW.REDDIT_ADS.TIME_ZONE",[12,1175,1176],{},"-- Composite primary key tables\nUNION ALL",[12,1178,1179],{},"SELECT 'ad_group' AS table_name, COUNT(DISTINCT CONCAT(account_id, '|', id)) AS raw_count\nFROM RAW.REDDIT_ADS.AD_GROUP",[12,1181,1145],{},[12,1183,1184],{},"SELECT 'targeting_community' AS table_name, COUNT(DISTINCT CONCAT(ad_group_id, '|', community_id)) AS raw_count\nFROM RAW.REDDIT_ADS.TARGETING_COMMUNITY",[12,1186,1145],{},[12,1188,1189],{},"SELECT 'targeting_custom_audience' AS table_name, COUNT(DISTINCT CONCAT(ad_group_id, '|', custom_audience_id)) AS raw_count\nFROM RAW.REDDIT_ADS.TARGETING_CUSTOM_AUDIENCE",[12,1191,1145],{},[12,1193,1194],{},"SELECT 'targeting_device' AS table_name, COUNT(DISTINCT _fivetran_id) AS raw_count\nFROM RAW.REDDIT_ADS.TARGETING_DEVICE",[12,1196,1145],{},[12,1198,1199],{},"SELECT 'targeting_geolocation' AS table_name, COUNT(DISTINCT CONCAT(ad_group_id, '|', geolocation_id)) AS raw_count\nFROM RAW.REDDIT_ADS.TARGETING_GEOLOCATION",[12,1201,1145],{},[12,1203,1204],{},"SELECT 'targeting_interest' AS table_name, COUNT(DISTINCT CONCAT(ad_group_id, '|', interest_id)) AS raw_count\nFROM RAW.REDDIT_ADS.TARGETING_INTEREST\n),\nworkspace_counts AS (\n-- Workspace layer counts using primary keys from schema.yml\nSELECT 'ad' AS table_name, COUNT(DISTINCT ad_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_AD",[12,1206,1145],{},[12,1208,1209],{},"SELECT 'business_account' AS table_name, COUNT(DISTINCT business_account_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_BUSINESS_ACCOUNT",[12,1211,1145],{},[12,1213,1214],{},"SELECT 'campaign' AS table_name, COUNT(DISTINCT campaign_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_CAMPAIGN",[12,1216,1145],{},[12,1218,1219],{},"SELECT 'custom_audience_history' AS table_name, COUNT(DISTINCT custom_audience_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_CUSTOM_AUDIENCE_HISTORY",[12,1221,1145],{},[12,1223,1224],{},"SELECT 'geolocation' AS table_name, COUNT(DISTINCT geolocation_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_GEOLOCATION",[12,1226,1145],{},[12,1228,1229],{},"SELECT 'interest' AS table_name, COUNT(DISTINCT interest_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_INTEREST",[12,1231,1145],{},[12,1233,1234],{},"SELECT 'time_zone' AS table_name, COUNT(DISTINCT time_zone_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_TIME_ZONE",[12,1236,1176],{},[12,1238,1239],{},"SELECT 'ad_group' AS table_name, COUNT(DISTINCT CONCAT(ad_group_account_id, '|', ad_group_id)) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_AD_GROUP",[12,1241,1145],{},[12,1243,1244],{},"SELECT 'targeting_community' AS table_name, COUNT(DISTINCT CONCAT(targeting_community_ad_group_id, '|', targeting_community_id)) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_TARGETING_COMMUNITY",[12,1246,1145],{},[12,1248,1249],{},"SELECT 'targeting_custom_audience' AS table_name, COUNT(DISTINCT CONCAT(targeting_custom_audience_ad_group_id, '|', targeting_custom_audience_id)) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_TARGETING_CUSTOM_AUDIENCE",[12,1251,1145],{},[12,1253,1254],{},"SELECT 'targeting_device' AS table_name, COUNT(DISTINCT targeting_device_fivetran_id) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_TARGETING_DEVICE",[12,1256,1145],{},[12,1258,1259],{},"SELECT 'targeting_geolocation' AS table_name, COUNT(DISTINCT CONCAT(targeting_geolocation_ad_group_id, '|', targeting_geolocation_id)) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_TARGETING_GEOLOCATION",[12,1261,1145],{},[12,1263,1264],{},"SELECT 'targeting_interest' AS table_name, COUNT(DISTINCT CONCAT(targeting_interest_ad_group_id, '|', targeting_interest_id)) AS workspace_count\nFROM REDDIT_DBT_MODEL_GENERATION_PROD.WORKSPACE_MARKETING.WK_REDDIT_ADS_TARGETING_INTEREST\n)\n-- Final comparison with validation results SELECT\nr.table_name,\nr.raw_count,\nw.workspace_count,\nr.raw_count - w.workspace_count AS count_difference,\nCASE\nWHEN r.raw_count = w.workspace_count THEN '✅ PASS'\nWHEN r.raw_count > w.workspace_count THEN '⚠️ RAW > WORKSPACE (Expected due to deduplication)'\nELSE '❌ FAIL - WORKSPACE > RAW (Unexpected)'\nEND AS validation_status,\nROUND((w.workspace_count::FLOAT / r.raw_count::FLOAT) * 100, 2) AS data_retention_percentage\nFROM raw_counts r JOIN workspace_counts w ON r.table_name = w.table_name ORDER BY r.table_name;",[103,1266,1269],{"className":1267,"code":1268,"language":298},[296],"\u003C/details>\n![query results table](https://res.cloudinary.com/about-gitlab-com/image/upload/v1758030995/guicjhzwvrz3czwjs3xo.png)\nRunning this query showed:\n- **Zero differences** in row counts after deduplication - **100% data retention** across all tables - **All tests passed** successfully\n## The bottom line: Massive time savings\n- **Traditional approach:** 6-8 hours of manual coding, testing, and debugging\n- **GitLab Duo approach:** 6-8 minutes of generation + review time\nThis represents a 60x improvement in developer efficiency (from 6-8 hours to 6-8 minutes), while maintaining high code quality.\n## Best practices for success\nBased on this experience, here are key recommendations:\n### Prepare your metadata\n- Extract complete table structures including data types and constraints. - Identify primary keys and relationships upfront. - Export clean, well-formatted CSV input files.\n**Note:** By connecting GitLab Duo via MCP to your (meta)data, you could exclude this manual step.\n### Provide clear context\n- Reference existing example MRs when possible. - Specify your coding standards and style guides. - Be explicit about folder structure and naming conventions.\n### Validate thoroughly\n- Always create validation queries for data integrity. - Test locally before merging. - Run your CI/CD pipeline to catch any issues.\n### Leverage AI for follow-up tasks\n- Generate test queries automatically. - Create documentation templates. - Build validation scripts.\n## What's next\nThis demonstration shows how AI-powered development tools like GitLab Duo are also transforming data engineering workflows. The ability to generate hundreds of lines of production-ready code in minutes —  complete with tests, documentation, and proper structure — represents a fundamental shift in how we approach repetitive development tasks.\nBy leveraging AI to handle the repetitive aspects of dbt model creation, data engineers can focus on higher-value activities like data modeling strategy, performance optimization, and business logic implementation.\n**Ready to try this yourself?** Start with a small dataset, prepare your metadata carefully, and watch as GitLab Duo transforms hours of work into minutes of automated generation.\n> [Trial GitLab Duo Agent Platform today.](https://about.gitlab.com/gitlab-duo-agent-platform/)\n## Read more\n- [GitLab 18.3: Expanding AI orchestration in software engineering](https://about.gitlab.com/blog/gitlab-18-3-expanding-ai-orchestration-in-software-engineering/) - [GitLab Duo Agent Platform Public Beta: Next-gen AI orchestration and more](https://about.gitlab.com/blog/gitlab-duo-agent-platform-public-beta/)\n",[43,1270,1268],{"__ignoreMap":108},[1272,1273,1274],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}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 pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}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 .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}",{"title":108,"searchDepth":126,"depth":126,"links":1276},[1277,1278,1279,1280,1281,1282,1285],{"id":25,"depth":126,"text":26},{"id":91,"depth":126,"text":92},{"id":263,"depth":126,"text":264},{"id":288,"depth":126,"text":289},{"id":326,"depth":126,"text":327},{"id":386,"depth":126,"text":387,"children":1283},[1284],{"id":446,"depth":132,"text":447},{"id":1109,"depth":126,"text":1110},"ai","2025-09-16","Explore how to turn hours of manual coding into minutes of automated generation with this comprehensive dbt model creation walkthrough.","md",null,"blog/hero%20images/workflow_1800x945.png",{},"/en-us/blog/how-gitlab-duo-agent-platform-transforms-dataops",{"title":5,"description":1288},"how-gitlab-duo-agent-platform-transforms-dataops","en-us/blog/how-gitlab-duo-agent-platform-transforms-dataops",[1298,1299,1300],"product","tutorial","features","BlogPost","TnihcC4vVjEAwoh3LkKBX1GP3_u00BWxDQHrCCnH_3k",{"logo":1304,"freeTrial":1309,"sales":1314,"login":1319,"items":1324,"search":1650,"minimal":1681,"duo":1700,"switchNav":1709,"pricingDeployment":1720},{"config":1305},{"href":1306,"dataGaName":1307,"dataGaLocation":1308},"/","gitlab logo","header",{"text":1310,"config":1311},"Get free trial",{"href":1312,"dataGaName":1313,"dataGaLocation":1308},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":1315,"config":1316},"Request a demo",{"href":1317,"dataGaName":1318,"dataGaLocation":1308},"/sales/?contact-topic=request-demo","sales",{"text":1320,"config":1321},"Sign in",{"href":1322,"dataGaName":1323,"dataGaLocation":1308},"https://gitlab.com/users/sign_in/","sign in",[1325,1353,1453,1458,1572,1628],{"text":1326,"config":1327,"menu":1329},"Platform",{"dataNavLevelOne":1328},"platform",{"type":1330,"columns":1331},"cards",[1332,1338,1345],{"title":1326,"description":1333,"link":1334},"The intelligent orchestration platform for DevSecOps",{"text":1335,"config":1336},"Explore our Platform",{"href":1337,"dataGaName":1328,"dataGaLocation":1308},"/platform/",{"title":20,"description":1339,"link":1340},"Agentic AI for the entire software lifecycle",{"text":1341,"config":1342},"Meet GitLab Duo",{"href":1343,"dataGaName":1344,"dataGaLocation":1308},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1346,"description":1347,"link":1348},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1349,"config":1350},"Learn more",{"href":1351,"dataGaName":1352,"dataGaLocation":1308},"/why-gitlab/","why gitlab",{"text":1354,"left":995,"config":1355,"menu":1357},"Product",{"dataNavLevelOne":1356},"solutions",{"type":1358,"link":1359,"columns":1363,"feature":1432},"lists",{"text":1360,"config":1361},"View all Solutions",{"href":1362,"dataGaName":1356,"dataGaLocation":1308},"/solutions/",[1364,1388,1411],{"title":1365,"description":1366,"link":1367,"items":1372},"Automation","CI/CD and automation to accelerate deployment",{"config":1368},{"icon":1369,"href":1370,"dataGaName":1371,"dataGaLocation":1308},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[1373,1377,1380,1384],{"text":1374,"config":1375},"CI/CD",{"href":1376,"dataGaLocation":1308,"dataGaName":1374},"/solutions/continuous-integration/",{"text":20,"config":1378},{"href":1343,"dataGaLocation":1308,"dataGaName":1379},"gitlab duo agent platform - product menu",{"text":1381,"config":1382},"Source Code Management",{"href":1383,"dataGaLocation":1308,"dataGaName":1381},"/solutions/source-code-management/",{"text":1385,"config":1386},"Automated Software Delivery",{"href":1370,"dataGaLocation":1308,"dataGaName":1387},"Automated software delivery",{"title":1389,"description":1390,"link":1391,"items":1396},"Security","Deliver code faster without compromising security",{"config":1392},{"href":1393,"dataGaName":1394,"dataGaLocation":1308,"icon":1395},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1397,1401,1406],{"text":1398,"config":1399},"Application Security Testing",{"href":1393,"dataGaName":1400,"dataGaLocation":1308},"Application security testing",{"text":1402,"config":1403},"Software Supply Chain Security",{"href":1404,"dataGaLocation":1308,"dataGaName":1405},"/solutions/supply-chain/","Software supply chain security",{"text":1407,"config":1408},"Software Compliance",{"href":1409,"dataGaName":1410,"dataGaLocation":1308},"/solutions/software-compliance/","software compliance",{"title":1412,"link":1413,"items":1418},"Measurement",{"config":1414},{"icon":1415,"href":1416,"dataGaName":1417,"dataGaLocation":1308},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[1419,1423,1427],{"text":1420,"config":1421},"Visibility & Measurement",{"href":1416,"dataGaLocation":1308,"dataGaName":1422},"Visibility and Measurement",{"text":1424,"config":1425},"Value Stream Management",{"href":1426,"dataGaLocation":1308,"dataGaName":1424},"/solutions/value-stream-management/",{"text":1428,"config":1429},"Analytics & Insights",{"href":1430,"dataGaLocation":1308,"dataGaName":1431},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1433,"type":1358,"items":1434},"GitLab for",[1435,1441,1447],{"text":1436,"config":1437},"Enterprise",{"icon":1438,"href":1439,"dataGaLocation":1308,"dataGaName":1440},"Building","/enterprise/","enterprise",{"text":1442,"config":1443},"Small Business",{"icon":1444,"href":1445,"dataGaLocation":1308,"dataGaName":1446},"Work","/small-business/","small business",{"text":1448,"config":1449},"Public Sector",{"icon":1450,"href":1451,"dataGaLocation":1308,"dataGaName":1452},"Organization","/solutions/public-sector/","public sector",{"text":1454,"config":1455},"Pricing",{"href":1456,"dataGaName":1457,"dataGaLocation":1308,"dataNavLevelOne":1457},"/pricing/","pricing",{"text":1459,"config":1460,"menu":1462},"Resources",{"dataNavLevelOne":1461},"resources",{"type":1358,"link":1463,"columns":1467,"feature":1561},{"text":1464,"config":1465},"View all resources",{"href":1466,"dataGaName":1461,"dataGaLocation":1308},"/resources/",[1468,1501,1528],{"title":1469,"items":1470},"Getting started",[1471,1476,1481,1486,1491,1496],{"text":1472,"config":1473},"Install",{"href":1474,"dataGaName":1475,"dataGaLocation":1308},"/install/","install",{"text":1477,"config":1478},"Quick start guides",{"href":1479,"dataGaName":1480,"dataGaLocation":1308},"/get-started/","quick setup checklists",{"text":1482,"config":1483},"Learn",{"href":1484,"dataGaLocation":1308,"dataGaName":1485},"https://university.gitlab.com/","learn",{"text":1487,"config":1488},"Product documentation",{"href":1489,"dataGaName":1490,"dataGaLocation":1308},"https://docs.gitlab.com/","product documentation",{"text":1492,"config":1493},"Best practice videos",{"href":1494,"dataGaName":1495,"dataGaLocation":1308},"/getting-started-videos/","best practice videos",{"text":1497,"config":1498},"Integrations",{"href":1499,"dataGaName":1500,"dataGaLocation":1308},"/integrations/","integrations",{"title":1502,"items":1503},"Discover",[1504,1509,1514,1519,1523],{"text":1505,"config":1506},"Customer success stories",{"href":1507,"dataGaName":1508,"dataGaLocation":1308},"/customers/","customer success stories",{"text":1510,"config":1511},"Blog",{"href":1512,"dataGaName":1513,"dataGaLocation":1308},"/blog/","blog",{"text":1515,"config":1516},"Demo Hub",{"href":1517,"dataGaName":1518,"dataGaLocation":1308},"/demo-hub/","demo hub",{"text":1520,"config":1521},"The Source",{"href":1522,"dataGaName":1513,"dataGaLocation":1308},"/the-source/",{"text":1524,"config":1525},"Remote",{"href":1526,"dataGaName":1527,"dataGaLocation":1308},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1529,"items":1530},"Connect",[1531,1536,1541,1546,1551,1556],{"text":1532,"config":1533},"GitLab Services",{"href":1534,"dataGaName":1535,"dataGaLocation":1308},"/services/","services",{"text":1537,"config":1538},"Contribute",{"href":1539,"dataGaName":1540,"dataGaLocation":1308},"https://contributors.gitlab.com","contribute",{"text":1542,"config":1543},"Community",{"href":1544,"dataGaName":1545,"dataGaLocation":1308},"/community/","community",{"text":1547,"config":1548},"Forum",{"href":1549,"dataGaName":1550,"dataGaLocation":1308},"https://forum.gitlab.com/","forum",{"text":1552,"config":1553},"Events",{"href":1554,"dataGaName":1555,"dataGaLocation":1308},"/events/","events",{"text":1557,"config":1558},"Partners",{"href":1559,"dataGaName":1560,"dataGaLocation":1308},"/partners/","partners",{"config":1562,"title":1565,"text":1566,"link":1567},{"background":1563,"textColor":1564},"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":1568,"config":1569},"Read the latest",{"href":1570,"dataGaName":1571,"dataGaLocation":1308},"/whats-new/","whats new",{"text":1573,"config":1574,"menu":1576},"Company",{"dataNavLevelOne":1575},"company",{"type":1358,"columns":1577},[1578],{"items":1579},[1580,1585,1591,1593,1598,1603,1608,1613,1618,1623],{"text":1581,"config":1582},"About",{"href":1583,"dataGaName":1584,"dataGaLocation":1308},"/company/","about",{"text":1586,"config":1587,"footerGa":1590},"Jobs",{"href":1588,"dataGaName":1589,"dataGaLocation":1308},"/jobs/","jobs",{"dataGaName":1589},{"text":1552,"config":1592},{"href":1554,"dataGaName":1555,"dataGaLocation":1308},{"text":1594,"config":1595},"Leadership",{"href":1596,"dataGaName":1597,"dataGaLocation":1308},"/company/team/e-group/","leadership",{"text":1599,"config":1600},"Handbook",{"href":1601,"dataGaName":1602,"dataGaLocation":1308},"https://handbook.gitlab.com/","handbook",{"text":1604,"config":1605},"Investor relations",{"href":1606,"dataGaName":1607,"dataGaLocation":1308},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1609,"config":1610},"Trust Center",{"href":1611,"dataGaName":1612,"dataGaLocation":1308},"/security/","trust center",{"text":1614,"config":1615},"AI Transparency Center",{"href":1616,"dataGaName":1617,"dataGaLocation":1308},"/ai-transparency-center/","ai transparency center",{"text":1619,"config":1620},"Newsletter",{"href":1621,"dataGaName":1622,"dataGaLocation":1308},"/company/contact/#contact-forms","newsletter",{"text":1624,"config":1625},"Press",{"href":1626,"dataGaName":1627,"dataGaLocation":1308},"/press/","press",{"text":1629,"config":1630,"menu":1631},"Contact us",{"dataNavLevelOne":1575},{"type":1358,"columns":1632},[1633],{"items":1634},[1635,1640,1645],{"text":1636,"config":1637},"Talk to sales",{"href":1638,"dataGaName":1639,"dataGaLocation":1308},"/sales/","talk to sales",{"text":1641,"config":1642},"Support portal",{"href":1643,"dataGaName":1644,"dataGaLocation":1308},"https://support.gitlab.com/hc/en-us","support portal",{"text":1646,"config":1647},"Customer portal",{"href":1648,"dataGaName":1649,"dataGaLocation":1308},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1651,"login":1652,"suggestions":1659},"Close",{"text":1653,"link":1654},"To search repositories and projects, login to",{"text":1655,"config":1656},"gitlab.com",{"href":1322,"dataGaName":1657,"dataGaLocation":1658},"search login","search",{"text":1660,"default":1661},"Suggestions",[1662,1664,1668,1670,1674,1678],{"text":20,"config":1663},{"href":1343,"dataGaName":20,"dataGaLocation":1658},{"text":1665,"config":1666},"Code Suggestions (AI)",{"href":1667,"dataGaName":1665,"dataGaLocation":1658},"/solutions/code-suggestions/",{"text":1374,"config":1669},{"href":1376,"dataGaName":1374,"dataGaLocation":1658},{"text":1671,"config":1672},"GitLab on AWS",{"href":1673,"dataGaName":1671,"dataGaLocation":1658},"/partners/technology-partners/aws/",{"text":1675,"config":1676},"GitLab on Google Cloud",{"href":1677,"dataGaName":1675,"dataGaLocation":1658},"/partners/technology-partners/google-cloud-platform/",{"text":1679,"config":1680},"Why GitLab?",{"href":1351,"dataGaName":1679,"dataGaLocation":1658},{"freeTrial":1682,"mobileIcon":1687,"desktopIcon":1692,"secondaryButton":1695},{"text":1683,"config":1684},"Start free trial",{"href":1685,"dataGaName":1313,"dataGaLocation":1686},"https://gitlab.com/-/trials/new/","nav",{"altText":1688,"config":1689},"Gitlab Icon",{"src":1690,"dataGaName":1691,"dataGaLocation":1686},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1688,"config":1693},{"src":1694,"dataGaName":1691,"dataGaLocation":1686},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1696,"config":1697},"Get Started",{"href":1698,"dataGaName":1699,"dataGaLocation":1686},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1701,"mobileIcon":1705,"desktopIcon":1707},{"text":1702,"config":1703},"Learn more about GitLab Duo",{"href":1343,"dataGaName":1704,"dataGaLocation":1686},"gitlab duo",{"altText":1688,"config":1706},{"src":1690,"dataGaName":1691,"dataGaLocation":1686},{"altText":1688,"config":1708},{"src":1694,"dataGaName":1691,"dataGaLocation":1686},{"button":1710,"mobileIcon":1715,"desktopIcon":1717},{"text":1711,"config":1712},"/switch",{"href":1713,"dataGaName":1714,"dataGaLocation":1686},"#contact","switch",{"altText":1688,"config":1716},{"src":1690,"dataGaName":1691,"dataGaLocation":1686},{"altText":1688,"config":1718},{"src":1719,"dataGaName":1691,"dataGaLocation":1686},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1721,"mobileIcon":1726,"desktopIcon":1728},{"text":1722,"config":1723},"Back to pricing",{"href":1456,"dataGaName":1724,"dataGaLocation":1686,"icon":1725},"back to pricing","GoBack",{"altText":1688,"config":1727},{"src":1690,"dataGaName":1691,"dataGaLocation":1686},{"altText":1688,"config":1729},{"src":1694,"dataGaName":1691,"dataGaLocation":1686},{"title":1731,"titleMobile":1732,"button":1733,"config":1738},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1349,"config":1734},{"href":1735,"dataGaName":1736,"dataGaLocation":1737},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1739,"disabled":1740},"release",false,{"data":1742},{"text":1743,"source":1744,"edit":1750,"contribute":1755,"config":1760,"items":1765,"minimal":1975},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1745,"config":1746},"View page source",{"href":1747,"dataGaName":1748,"dataGaLocation":1749},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1751,"config":1752},"Edit this page",{"href":1753,"dataGaName":1754,"dataGaLocation":1749},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1756,"config":1757},"Please contribute",{"href":1758,"dataGaName":1759,"dataGaLocation":1749},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1761,"facebook":1762,"youtube":1763,"linkedin":1764},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1766,1813,1867,1911,1943],{"title":1454,"links":1767,"subMenu":1782},[1768,1772,1777],{"text":1769,"config":1770},"View plans",{"href":1456,"dataGaName":1771,"dataGaLocation":1749},"view plans",{"text":1773,"config":1774},"Why Premium?",{"href":1775,"dataGaName":1776,"dataGaLocation":1749},"/pricing/premium/","why premium",{"text":1778,"config":1779},"Why Ultimate?",{"href":1780,"dataGaName":1781,"dataGaLocation":1749},"/pricing/ultimate/","why ultimate",[1783],{"title":1784,"links":1785},"Contact Us",[1786,1789,1791,1793,1798,1803,1808],{"text":1787,"config":1788},"Contact sales",{"href":1638,"dataGaName":1318,"dataGaLocation":1749},{"text":1641,"config":1790},{"href":1643,"dataGaName":1644,"dataGaLocation":1749},{"text":1646,"config":1792},{"href":1648,"dataGaName":1649,"dataGaLocation":1749},{"text":1794,"config":1795},"Status",{"href":1796,"dataGaName":1797,"dataGaLocation":1749},"https://status.gitlab.com/","status",{"text":1799,"config":1800},"Terms of use",{"href":1801,"dataGaName":1802,"dataGaLocation":1749},"/terms/","terms of use",{"text":1804,"config":1805},"Privacy statement",{"href":1806,"dataGaName":1807,"dataGaLocation":1749},"/privacy/","privacy statement",{"text":1809,"config":1810},"Cookie preferences",{"dataGaName":1811,"dataGaLocation":1749,"id":1812,"isOneTrustButton":995},"cookie preferences","ot-sdk-btn",{"title":1354,"links":1814,"subMenu":1823},[1815,1819],{"text":1816,"config":1817},"DevSecOps platform",{"href":1337,"dataGaName":1818,"dataGaLocation":1749},"devsecops platform",{"text":1820,"config":1821},"AI-Assisted Development",{"href":1343,"dataGaName":1822,"dataGaLocation":1749},"ai-assisted development",[1824],{"title":1825,"links":1826},"Topics",[1827,1832,1837,1842,1847,1852,1857,1862],{"text":1828,"config":1829},"CICD",{"href":1830,"dataGaName":1831,"dataGaLocation":1749},"/topics/ci-cd/","cicd",{"text":1833,"config":1834},"GitOps",{"href":1835,"dataGaName":1836,"dataGaLocation":1749},"/topics/gitops/","gitops",{"text":1838,"config":1839},"DevOps",{"href":1840,"dataGaName":1841,"dataGaLocation":1749},"/topics/devops/","devops",{"text":1843,"config":1844},"Version Control",{"href":1845,"dataGaName":1846,"dataGaLocation":1749},"/topics/version-control/","version control",{"text":1848,"config":1849},"DevSecOps",{"href":1850,"dataGaName":1851,"dataGaLocation":1749},"/topics/devsecops/","devsecops",{"text":1853,"config":1854},"Cloud Native",{"href":1855,"dataGaName":1856,"dataGaLocation":1749},"/topics/cloud-native/","cloud native",{"text":1858,"config":1859},"AI for Coding",{"href":1860,"dataGaName":1861,"dataGaLocation":1749},"/topics/devops/ai-for-coding/","ai for coding",{"text":1863,"config":1864},"Agentic AI",{"href":1865,"dataGaName":1866,"dataGaLocation":1749},"/topics/agentic-ai/","agentic ai",{"title":1868,"links":1869},"Solutions",[1870,1872,1874,1879,1883,1886,1890,1893,1895,1898,1901,1906],{"text":1398,"config":1871},{"href":1393,"dataGaName":1398,"dataGaLocation":1749},{"text":1387,"config":1873},{"href":1370,"dataGaName":1371,"dataGaLocation":1749},{"text":1875,"config":1876},"Agile development",{"href":1877,"dataGaName":1878,"dataGaLocation":1749},"/solutions/agile-delivery/","agile delivery",{"text":1880,"config":1881},"SCM",{"href":1383,"dataGaName":1882,"dataGaLocation":1749},"source code management",{"text":1828,"config":1884},{"href":1376,"dataGaName":1885,"dataGaLocation":1749},"continuous integration & delivery",{"text":1887,"config":1888},"Value stream management",{"href":1426,"dataGaName":1889,"dataGaLocation":1749},"value stream management",{"text":1833,"config":1891},{"href":1892,"dataGaName":1836,"dataGaLocation":1749},"/solutions/gitops/",{"text":1436,"config":1894},{"href":1439,"dataGaName":1440,"dataGaLocation":1749},{"text":1896,"config":1897},"Small business",{"href":1445,"dataGaName":1446,"dataGaLocation":1749},{"text":1899,"config":1900},"Public sector",{"href":1451,"dataGaName":1452,"dataGaLocation":1749},{"text":1902,"config":1903},"Education",{"href":1904,"dataGaName":1905,"dataGaLocation":1749},"/solutions/education/","education",{"text":1907,"config":1908},"Financial services",{"href":1909,"dataGaName":1910,"dataGaLocation":1749},"/solutions/finance/","financial services",{"title":1459,"links":1912},[1913,1915,1917,1919,1922,1924,1927,1929,1931,1933,1935,1937,1939,1941],{"text":1472,"config":1914},{"href":1474,"dataGaName":1475,"dataGaLocation":1749},{"text":1477,"config":1916},{"href":1479,"dataGaName":1480,"dataGaLocation":1749},{"text":1482,"config":1918},{"href":1484,"dataGaName":1485,"dataGaLocation":1749},{"text":1487,"config":1920},{"href":1489,"dataGaName":1921,"dataGaLocation":1749},"docs",{"text":1510,"config":1923},{"href":1512,"dataGaName":1513,"dataGaLocation":1749},{"text":1925,"config":1926},"What's new",{"href":1570,"dataGaName":1571,"dataGaLocation":1749},{"text":1505,"config":1928},{"href":1507,"dataGaName":1508,"dataGaLocation":1749},{"text":1524,"config":1930},{"href":1526,"dataGaName":1527,"dataGaLocation":1749},{"text":1532,"config":1932},{"href":1534,"dataGaName":1535,"dataGaLocation":1749},{"text":1537,"config":1934},{"href":1539,"dataGaName":1540,"dataGaLocation":1749},{"text":1542,"config":1936},{"href":1544,"dataGaName":1545,"dataGaLocation":1749},{"text":1547,"config":1938},{"href":1549,"dataGaName":1550,"dataGaLocation":1749},{"text":1552,"config":1940},{"href":1554,"dataGaName":1555,"dataGaLocation":1749},{"text":1557,"config":1942},{"href":1559,"dataGaName":1560,"dataGaLocation":1749},{"title":1573,"links":1944},[1945,1947,1949,1951,1953,1955,1959,1964,1966,1968,1970],{"text":1581,"config":1946},{"href":1583,"dataGaName":1575,"dataGaLocation":1749},{"text":1586,"config":1948},{"href":1588,"dataGaName":1589,"dataGaLocation":1749},{"text":1594,"config":1950},{"href":1596,"dataGaName":1597,"dataGaLocation":1749},{"text":1599,"config":1952},{"href":1601,"dataGaName":1602,"dataGaLocation":1749},{"text":1604,"config":1954},{"href":1606,"dataGaName":1607,"dataGaLocation":1749},{"text":1956,"config":1957},"Sustainability",{"href":1958,"dataGaName":1956,"dataGaLocation":1749},"/sustainability/",{"text":1960,"config":1961},"Diversity, inclusion and belonging (DIB)",{"href":1962,"dataGaName":1963,"dataGaLocation":1749},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1609,"config":1965},{"href":1611,"dataGaName":1612,"dataGaLocation":1749},{"text":1619,"config":1967},{"href":1621,"dataGaName":1622,"dataGaLocation":1749},{"text":1624,"config":1969},{"href":1626,"dataGaName":1627,"dataGaLocation":1749},{"text":1971,"config":1972},"Modern Slavery Transparency Statement",{"href":1973,"dataGaName":1974,"dataGaLocation":1749},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1976},[1977,1980,1983],{"text":1978,"config":1979},"Terms",{"href":1801,"dataGaName":1802,"dataGaLocation":1749},{"text":1981,"config":1982},"Cookies",{"dataGaName":1811,"dataGaLocation":1749,"id":1812,"isOneTrustButton":995},{"text":1984,"config":1985},"Privacy",{"href":1806,"dataGaName":1807,"dataGaLocation":1749},[1987],{"id":1988,"title":1989,"body":1290,"config":1990,"content":1993,"description":1290,"extension":1996,"meta":1997,"navigation":995,"path":1998,"seo":1999,"stem":2000,"__hash__":2001},"blogAuthors/en-us/blog/authors/dennis-van-rooijen.yml","Dennis Van Rooijen",{"template":1991,"gitlabHandle":1992},"BlogAuthor","dvanrooijen2",{"name":7,"config":1994},{"headshot":1995},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758031391/muvwg1sxetzekmuhqdql.png","yml",{},"/en-us/blog/authors/dennis-van-rooijen",{},"en-us/blog/authors/dennis-van-rooijen","-VrBQM0MkpSMVi6cd_BwGMYKVgzryOCw3IxXukVkNGg",[2003,2011,2019],{"title":2004,"description":2005,"heroImage":2006,"category":1286,"date":2007,"authors":2008,"slug":2010,"externalUrl":1290},"How I built a demo generator with GitLab Duo Agent Platform","Demos haven't always looked like this. Looking back over my career, here's how they have evolved. Follow the tutorial to build your own demo and agent.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-08-13",[2009],"Itzik Gan Baruch","agentic-click-through-demo",{"title":2012,"description":2013,"heroImage":2014,"category":1286,"date":2015,"authors":2016,"slug":2018,"externalUrl":1290},"How to govern agentic AI, MCPs, and AI code assistants","AI agents can write, push, and deploy without human review at each step. Learn a practical framework for controlling access, permissions, and audit trails.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1785337625/ifzlc2hyzaxhzmjgyymh.png","2026-07-31",[2017],"Julie Griffin","govern-agentic-ai-mcps-code-assistants",{"title":2020,"description":2021,"heroImage":2022,"category":1286,"date":2023,"authors":2024,"slug":2026,"externalUrl":1290},"Claude Opus 5 on GitLab: Reasoning built for the hard tasks","Anthropic’s Claude Opus 5 is now available on GitLab Duo Agent Platform, delivering deeper reasoning without sacrificing speed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099203/Blog/Hero%20Images/Blog/Hero%20Images/blog-image-template-1800x945%20%2820%29_2bJGC5ZP3WheoqzlLT05C5_1750099203484.png","2026-07-27",[2025],"Brittany Lutz","claude-opus-5-on-gitlab-duo-agent-platform",{"promotions":2028},[2029,2042,2053,2065],{"id":2030,"categories":2031,"header":2032,"text":2033,"button":2034,"image":2039},"ai-modernization",[1286],"Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":2035,"config":2036},"Get your AI maturity score",{"href":2037,"dataGaName":2038,"dataGaLocation":1513},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":2040},{"src":2041},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":2043,"categories":2044,"header":2045,"text":2033,"button":2046,"image":2050},"devops-modernization",[1298,1851],"Are you just managing tools or shipping innovation?",{"text":2047,"config":2048},"Get your DevOps maturity score",{"href":2049,"dataGaName":2038,"dataGaLocation":1513},"/assessments/devops-modernization-assessment/",{"config":2051},{"src":2052},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":2054,"categories":2055,"header":2057,"text":2033,"button":2058,"image":2062},"security-modernization",[2056],"security","Are you trading speed for security?",{"text":2059,"config":2060},"Get your security maturity score",{"href":2061,"dataGaName":2038,"dataGaLocation":1513},"/assessments/security-modernization-assessment/",{"config":2063},{"src":2064},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":2066,"paths":2067,"header":2070,"text":2071,"button":2072,"image":2077},"github-azure-migration",[2068,2069],"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":2073,"config":2074},"See how GitLab compares to GitHub",{"href":2075,"dataGaName":2076,"dataGaLocation":1513},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":2078},{"src":2052},{"header":2080,"blurb":2081,"button":2082,"secondaryButton":2087},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":2083,"config":2084},"Get your free trial",{"href":2085,"dataGaName":1313,"dataGaLocation":2086},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1787,"config":2088},{"href":1638,"dataGaName":1318,"dataGaLocation":2086},1786803751691]