[{"data":1,"prerenderedAt":1081},["ShallowReactive",2],{"/blog/more-performant-and-robust-task-lists-in-gitlab":3,"navigation-en-us":282,"banner-en-us":710,"footer-en-us":720,"blog-post-authors-en-us-Fatih Acet|Brett Walker":965,"blog-related-posts-en-us-more-performant-and-robust-task-lists-in-gitlab":991,"blog-promotions-en-us":1017,"next-steps-en-us":1071},{"id":4,"title":5,"authors":6,"body":9,"category":261,"date":262,"description":263,"extension":264,"externalUrl":265,"faq":265,"featured":266,"heroImage":267,"meta":268,"navigation":269,"path":270,"seo":271,"slug":275,"stem":276,"tags":277,"template":280,"updatedDate":265,"__hash__":281},"blogPosts/en-us/blog/more-performant-and-robust-task-lists-in-gitlab.md","How we delivered more performant and robust task lists in GitLab",[7,8],"Fatih Acet","Brett Walker",{"type":10,"value":11,"toc":252},"minimark",[12,28,33,36,39,48,53,63,66,70,73,77,97,101,110,121,201,210,213,216,234,248],[13,14,15,21,22,27],"p",{},[16,17,20],"a",{"href":18,"rel":19},"https://docs.gitlab.com/user/markdown/#task-lists",[],"GitLab task lists"," are\na list of checkboxes that you can include anywhere in GitLab where you can have\n",[16,23,26],{"href":24,"rel":25},"https://docs.gitlab.com/user/markdown/#gitlab-flavored-markdown-gfm",[],"GitLab Flavored Markdown (GFM)",".\nThis includes issue descriptions and comments, as well as merge requests and epics.\nThey can be used for a list of items to consider when building a feature, tracking\ntasks for new employees to complete when onboarding, or even managing that list\nof materials to purchase for your next home renovation. You can use them as todo\nlists, and so checking off an item should be quick and satisfying.",[29,30,32],"h2",{"id":31},"more-checkboxes-more-problems","More checkboxes, more problems",[13,34,35],{},"In the past, task lists with several items, even dozens, worked fairly well. Check\nan empty checkbox, and a database record gets updated. The checkbox is then displayed\nas checked. Done.",[13,37,38],{},"However, as the number of items increases, and the consequent\nmarkdown becomes more complex and longer, problems begin to appear. For example,\nvisually the checkbox appears checked, but because updating the backend takes a\nlonger time, if you checked another checkbox, the screen would refresh several seconds\nlater and the checkbox might then be unchecked. It soon became next to impossible\nto go down a list and check off items without waiting 10 seconds between each one.\nYet another problem was that if other users were also checking items on the list,\nyour change could be erased by them checking their item – they were overwriting\nyour data.",[13,40,41,42,47],{},"In ",[16,43,46],{"href":44,"rel":45},"https://docs.gitlab.com/releases/#performance-improvements",[],"GitLab 11.8"," (released on Feb. 22, 2019),\nwe significantly increased the performance of task lists, as well as making them\nmuch more robust. Here's how we did it:",[49,50,52],"h3",{"id":51},"essentially-we-wanted","Essentially we wanted:",[54,55,56,60],"ul",{},[57,58,59],"li",{},"Checking a checkbox to be as fast as possible.",[57,61,62],{},"Many users to concurrently interact with checkboxes in the same task list,\nwithout overwriting each other.",[13,64,65],{},"Both the performance and data integrity issues stemmed from the fact that we were\nupdating the complete markdown. This meant that we changed the markdown source in\nthe browser with the updated checkbox, sent it to the backend, where it was saved\nto the database, and then re-rendered so that we could cache the new  and send\nit back to the user.",[29,67,69],{"id":68},"a-scalable-solution","A scalable solution",[13,71,72],{},"But what if we could update a single checkbox, and send only that to the backend? That\nmight allow multiple users to check off as many tasks as they wanted, without clobbering\neach other. And what if we didn't have to do any markdown rendering at all? We wouldn't\nhave to do any markdown processing, or process embedded issue links, or query if\nlabels have changed, or any of the other advanced things that go on when updating\nan issue. Performance would definitely increase in this case.",[49,74,76],{"id":75},"frontend-work","Frontend work",[13,78,79,80,85,86,91,92,96],{},"On the frontend, with only a small modification to the\n",[16,81,84],{"href":82,"rel":83},"https://github.com/deckar01/task_list/commit/d1c96451df5fb8fdadc2cd080f65ffe2d2076a3a",[],"deckar01/task_list","\ngem we use, we were able to pass the exact text and line number in the markdown source\nfor the clicked task.\n",[16,87,90],{"href":88,"rel":89},"https://gitlab.com/gitlab-org/gitlab-ce/blob/b4165554113a7f9ce9fecd7d169f9a64686b5c44/app/assets/javascripts/task_list.js#L63-68",[],"Wrap this piece of information","\nin a new ",[93,94,95],"code",{},"update_task"," parameter for our update endpoint, and send it to the backend.",[49,98,100],{"id":99},"backend-work","Backend work",[13,102,103,104,109],{},"On the backend,\n",[16,105,108],{"href":106,"rel":107},"https://gitlab.com/gitlab-org/gitlab-ce/blob/b4165554113a7f9ce9fecd7d169f9a64686b5c44/app/services/task_list_toggle_service.rb#L30-51",[],"we needed to verify","\nthat the task we were interested in still existed in exactly the same format – the text had to match\nthe exact line number in the source. This meant that even if someone changed text above or below\nthe task item, as long as our line matched exactly, we could update that line in the latest source\nand save it without losing changes.",[13,111,112,113,116,117,120],{},"In order to update our cached HTML so that we wouldn't have to re-render it, we turned on\nthe ",[93,114,115],{},"SOURCEPOS"," flag of the CommonMark renderer, which adds a ",[93,118,119],{},"data-sourcepos"," attribute to the HTML.\nFor example, a task item's might look like this:",[122,123,128],"pre",{"className":124,"code":125,"language":126,"meta":127,"style":127},"language-xml shiki shiki-themes github-light","\u003Cli data-sourcepos=\"1:1-1:12\" class=\"task-list-item\">\n  \u003Cinput type=\"checkbox\" class=\"task-list-item-checkbox\" disabled> Task 1\n\u003C/li>\n","xml","",[93,129,130,164,191],{"__ignoreMap":127},[131,132,135,139,142,146,149,153,156,158,161],"span",{"class":133,"line":134},"line",1,[131,136,138],{"class":137},"sgsFI","\u003C",[131,140,57],{"class":141},"shJU0",[131,143,145],{"class":144},"s7eDp"," data-sourcepos",[131,147,148],{"class":137},"=",[131,150,152],{"class":151},"sYBdl","\"1:1-1:12\"",[131,154,155],{"class":144}," class",[131,157,148],{"class":137},[131,159,160],{"class":151},"\"task-list-item\"",[131,162,163],{"class":137},">\n",[131,165,167,170,173,176,178,181,183,185,188],{"class":133,"line":166},2,[131,168,169],{"class":137},"  \u003C",[131,171,172],{"class":141},"input",[131,174,175],{"class":144}," type",[131,177,148],{"class":137},[131,179,180],{"class":151},"\"checkbox\"",[131,182,155],{"class":144},[131,184,148],{"class":137},[131,186,187],{"class":151},"\"task-list-item-checkbox\"",[131,189,190],{"class":137}," disabled> Task 1\n",[131,192,194,197,199],{"class":133,"line":193},3,[131,195,196],{"class":137},"\u003C/",[131,198,57],{"class":141},[131,200,163],{"class":137},[13,202,203,204,209],{},"With a little ",[16,205,208],{"href":206,"rel":207},"https://nokogiri.org",[],"Nokogiri"," magic we were able to find the correct line\nand toggle the checked attribute.",[13,211,212],{},"Since we updated the cache directly, we completely bypassed any markdown rendering,\nprocessing of special attributes, etc. Performance dramatically increased. However,\nsince we are not able to get it down to zero, we disabled the checkboxes while the\nrequest was in flight to ensure we weren't getting clicks on other tasks.",[13,214,215],{},"The result: a much more satisfying task list.",[13,217,218,222,223,227,228,233],{},[16,219,8],{"href":220,"rel":221},"https://gitlab.com/digitalmoksha",[]," worked on the backend changes and\n",[16,224,7],{"href":225,"rel":226},"https://gitlab.com/fatihacet",[]," worked on the frontend changes in this\nimprovement. See more details in ",[16,229,232],{"href":230,"rel":231},"https://gitlab.com/gitlab-org/gitlab-ce/issues/19745",[],"the GitLab issue",".",[13,235,236,237,242,243],{},"Photo by ",[16,238,241],{"href":239,"rel":240},"https://unsplash.com/photos/RLw-UC03Gwc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Glenn Carstens-Peters"," on ",[16,244,247],{"href":245,"rel":246},"https://unsplash.com/",[],"Unsplash",[249,250,251],"style",{},"html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}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);}",{"title":127,"searchDepth":166,"depth":166,"links":253},[254,257],{"id":31,"depth":166,"text":32,"children":255},[256],{"id":51,"depth":193,"text":52},{"id":68,"depth":166,"text":69,"children":258},[259,260],{"id":75,"depth":193,"text":76},{"id":99,"depth":193,"text":100},"engineering","2019-04-05","How simple checkboxes became a challenging engineering problem – and how we fixed it.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749668319/Blog/Hero%20Images/more-robust-task-lists.jpg",{},true,"/en-us/blog/more-performant-and-robust-task-lists-in-gitlab",{"title":5,"description":263,"ogTitle":5,"ogDescription":263,"noIndex":266,"ogImage":267,"ogUrl":272,"ogSiteName":273,"ogType":274,"canonicalUrls":272},"https://about.gitlab.com/blog/more-performant-and-robust-task-lists-in-gitlab","https://about.gitlab.com","article","more-performant-and-robust-task-lists-in-gitlab","en-us/blog/more-performant-and-robust-task-lists-in-gitlab",[278,279],"frontend","inside GitLab","BlogPost","whX-KTP0yU5WqY84CiL2rkee60eGxVzl2fGRRtAOSDk",{"logo":283,"freeTrial":288,"sales":293,"login":298,"items":303,"search":630,"minimal":661,"duo":680,"switchNav":689,"pricingDeployment":700},{"config":284},{"href":285,"dataGaName":286,"dataGaLocation":287},"/","gitlab logo","header",{"text":289,"config":290},"Get free trial",{"href":291,"dataGaName":292,"dataGaLocation":287},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":294,"config":295},"Request a demo",{"href":296,"dataGaName":297,"dataGaLocation":287},"/sales/?contact-topic=request-demo","sales",{"text":299,"config":300},"Sign in",{"href":301,"dataGaName":302,"dataGaLocation":287},"https://gitlab.com/users/sign_in/","sign in",[304,333,433,438,552,608],{"text":305,"config":306,"menu":308},"Platform",{"dataNavLevelOne":307},"platform",{"type":309,"columns":310},"cards",[311,317,325],{"title":305,"description":312,"link":313},"The intelligent orchestration platform for DevSecOps",{"text":314,"config":315},"Explore our Platform",{"href":316,"dataGaName":307,"dataGaLocation":287},"/platform/",{"title":318,"description":319,"link":320},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":321,"config":322},"Meet GitLab Duo",{"href":323,"dataGaName":324,"dataGaLocation":287},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":326,"description":327,"link":328},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":329,"config":330},"Learn more",{"href":331,"dataGaName":332,"dataGaLocation":287},"/why-gitlab/","why gitlab",{"text":334,"left":269,"config":335,"menu":337},"Product",{"dataNavLevelOne":336},"solutions",{"type":338,"link":339,"columns":343,"feature":412},"lists",{"text":340,"config":341},"View all Solutions",{"href":342,"dataGaName":336,"dataGaLocation":287},"/solutions/",[344,368,391],{"title":345,"description":346,"link":347,"items":352},"Automation","CI/CD and automation to accelerate deployment",{"config":348},{"icon":349,"href":350,"dataGaName":351,"dataGaLocation":287},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[353,357,360,364],{"text":354,"config":355},"CI/CD",{"href":356,"dataGaLocation":287,"dataGaName":354},"/solutions/continuous-integration/",{"text":318,"config":358},{"href":323,"dataGaLocation":287,"dataGaName":359},"gitlab duo agent platform - product menu",{"text":361,"config":362},"Source Code Management",{"href":363,"dataGaLocation":287,"dataGaName":361},"/solutions/source-code-management/",{"text":365,"config":366},"Automated Software Delivery",{"href":350,"dataGaLocation":287,"dataGaName":367},"Automated software delivery",{"title":369,"description":370,"link":371,"items":376},"Security","Deliver code faster without compromising security",{"config":372},{"href":373,"dataGaName":374,"dataGaLocation":287,"icon":375},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[377,381,386],{"text":378,"config":379},"Application Security Testing",{"href":373,"dataGaName":380,"dataGaLocation":287},"Application security testing",{"text":382,"config":383},"Software Supply Chain Security",{"href":384,"dataGaLocation":287,"dataGaName":385},"/solutions/supply-chain/","Software supply chain security",{"text":387,"config":388},"Software Compliance",{"href":389,"dataGaName":390,"dataGaLocation":287},"/solutions/software-compliance/","software compliance",{"title":392,"link":393,"items":398},"Measurement",{"config":394},{"icon":395,"href":396,"dataGaName":397,"dataGaLocation":287},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[399,403,407],{"text":400,"config":401},"Visibility & Measurement",{"href":396,"dataGaLocation":287,"dataGaName":402},"Visibility and Measurement",{"text":404,"config":405},"Value Stream Management",{"href":406,"dataGaLocation":287,"dataGaName":404},"/solutions/value-stream-management/",{"text":408,"config":409},"Analytics & Insights",{"href":410,"dataGaLocation":287,"dataGaName":411},"/solutions/analytics-and-insights/","Analytics and insights",{"title":413,"type":338,"items":414},"GitLab for",[415,421,427],{"text":416,"config":417},"Enterprise",{"icon":418,"href":419,"dataGaLocation":287,"dataGaName":420},"Building","/enterprise/","enterprise",{"text":422,"config":423},"Small Business",{"icon":424,"href":425,"dataGaLocation":287,"dataGaName":426},"Work","/small-business/","small business",{"text":428,"config":429},"Public Sector",{"icon":430,"href":431,"dataGaLocation":287,"dataGaName":432},"Organization","/solutions/public-sector/","public sector",{"text":434,"config":435},"Pricing",{"href":436,"dataGaName":437,"dataGaLocation":287,"dataNavLevelOne":437},"/pricing/","pricing",{"text":439,"config":440,"menu":442},"Resources",{"dataNavLevelOne":441},"resources",{"type":338,"link":443,"columns":447,"feature":541},{"text":444,"config":445},"View all resources",{"href":446,"dataGaName":441,"dataGaLocation":287},"/resources/",[448,481,508],{"title":449,"items":450},"Getting started",[451,456,461,466,471,476],{"text":452,"config":453},"Install",{"href":454,"dataGaName":455,"dataGaLocation":287},"/install/","install",{"text":457,"config":458},"Quick start guides",{"href":459,"dataGaName":460,"dataGaLocation":287},"/get-started/","quick setup checklists",{"text":462,"config":463},"Learn",{"href":464,"dataGaLocation":287,"dataGaName":465},"https://university.gitlab.com/","learn",{"text":467,"config":468},"Product documentation",{"href":469,"dataGaName":470,"dataGaLocation":287},"https://docs.gitlab.com/","product documentation",{"text":472,"config":473},"Best practice videos",{"href":474,"dataGaName":475,"dataGaLocation":287},"/getting-started-videos/","best practice videos",{"text":477,"config":478},"Integrations",{"href":479,"dataGaName":480,"dataGaLocation":287},"/integrations/","integrations",{"title":482,"items":483},"Discover",[484,489,494,499,503],{"text":485,"config":486},"Customer success stories",{"href":487,"dataGaName":488,"dataGaLocation":287},"/customers/","customer success stories",{"text":490,"config":491},"Blog",{"href":492,"dataGaName":493,"dataGaLocation":287},"/blog/","blog",{"text":495,"config":496},"Demo Hub",{"href":497,"dataGaName":498,"dataGaLocation":287},"/demo-hub/","demo hub",{"text":500,"config":501},"The Source",{"href":502,"dataGaName":493,"dataGaLocation":287},"/the-source/",{"text":504,"config":505},"Remote",{"href":506,"dataGaName":507,"dataGaLocation":287},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":509,"items":510},"Connect",[511,516,521,526,531,536],{"text":512,"config":513},"GitLab Services",{"href":514,"dataGaName":515,"dataGaLocation":287},"/services/","services",{"text":517,"config":518},"Contribute",{"href":519,"dataGaName":520,"dataGaLocation":287},"https://contributors.gitlab.com","contribute",{"text":522,"config":523},"Community",{"href":524,"dataGaName":525,"dataGaLocation":287},"/community/","community",{"text":527,"config":528},"Forum",{"href":529,"dataGaName":530,"dataGaLocation":287},"https://forum.gitlab.com/","forum",{"text":532,"config":533},"Events",{"href":534,"dataGaName":535,"dataGaLocation":287},"/events/","events",{"text":537,"config":538},"Partners",{"href":539,"dataGaName":540,"dataGaLocation":287},"/partners/","partners",{"config":542,"title":545,"text":546,"link":547},{"background":543,"textColor":544},"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":548,"config":549},"Read the latest",{"href":550,"dataGaName":551,"dataGaLocation":287},"/whats-new/","whats new",{"text":553,"config":554,"menu":556},"Company",{"dataNavLevelOne":555},"company",{"type":338,"columns":557},[558],{"items":559},[560,565,571,573,578,583,588,593,598,603],{"text":561,"config":562},"About",{"href":563,"dataGaName":564,"dataGaLocation":287},"/company/","about",{"text":566,"config":567,"footerGa":570},"Jobs",{"href":568,"dataGaName":569,"dataGaLocation":287},"/jobs/","jobs",{"dataGaName":569},{"text":532,"config":572},{"href":534,"dataGaName":535,"dataGaLocation":287},{"text":574,"config":575},"Leadership",{"href":576,"dataGaName":577,"dataGaLocation":287},"/company/team/e-group/","leadership",{"text":579,"config":580},"Handbook",{"href":581,"dataGaName":582,"dataGaLocation":287},"https://handbook.gitlab.com/","handbook",{"text":584,"config":585},"Investor relations",{"href":586,"dataGaName":587,"dataGaLocation":287},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":589,"config":590},"Trust Center",{"href":591,"dataGaName":592,"dataGaLocation":287},"/security/","trust center",{"text":594,"config":595},"AI Transparency Center",{"href":596,"dataGaName":597,"dataGaLocation":287},"/ai-transparency-center/","ai transparency center",{"text":599,"config":600},"Newsletter",{"href":601,"dataGaName":602,"dataGaLocation":287},"/company/contact/#contact-forms","newsletter",{"text":604,"config":605},"Press",{"href":606,"dataGaName":607,"dataGaLocation":287},"/press/","press",{"text":609,"config":610,"menu":611},"Contact us",{"dataNavLevelOne":555},{"type":338,"columns":612},[613],{"items":614},[615,620,625],{"text":616,"config":617},"Talk to sales",{"href":618,"dataGaName":619,"dataGaLocation":287},"/sales/","talk to sales",{"text":621,"config":622},"Support portal",{"href":623,"dataGaName":624,"dataGaLocation":287},"https://support.gitlab.com/hc/en-us","support portal",{"text":626,"config":627},"Customer portal",{"href":628,"dataGaName":629,"dataGaLocation":287},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":631,"login":632,"suggestions":639},"Close",{"text":633,"link":634},"To search repositories and projects, login to",{"text":635,"config":636},"gitlab.com",{"href":301,"dataGaName":637,"dataGaLocation":638},"search login","search",{"text":640,"default":641},"Suggestions",[642,644,648,650,654,658],{"text":318,"config":643},{"href":323,"dataGaName":318,"dataGaLocation":638},{"text":645,"config":646},"Code Suggestions (AI)",{"href":647,"dataGaName":645,"dataGaLocation":638},"/solutions/code-suggestions/",{"text":354,"config":649},{"href":356,"dataGaName":354,"dataGaLocation":638},{"text":651,"config":652},"GitLab on AWS",{"href":653,"dataGaName":651,"dataGaLocation":638},"/partners/technology-partners/aws/",{"text":655,"config":656},"GitLab on Google Cloud",{"href":657,"dataGaName":655,"dataGaLocation":638},"/partners/technology-partners/google-cloud-platform/",{"text":659,"config":660},"Why GitLab?",{"href":331,"dataGaName":659,"dataGaLocation":638},{"freeTrial":662,"mobileIcon":667,"desktopIcon":672,"secondaryButton":675},{"text":663,"config":664},"Start free trial",{"href":665,"dataGaName":292,"dataGaLocation":666},"https://gitlab.com/-/trials/new/","nav",{"altText":668,"config":669},"Gitlab Icon",{"src":670,"dataGaName":671,"dataGaLocation":666},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":668,"config":673},{"src":674,"dataGaName":671,"dataGaLocation":666},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":676,"config":677},"Get Started",{"href":678,"dataGaName":679,"dataGaLocation":666},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":681,"mobileIcon":685,"desktopIcon":687},{"text":682,"config":683},"Learn more about GitLab Duo",{"href":323,"dataGaName":684,"dataGaLocation":666},"gitlab duo",{"altText":668,"config":686},{"src":670,"dataGaName":671,"dataGaLocation":666},{"altText":668,"config":688},{"src":674,"dataGaName":671,"dataGaLocation":666},{"button":690,"mobileIcon":695,"desktopIcon":697},{"text":691,"config":692},"/switch",{"href":693,"dataGaName":694,"dataGaLocation":666},"#contact","switch",{"altText":668,"config":696},{"src":670,"dataGaName":671,"dataGaLocation":666},{"altText":668,"config":698},{"src":699,"dataGaName":671,"dataGaLocation":666},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":701,"mobileIcon":706,"desktopIcon":708},{"text":702,"config":703},"Back to pricing",{"href":436,"dataGaName":704,"dataGaLocation":666,"icon":705},"back to pricing","GoBack",{"altText":668,"config":707},{"src":670,"dataGaName":671,"dataGaLocation":666},{"altText":668,"config":709},{"src":674,"dataGaName":671,"dataGaLocation":666},{"title":711,"titleMobile":712,"button":713,"config":718},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":329,"config":714},{"href":715,"dataGaName":716,"dataGaLocation":717},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":719,"disabled":266},"release",{"data":721},{"text":722,"source":723,"edit":729,"contribute":734,"config":739,"items":744,"minimal":954},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":724,"config":725},"View page source",{"href":726,"dataGaName":727,"dataGaLocation":728},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":730,"config":731},"Edit this page",{"href":732,"dataGaName":733,"dataGaLocation":728},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":735,"config":736},"Please contribute",{"href":737,"dataGaName":738,"dataGaLocation":728},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":740,"facebook":741,"youtube":742,"linkedin":743},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[745,792,846,890,922],{"title":434,"links":746,"subMenu":761},[747,751,756],{"text":748,"config":749},"View plans",{"href":436,"dataGaName":750,"dataGaLocation":728},"view plans",{"text":752,"config":753},"Why Premium?",{"href":754,"dataGaName":755,"dataGaLocation":728},"/pricing/premium/","why premium",{"text":757,"config":758},"Why Ultimate?",{"href":759,"dataGaName":760,"dataGaLocation":728},"/pricing/ultimate/","why ultimate",[762],{"title":763,"links":764},"Contact Us",[765,768,770,772,777,782,787],{"text":766,"config":767},"Contact sales",{"href":618,"dataGaName":297,"dataGaLocation":728},{"text":621,"config":769},{"href":623,"dataGaName":624,"dataGaLocation":728},{"text":626,"config":771},{"href":628,"dataGaName":629,"dataGaLocation":728},{"text":773,"config":774},"Status",{"href":775,"dataGaName":776,"dataGaLocation":728},"https://status.gitlab.com/","status",{"text":778,"config":779},"Terms of use",{"href":780,"dataGaName":781,"dataGaLocation":728},"/terms/","terms of use",{"text":783,"config":784},"Privacy statement",{"href":785,"dataGaName":786,"dataGaLocation":728},"/privacy/","privacy statement",{"text":788,"config":789},"Cookie preferences",{"dataGaName":790,"dataGaLocation":728,"id":791,"isOneTrustButton":269},"cookie preferences","ot-sdk-btn",{"title":334,"links":793,"subMenu":802},[794,798],{"text":795,"config":796},"DevSecOps platform",{"href":316,"dataGaName":797,"dataGaLocation":728},"devsecops platform",{"text":799,"config":800},"AI-Assisted Development",{"href":323,"dataGaName":801,"dataGaLocation":728},"ai-assisted development",[803],{"title":804,"links":805},"Topics",[806,811,816,821,826,831,836,841],{"text":807,"config":808},"CICD",{"href":809,"dataGaName":810,"dataGaLocation":728},"/topics/ci-cd/","cicd",{"text":812,"config":813},"GitOps",{"href":814,"dataGaName":815,"dataGaLocation":728},"/topics/gitops/","gitops",{"text":817,"config":818},"DevOps",{"href":819,"dataGaName":820,"dataGaLocation":728},"/topics/devops/","devops",{"text":822,"config":823},"Version Control",{"href":824,"dataGaName":825,"dataGaLocation":728},"/topics/version-control/","version control",{"text":827,"config":828},"DevSecOps",{"href":829,"dataGaName":830,"dataGaLocation":728},"/topics/devsecops/","devsecops",{"text":832,"config":833},"Cloud Native",{"href":834,"dataGaName":835,"dataGaLocation":728},"/topics/cloud-native/","cloud native",{"text":837,"config":838},"AI for Coding",{"href":839,"dataGaName":840,"dataGaLocation":728},"/topics/devops/ai-for-coding/","ai for coding",{"text":842,"config":843},"Agentic AI",{"href":844,"dataGaName":845,"dataGaLocation":728},"/topics/agentic-ai/","agentic ai",{"title":847,"links":848},"Solutions",[849,851,853,858,862,865,869,872,874,877,880,885],{"text":378,"config":850},{"href":373,"dataGaName":378,"dataGaLocation":728},{"text":367,"config":852},{"href":350,"dataGaName":351,"dataGaLocation":728},{"text":854,"config":855},"Agile development",{"href":856,"dataGaName":857,"dataGaLocation":728},"/solutions/agile-delivery/","agile delivery",{"text":859,"config":860},"SCM",{"href":363,"dataGaName":861,"dataGaLocation":728},"source code management",{"text":807,"config":863},{"href":356,"dataGaName":864,"dataGaLocation":728},"continuous integration & delivery",{"text":866,"config":867},"Value stream management",{"href":406,"dataGaName":868,"dataGaLocation":728},"value stream management",{"text":812,"config":870},{"href":871,"dataGaName":815,"dataGaLocation":728},"/solutions/gitops/",{"text":416,"config":873},{"href":419,"dataGaName":420,"dataGaLocation":728},{"text":875,"config":876},"Small business",{"href":425,"dataGaName":426,"dataGaLocation":728},{"text":878,"config":879},"Public sector",{"href":431,"dataGaName":432,"dataGaLocation":728},{"text":881,"config":882},"Education",{"href":883,"dataGaName":884,"dataGaLocation":728},"/solutions/education/","education",{"text":886,"config":887},"Financial services",{"href":888,"dataGaName":889,"dataGaLocation":728},"/solutions/finance/","financial services",{"title":439,"links":891},[892,894,896,898,901,903,906,908,910,912,914,916,918,920],{"text":452,"config":893},{"href":454,"dataGaName":455,"dataGaLocation":728},{"text":457,"config":895},{"href":459,"dataGaName":460,"dataGaLocation":728},{"text":462,"config":897},{"href":464,"dataGaName":465,"dataGaLocation":728},{"text":467,"config":899},{"href":469,"dataGaName":900,"dataGaLocation":728},"docs",{"text":490,"config":902},{"href":492,"dataGaName":493,"dataGaLocation":728},{"text":904,"config":905},"What's new",{"href":550,"dataGaName":551,"dataGaLocation":728},{"text":485,"config":907},{"href":487,"dataGaName":488,"dataGaLocation":728},{"text":504,"config":909},{"href":506,"dataGaName":507,"dataGaLocation":728},{"text":512,"config":911},{"href":514,"dataGaName":515,"dataGaLocation":728},{"text":517,"config":913},{"href":519,"dataGaName":520,"dataGaLocation":728},{"text":522,"config":915},{"href":524,"dataGaName":525,"dataGaLocation":728},{"text":527,"config":917},{"href":529,"dataGaName":530,"dataGaLocation":728},{"text":532,"config":919},{"href":534,"dataGaName":535,"dataGaLocation":728},{"text":537,"config":921},{"href":539,"dataGaName":540,"dataGaLocation":728},{"title":553,"links":923},[924,926,928,930,932,934,938,943,945,947,949],{"text":561,"config":925},{"href":563,"dataGaName":555,"dataGaLocation":728},{"text":566,"config":927},{"href":568,"dataGaName":569,"dataGaLocation":728},{"text":574,"config":929},{"href":576,"dataGaName":577,"dataGaLocation":728},{"text":579,"config":931},{"href":581,"dataGaName":582,"dataGaLocation":728},{"text":584,"config":933},{"href":586,"dataGaName":587,"dataGaLocation":728},{"text":935,"config":936},"Sustainability",{"href":937,"dataGaName":935,"dataGaLocation":728},"/sustainability/",{"text":939,"config":940},"Diversity, inclusion and belonging (DIB)",{"href":941,"dataGaName":942,"dataGaLocation":728},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":589,"config":944},{"href":591,"dataGaName":592,"dataGaLocation":728},{"text":599,"config":946},{"href":601,"dataGaName":602,"dataGaLocation":728},{"text":604,"config":948},{"href":606,"dataGaName":607,"dataGaLocation":728},{"text":950,"config":951},"Modern Slavery Transparency Statement",{"href":952,"dataGaName":953,"dataGaLocation":728},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":955},[956,959,962],{"text":957,"config":958},"Terms",{"href":780,"dataGaName":781,"dataGaLocation":728},{"text":960,"config":961},"Cookies",{"dataGaName":790,"dataGaLocation":728,"id":791,"isOneTrustButton":269},{"text":963,"config":964},"Privacy",{"href":785,"dataGaName":786,"dataGaLocation":728},[966,979],{"id":967,"title":7,"body":265,"config":968,"content":970,"description":265,"extension":973,"meta":974,"navigation":269,"path":975,"seo":976,"stem":977,"__hash__":978},"blogAuthors/en-us/blog/authors/fatih-acet.yml",{"template":969},"BlogAuthor",{"name":7,"config":971},{"headshot":127,"ctfId":972},"fatihacet","yml",{},"/en-us/blog/authors/fatih-acet",{},"en-us/blog/authors/fatih-acet","1DMYjoRqjs7iiSaS6hub5ZRy7F9mTMri3jxRzWIS1oQ",{"id":980,"title":8,"body":265,"config":981,"content":982,"description":265,"extension":973,"meta":986,"navigation":269,"path":987,"seo":988,"stem":989,"__hash__":990},"blogAuthors/en-us/blog/authors/brett-walker.yml",{"template":969},{"name":8,"config":983},{"headshot":984,"ctfId":985},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749670155/Blog/Author%20Headshots/digitalmoksha-headshot.jpg","digitalmoksha",{},"/en-us/blog/authors/brett-walker",{},"en-us/blog/authors/brett-walker","ktLx8H0sWL7REEk1p_RJaxT-uuNjGcn89CyctrJ-ELE",[992,1001,1009],{"title":993,"description":994,"heroImage":995,"category":261,"date":996,"authors":997,"slug":1000,"externalUrl":265},"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",[998,999],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1002,"description":1003,"heroImage":1004,"category":261,"date":1005,"authors":1006,"slug":1008,"externalUrl":265},"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",[1007],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1010,"description":1011,"heroImage":1012,"category":261,"date":1013,"authors":1014,"slug":1016,"externalUrl":265},"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",[1015],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1018},[1019,1033,1045,1057],{"id":1020,"categories":1021,"header":1023,"text":1024,"button":1025,"image":1030},"ai-modernization",[1022],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1026,"config":1027},"Get your AI maturity score",{"href":1028,"dataGaName":1029,"dataGaLocation":493},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1031},{"src":1032},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1034,"categories":1035,"header":1037,"text":1024,"button":1038,"image":1042},"devops-modernization",[1036,830],"product","Are you just managing tools or shipping innovation?",{"text":1039,"config":1040},"Get your DevOps maturity score",{"href":1041,"dataGaName":1029,"dataGaLocation":493},"/assessments/devops-modernization-assessment/",{"config":1043},{"src":1044},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1046,"categories":1047,"header":1049,"text":1024,"button":1050,"image":1054},"security-modernization",[1048],"security","Are you trading speed for security?",{"text":1051,"config":1052},"Get your security maturity score",{"href":1053,"dataGaName":1029,"dataGaLocation":493},"/assessments/security-modernization-assessment/",{"config":1055},{"src":1056},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1058,"paths":1059,"header":1062,"text":1063,"button":1064,"image":1069},"github-azure-migration",[1060,1061],"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":1065,"config":1066},"See how GitLab compares to GitHub",{"href":1067,"dataGaName":1068,"dataGaLocation":493},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1070},{"src":1044},{"header":1072,"blurb":1073,"button":1074,"secondaryButton":1079},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1075,"config":1076},"Get your free trial",{"href":1077,"dataGaName":292,"dataGaLocation":1078},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":766,"config":1080},{"href":618,"dataGaName":297,"dataGaLocation":1078},1786803753591]