[{"data":1,"prerenderedAt":1304},["ShallowReactive",2],{"/blog/gitlab-todos-feature-highlight":3,"navigation-en-us":520,"banner-en-us":947,"footer-en-us":957,"blog-post-authors-en-us-Douglas Alexandre":1201,"blog-related-posts-en-us-gitlab-todos-feature-highlight":1216,"blog-promotions-en-us":1240,"next-steps-en-us":1294},{"id":4,"title":5,"authors":6,"body":8,"category":496,"date":497,"description":498,"extension":499,"externalUrl":500,"faq":500,"featured":501,"heroImage":502,"meta":503,"navigation":215,"path":511,"seo":512,"slug":516,"stem":517,"tags":500,"template":518,"updatedDate":500,"__hash__":519},"blogPosts/en-us/blog/gitlab-todos-feature-highlight.md","Feature Highlight: Todos",[7],"Douglas Alexandre",{"type":9,"value":10,"toc":490},"minimark",[11,22,27,30,38,45,48,56,59,62,65,69,78,81,89,92,95,98,101,107,116,121,128,135,138,341,352,355,414,421,430,433,437,440,443,446,450,453,456,475,480,486],[12,13,14,15,21],"p",{},"In this blog post I'll guide you through the \"Todos\" feature we introduced in\n",[16,17,20],"a",{"href":18,"rel":19},"https://docs.gitlab.com/releases/",[],"GitLab 8.5","\nand also tell you how this feature came to life.\nWe'll also talk about how you can contribute to GitLab.",[23,24,26],"h3",{"id":25},"first-lets-look-at-this-feature","First let’s look at this feature",[12,28,29],{},"When you log into GitLab, you'll want to see where you should spend your time,\nwhere your team members need help, where you need to take some action, or what\nyou need to keep an eye on. All without the mess of a huge pile of e-mail\nnotifications.",[12,31,32,33,37],{},"When an issue or merge request is assigned to you, or when you are ",[34,35,36],"code",{},"@mentioned","\nin a comment, this triggers a notification and you may get an e-mail, depending\non your notification settings. Starting from GitLab 8.5, these actions will\nalso add a notice in your Todos list, which you can access by clicking the\nround gray icon next to the search bar in the upper right corner.",[12,39,40],{},[41,42],"img",{"alt":43,"src":44},"Todos screenshot showing a list of items to check on","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399228/blog/Content%20Images/todos-screenshot.jpg",[12,46,47],{},"You can then respond to the todo by performing an action to the corresponding\nissue or merge request. This action can include changing the milestone, adding\na label, commenting on the issue and pretty much everything that adds extra\ninformation to the issue/merge request.",[12,49,50,51,55],{},"In case where you think no action is needed, you can manually mark the todo as\n",[52,53,54],"strong",{},"Done"," and it will disappear from your Todos list.",[12,57,58],{},"In order for a todo to be marked as done, the action must be coming from you.\nSo, if you close/merge the related issue or merge request yourself, and you had\na todo for that, it will automatically get marked as done. On the other hand,if\nsomeone else closes, merges or takes action on the issue or merge request, your\ntodo will remain pending. This makes sense because you may need to give\nattention to an issue even if it has been resolved.",[12,60,61],{},"Another thing is that you won't have a gazillion Todos about the same issue or\nmerge request. There is just one todo per issue or merge request, you won’t be\nannoyed repeatedly.",[12,63,64],{},"Next, let’s see how this feature came to life.",[23,66,68],{"id":67},"how-we-developed-this-feature","How we developed this feature",[12,70,71,72,77],{},"It was just a few weeks before the 8.5 release when\n",[16,73,76],{"href":74,"rel":75},"https://twitter.com/dzaporozhets",[],"Dmitry"," asked me to work on this feature.\nIn fact, that was a lot of work to be done in such a short time and I actually\ndelivered this task on the last release candidate before our release day.\nAs it turned out, people were quite surprised and happy to have this feature in\n8.5.",[12,79,80],{},"There had been a long discussion about this feature, but no one had touched it\nyet. The original proposal was very complicated, there were a lot of comments\non the issue too, with a more active discussion in the last two months.",[12,82,83,84,88],{},"Finally, after considering all the options,\n[Dmitry commented on the issue]",[85,86,87],"span",{},"comment"," and greatly simplified it.",[12,90,91],{},"I was excited about taking on the challenge. Not only because I knew it would\nbe an important feature, but it would also be a feature that most people will\nuse every day. This is the second major feature I've worked on since I started\nat GitLab in December. Previously, I had worked on the GitHub import project\nwhich came out in GitLab 8.4.",[12,93,94],{},"I needed to learn quite a lot of the GitLab codebase in order to create this\nfeature. And then, while I was reading the code, I was surprised to discover it\nwas going to be much easier than I thought. My epiphany was that we already had\nthe Activity feed system in place which I could reuse. As it turned out, it was\ngoing to be very easy to implement this feature.",[12,96,97],{},"At the beginning, I spent the first week reading the code, understanding how\nthe system worked, discovering the points that we needed to trigger a todo. I\nwrote some guidelines about what I needed to achieve and then I started writing\nthe actual code by the end of the week. Yep, most of the coding was done in one\nweek.",[12,99,100],{},"At first, I needed to investigate how the Activity feed system works.",[12,102,103],{},[41,104],{"alt":105,"src":106},"Screenshot of activity feed","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397921/blog/Content%20Images/activity-feed-screenshot.jpg",[12,108,109,110,112,113,115],{},"Then, I plugged in another service to trigger the todo when an issue or merge\nrequest is assigned to someone, or when someone is ",[34,111,36],{}," in a comment or\nthe issue description. After this, I just needed to make sure that when someone\ncompleted whatever they thought is needed on the issue or merge request, the\ntodo will be marked as ",[52,114,54],{},".",[12,117,118,119,115],{},"Depending on the action that someone made on the issue or merge request, we\nneed to know exactly what attributes were changed. This is specially important\nto know when Todos are automatically marked as ",[52,120,54],{},[12,122,123,124,127],{},"To know what changed inside a issue or merge request, we used the\n[Rails ActiveModel::Dirty API]",[85,125,126],{},"dirty"," that allows us to quick track what\nattributes on a model have changed, even after the model was saved.",[12,129,130,131,134],{},"The ",[34,132,133],{},"ActiveModel::Dirty"," works very well, except that it doesn't track changes\non associations. So how we determine what changed inside the association? For\nexample, how to check if a label was added, or removed from an issue or merge\nrequest? To accomplish this, we came with a boring solution, where we receive\nthe old items of the association through a parameter, and compare it with\nthe current association to check if something has changed.",[12,136,137],{},"The method that checks if at least one valid attribute has changed, is the\nfollowing:",[139,140,145],"pre",{"className":141,"code":142,"language":143,"meta":144,"style":144},"language-ruby shiki shiki-themes github-light","def has_changes?(issuable, options = {})\n  valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch]\n\n  attrs_changed = valid_attrs.any? do |attr|\n    issuable.previous_changes.include?(attr.to_s)\n  end\n\n  old_labels = options[:old_labels]\n  labels_changed = old_labels && issuable.labels != old_labels\n\n  attrs_changed || labels_changed\nend\n","ruby","",[34,146,147,169,210,217,237,265,271,276,292,318,323,335],{"__ignoreMap":144},[85,148,151,155,159,163,166],{"class":149,"line":150},"line",1,[85,152,154],{"class":153},"sD7c4","def",[85,156,158],{"class":157},"s7eDp"," has_changes?",[85,160,162],{"class":161},"sgsFI","(issuable, options ",[85,164,165],{"class":153},"=",[85,167,168],{"class":161}," {})\n",[85,170,172,176,179,182,186,189,192,194,197,199,202,204,207],{"class":149,"line":171},2,[85,173,175],{"class":174},"sqxcx","  valid_attrs",[85,177,178],{"class":153}," =",[85,180,181],{"class":161}," [",[85,183,185],{"class":184},"sYu0t",":title",[85,187,188],{"class":161},", ",[85,190,191],{"class":184},":description",[85,193,188],{"class":161},[85,195,196],{"class":184},":assignee_id",[85,198,188],{"class":161},[85,200,201],{"class":184},":milestone_id",[85,203,188],{"class":161},[85,205,206],{"class":184},":target_branch",[85,208,209],{"class":161},"]\n",[85,211,213],{"class":149,"line":212},3,[85,214,216],{"emptyLinePlaceholder":215},true,"\n",[85,218,220,223,225,228,231,234],{"class":149,"line":219},4,[85,221,222],{"class":174},"  attrs_changed",[85,224,178],{"class":153},[85,226,227],{"class":161}," valid_attrs.",[85,229,230],{"class":157},"any?",[85,232,233],{"class":153}," do",[85,235,236],{"class":161}," |attr|\n",[85,238,240,243,246,248,251,254,257,259,262],{"class":149,"line":239},5,[85,241,242],{"class":161},"    issuable.",[85,244,245],{"class":157},"previous_changes",[85,247,115],{"class":161},[85,249,250],{"class":157},"include?",[85,252,253],{"class":161},"(",[85,255,256],{"class":153},"attr",[85,258,115],{"class":161},[85,260,261],{"class":157},"to_s",[85,263,264],{"class":161},")\n",[85,266,268],{"class":149,"line":267},6,[85,269,270],{"class":153},"  end\n",[85,272,274],{"class":149,"line":273},7,[85,275,216],{"emptyLinePlaceholder":215},[85,277,279,282,284,287,290],{"class":149,"line":278},8,[85,280,281],{"class":174},"  old_labels",[85,283,178],{"class":153},[85,285,286],{"class":161}," options[",[85,288,289],{"class":184},":old_labels",[85,291,209],{"class":161},[85,293,295,298,300,303,306,309,312,315],{"class":149,"line":294},9,[85,296,297],{"class":174},"  labels_changed",[85,299,178],{"class":153},[85,301,302],{"class":161}," old_labels ",[85,304,305],{"class":153},"&&",[85,307,308],{"class":161}," issuable.",[85,310,311],{"class":157},"labels",[85,313,314],{"class":153}," !=",[85,316,317],{"class":161}," old_labels\n",[85,319,321],{"class":149,"line":320},10,[85,322,216],{"emptyLinePlaceholder":215},[85,324,326,329,332],{"class":149,"line":325},11,[85,327,328],{"class":161},"  attrs_changed ",[85,330,331],{"class":153},"||",[85,333,334],{"class":161}," labels_changed\n",[85,336,338],{"class":149,"line":337},12,[85,339,340],{"class":153},"end\n",[12,342,343,344,348,349,351],{},"A quick note about this code snippet is that it will run ",[345,346,347],"em",{},"after"," the model was\nsaved, so ",[34,350,245],{}," is the method that we need to call, and it returns\na hash with the attributes that were changed before the model was saved.",[12,353,354],{},"We can see on the following snippet that other GitLab features, like\nthe system note and the notification service, also use this API to trigger an\naction after certain attributes have changed:",[139,356,358],{"className":141,"code":357,"language":143,"meta":144,"style":144},"if issue.previous_changes.include?('assignee_id')\n  create_assignee_note(issue)\n  notification_service.reassigned_issue(issue, current_user)\n  todo_service.reassigned_issue(issue, current_user)\nend\n",[34,359,360,382,390,401,410],{"__ignoreMap":144},[85,361,362,365,368,370,372,374,376,380],{"class":149,"line":150},[85,363,364],{"class":153},"if",[85,366,367],{"class":161}," issue.",[85,369,245],{"class":157},[85,371,115],{"class":161},[85,373,250],{"class":157},[85,375,253],{"class":161},[85,377,379],{"class":378},"sYBdl","'assignee_id'",[85,381,264],{"class":161},[85,383,384,387],{"class":149,"line":171},[85,385,386],{"class":157},"  create_assignee_note",[85,388,389],{"class":161},"(issue)\n",[85,391,392,395,398],{"class":149,"line":212},[85,393,394],{"class":161},"  notification_service.",[85,396,397],{"class":157},"reassigned_issue",[85,399,400],{"class":161},"(issue, current_user)\n",[85,402,403,406,408],{"class":149,"line":219},[85,404,405],{"class":161},"  todo_service.",[85,407,397],{"class":157},[85,409,400],{"class":161},[85,411,412],{"class":149,"line":239},[85,413,340],{"class":153},[12,415,416,417,420],{},"To get an idea about the points that trigger an action related to Todos,\nyou can take a look at the source code of the ",[85,418,419],{},"TodoService"," class, that\ndescribes the expected behavior for each of these points.",[12,422,423,424,429],{},"My colleague, ",[16,425,428],{"href":426,"rel":427},"https://twitter.com/DouweM",[],"Douwe",", guided me through some of\nthese edge cases and helped me a lot with daily code reviews. Together we\nconsidered the aspects of the functionality. I had to make many changes during\nthe review period, but that worked out well in the end.",[12,431,432],{},"Some people asked for changes which we thought didn't fit for what we were\ntrying to achieve. There was for example a request to be notified of Todos on\ncommits. We ultimately decided against this request since there are no clear\nactions to take on a commit, except for replying to a comment. Most of the time\nyou’re actually working on an issue or a merge request, this is where the action\nhappens.",[23,434,436],{"id":435},"about-that-name","About that name",[12,438,439],{},"There was a long discussion about naming the feature. In the beginning this\nfeature was called 'Notification System'. Then 'Action Center'. Then 'Todos'.\nThen 'Tasks', and finally 'Task Queue' was chosen.",[12,441,442],{},"But Douwe and Sytse discussed the naming for the umpteenth time, and concluded\nthat 'Todos' would be the best name since 'Tasks' is already used (by us and\nGitHub) for the Markdown checklist.",[12,444,445],{},"Todo makes more sense to me than a Task, you just need to take a look at it.\nTasks are obligations. A Todo is just a reminder, and as such, maybe it is\nrelevant for you and you'll take a look or maybe you'll just mark it as done.",[23,447,449],{"id":448},"conclusion","Conclusion",[12,451,452],{},"I'm really happy to have worked on a feature that people will use every day!\nIt's a simple feature and even though we've only been using it a few days,\nwe can't imagine how we managed to work without it.",[12,454,455],{},"This Tweet from a user pretty much sums it up.",[457,458,462,470,471],"blockquote",{"className":459,"dataLang":461},[460],"twitter-tweet","en",[12,463,115,465,469],{"lang":461,"dir":464},"ltr",[16,466,468],{"href":467},"https://twitter.com/gitlab","@gitlab","'s new ToDos feature is simply amazing. Simple feature but something you cannot live without once you tried it.","— Tero Tasanen (@ttasanen) ",[16,472,474],{"href":473},"https://twitter.com/ttasanen/status/702249244950974464","February 23, 2016",[476,477],"script",{"async":215,"src":478,"charSet":479},"//platform.twitter.com/widgets.js","utf-8",[12,481,482,483,115],{},"For more information you can [read our documentation]",[85,484,485],{},"doc",[487,488,489],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}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 .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}",{"title":144,"searchDepth":171,"depth":171,"links":491},[492,493,494,495],{"id":25,"depth":212,"text":26},{"id":67,"depth":212,"text":68},{"id":435,"depth":212,"text":436},{"id":448,"depth":212,"text":449},"company","2016-03-02","In this blog post I'll guide you through the Todos feature we introduced in GitLab 8.5 and also tell you how this feature came to life.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749684608/Blog/Hero%20Images/taking-notes.jpg",{"excerpt":504},{"type":9,"value":505},[506],[12,507,14,508,21],{},[16,509,20],{"href":18,"rel":510},[],"/en-us/blog/gitlab-todos-feature-highlight",{"title":5,"description":498,"ogTitle":5,"ogDescription":498,"noIndex":501,"ogImage":502,"ogUrl":513,"ogSiteName":514,"ogType":515,"canonicalUrls":513},"https://about.gitlab.com/blog/gitlab-todos-feature-highlight","https://about.gitlab.com","article","gitlab-todos-feature-highlight","en-us/blog/gitlab-todos-feature-highlight","BlogPost","EYi29tKbyUSEONs_BUkuWR5xL8869r6mkPhk53c8Ik4",{"logo":521,"freeTrial":526,"sales":531,"login":536,"items":541,"search":867,"minimal":898,"duo":917,"switchNav":926,"pricingDeployment":937},{"config":522},{"href":523,"dataGaName":524,"dataGaLocation":525},"/","gitlab logo","header",{"text":527,"config":528},"Get free trial",{"href":529,"dataGaName":530,"dataGaLocation":525},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":532,"config":533},"Request a demo",{"href":534,"dataGaName":535,"dataGaLocation":525},"/sales/?contact-topic=request-demo","sales",{"text":537,"config":538},"Sign in",{"href":539,"dataGaName":540,"dataGaLocation":525},"https://gitlab.com/users/sign_in/","sign in",[542,571,671,676,790,845],{"text":543,"config":544,"menu":546},"Platform",{"dataNavLevelOne":545},"platform",{"type":547,"columns":548},"cards",[549,555,563],{"title":543,"description":550,"link":551},"The intelligent orchestration platform for DevSecOps",{"text":552,"config":553},"Explore our Platform",{"href":554,"dataGaName":545,"dataGaLocation":525},"/platform/",{"title":556,"description":557,"link":558},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":559,"config":560},"Meet GitLab Duo",{"href":561,"dataGaName":562,"dataGaLocation":525},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":564,"description":565,"link":566},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":567,"config":568},"Learn more",{"href":569,"dataGaName":570,"dataGaLocation":525},"/why-gitlab/","why gitlab",{"text":572,"left":215,"config":573,"menu":575},"Product",{"dataNavLevelOne":574},"solutions",{"type":576,"link":577,"columns":581,"feature":650},"lists",{"text":578,"config":579},"View all Solutions",{"href":580,"dataGaName":574,"dataGaLocation":525},"/solutions/",[582,606,629],{"title":583,"description":584,"link":585,"items":590},"Automation","CI/CD and automation to accelerate deployment",{"config":586},{"icon":587,"href":588,"dataGaName":589,"dataGaLocation":525},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[591,595,598,602],{"text":592,"config":593},"CI/CD",{"href":594,"dataGaLocation":525,"dataGaName":592},"/solutions/continuous-integration/",{"text":556,"config":596},{"href":561,"dataGaLocation":525,"dataGaName":597},"gitlab duo agent platform - product menu",{"text":599,"config":600},"Source Code Management",{"href":601,"dataGaLocation":525,"dataGaName":599},"/solutions/source-code-management/",{"text":603,"config":604},"Automated Software Delivery",{"href":588,"dataGaLocation":525,"dataGaName":605},"Automated software delivery",{"title":607,"description":608,"link":609,"items":614},"Security","Deliver code faster without compromising security",{"config":610},{"href":611,"dataGaName":612,"dataGaLocation":525,"icon":613},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[615,619,624],{"text":616,"config":617},"Application Security Testing",{"href":611,"dataGaName":618,"dataGaLocation":525},"Application security testing",{"text":620,"config":621},"Software Supply Chain Security",{"href":622,"dataGaLocation":525,"dataGaName":623},"/solutions/supply-chain/","Software supply chain security",{"text":625,"config":626},"Software Compliance",{"href":627,"dataGaName":628,"dataGaLocation":525},"/solutions/software-compliance/","software compliance",{"title":630,"link":631,"items":636},"Measurement",{"config":632},{"icon":633,"href":634,"dataGaName":635,"dataGaLocation":525},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[637,641,645],{"text":638,"config":639},"Visibility & Measurement",{"href":634,"dataGaLocation":525,"dataGaName":640},"Visibility and Measurement",{"text":642,"config":643},"Value Stream Management",{"href":644,"dataGaLocation":525,"dataGaName":642},"/solutions/value-stream-management/",{"text":646,"config":647},"Analytics & Insights",{"href":648,"dataGaLocation":525,"dataGaName":649},"/solutions/analytics-and-insights/","Analytics and insights",{"title":651,"type":576,"items":652},"GitLab for",[653,659,665],{"text":654,"config":655},"Enterprise",{"icon":656,"href":657,"dataGaLocation":525,"dataGaName":658},"Building","/enterprise/","enterprise",{"text":660,"config":661},"Small Business",{"icon":662,"href":663,"dataGaLocation":525,"dataGaName":664},"Work","/small-business/","small business",{"text":666,"config":667},"Public Sector",{"icon":668,"href":669,"dataGaLocation":525,"dataGaName":670},"Organization","/solutions/public-sector/","public sector",{"text":672,"config":673},"Pricing",{"href":674,"dataGaName":675,"dataGaLocation":525,"dataNavLevelOne":675},"/pricing/","pricing",{"text":677,"config":678,"menu":680},"Resources",{"dataNavLevelOne":679},"resources",{"type":576,"link":681,"columns":685,"feature":779},{"text":682,"config":683},"View all resources",{"href":684,"dataGaName":679,"dataGaLocation":525},"/resources/",[686,719,746],{"title":687,"items":688},"Getting started",[689,694,699,704,709,714],{"text":690,"config":691},"Install",{"href":692,"dataGaName":693,"dataGaLocation":525},"/install/","install",{"text":695,"config":696},"Quick start guides",{"href":697,"dataGaName":698,"dataGaLocation":525},"/get-started/","quick setup checklists",{"text":700,"config":701},"Learn",{"href":702,"dataGaLocation":525,"dataGaName":703},"https://university.gitlab.com/","learn",{"text":705,"config":706},"Product documentation",{"href":707,"dataGaName":708,"dataGaLocation":525},"https://docs.gitlab.com/","product documentation",{"text":710,"config":711},"Best practice videos",{"href":712,"dataGaName":713,"dataGaLocation":525},"/getting-started-videos/","best practice videos",{"text":715,"config":716},"Integrations",{"href":717,"dataGaName":718,"dataGaLocation":525},"/integrations/","integrations",{"title":720,"items":721},"Discover",[722,727,732,737,741],{"text":723,"config":724},"Customer success stories",{"href":725,"dataGaName":726,"dataGaLocation":525},"/customers/","customer success stories",{"text":728,"config":729},"Blog",{"href":730,"dataGaName":731,"dataGaLocation":525},"/blog/","blog",{"text":733,"config":734},"Demo Hub",{"href":735,"dataGaName":736,"dataGaLocation":525},"/demo-hub/","demo hub",{"text":738,"config":739},"The Source",{"href":740,"dataGaName":731,"dataGaLocation":525},"/the-source/",{"text":742,"config":743},"Remote",{"href":744,"dataGaName":745,"dataGaLocation":525},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":747,"items":748},"Connect",[749,754,759,764,769,774],{"text":750,"config":751},"GitLab Services",{"href":752,"dataGaName":753,"dataGaLocation":525},"/services/","services",{"text":755,"config":756},"Contribute",{"href":757,"dataGaName":758,"dataGaLocation":525},"https://contributors.gitlab.com","contribute",{"text":760,"config":761},"Community",{"href":762,"dataGaName":763,"dataGaLocation":525},"/community/","community",{"text":765,"config":766},"Forum",{"href":767,"dataGaName":768,"dataGaLocation":525},"https://forum.gitlab.com/","forum",{"text":770,"config":771},"Events",{"href":772,"dataGaName":773,"dataGaLocation":525},"/events/","events",{"text":775,"config":776},"Partners",{"href":777,"dataGaName":778,"dataGaLocation":525},"/partners/","partners",{"config":780,"title":783,"text":784,"link":785},{"background":781,"textColor":782},"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":786,"config":787},"Read the latest",{"href":788,"dataGaName":789,"dataGaLocation":525},"/whats-new/","whats new",{"text":791,"config":792,"menu":793},"Company",{"dataNavLevelOne":496},{"type":576,"columns":794},[795],{"items":796},[797,802,808,810,815,820,825,830,835,840],{"text":798,"config":799},"About",{"href":800,"dataGaName":801,"dataGaLocation":525},"/company/","about",{"text":803,"config":804,"footerGa":807},"Jobs",{"href":805,"dataGaName":806,"dataGaLocation":525},"/jobs/","jobs",{"dataGaName":806},{"text":770,"config":809},{"href":772,"dataGaName":773,"dataGaLocation":525},{"text":811,"config":812},"Leadership",{"href":813,"dataGaName":814,"dataGaLocation":525},"/company/team/e-group/","leadership",{"text":816,"config":817},"Handbook",{"href":818,"dataGaName":819,"dataGaLocation":525},"https://handbook.gitlab.com/","handbook",{"text":821,"config":822},"Investor relations",{"href":823,"dataGaName":824,"dataGaLocation":525},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":826,"config":827},"Trust Center",{"href":828,"dataGaName":829,"dataGaLocation":525},"/security/","trust center",{"text":831,"config":832},"AI Transparency Center",{"href":833,"dataGaName":834,"dataGaLocation":525},"/ai-transparency-center/","ai transparency center",{"text":836,"config":837},"Newsletter",{"href":838,"dataGaName":839,"dataGaLocation":525},"/company/contact/#contact-forms","newsletter",{"text":841,"config":842},"Press",{"href":843,"dataGaName":844,"dataGaLocation":525},"/press/","press",{"text":846,"config":847,"menu":848},"Contact us",{"dataNavLevelOne":496},{"type":576,"columns":849},[850],{"items":851},[852,857,862],{"text":853,"config":854},"Talk to sales",{"href":855,"dataGaName":856,"dataGaLocation":525},"/sales/","talk to sales",{"text":858,"config":859},"Support portal",{"href":860,"dataGaName":861,"dataGaLocation":525},"https://support.gitlab.com/hc/en-us","support portal",{"text":863,"config":864},"Customer portal",{"href":865,"dataGaName":866,"dataGaLocation":525},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":868,"login":869,"suggestions":876},"Close",{"text":870,"link":871},"To search repositories and projects, login to",{"text":872,"config":873},"gitlab.com",{"href":539,"dataGaName":874,"dataGaLocation":875},"search login","search",{"text":877,"default":878},"Suggestions",[879,881,885,887,891,895],{"text":556,"config":880},{"href":561,"dataGaName":556,"dataGaLocation":875},{"text":882,"config":883},"Code Suggestions (AI)",{"href":884,"dataGaName":882,"dataGaLocation":875},"/solutions/code-suggestions/",{"text":592,"config":886},{"href":594,"dataGaName":592,"dataGaLocation":875},{"text":888,"config":889},"GitLab on AWS",{"href":890,"dataGaName":888,"dataGaLocation":875},"/partners/technology-partners/aws/",{"text":892,"config":893},"GitLab on Google Cloud",{"href":894,"dataGaName":892,"dataGaLocation":875},"/partners/technology-partners/google-cloud-platform/",{"text":896,"config":897},"Why GitLab?",{"href":569,"dataGaName":896,"dataGaLocation":875},{"freeTrial":899,"mobileIcon":904,"desktopIcon":909,"secondaryButton":912},{"text":900,"config":901},"Start free trial",{"href":902,"dataGaName":530,"dataGaLocation":903},"https://gitlab.com/-/trials/new/","nav",{"altText":905,"config":906},"Gitlab Icon",{"src":907,"dataGaName":908,"dataGaLocation":903},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":905,"config":910},{"src":911,"dataGaName":908,"dataGaLocation":903},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":913,"config":914},"Get Started",{"href":915,"dataGaName":916,"dataGaLocation":903},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":918,"mobileIcon":922,"desktopIcon":924},{"text":919,"config":920},"Learn more about GitLab Duo",{"href":561,"dataGaName":921,"dataGaLocation":903},"gitlab duo",{"altText":905,"config":923},{"src":907,"dataGaName":908,"dataGaLocation":903},{"altText":905,"config":925},{"src":911,"dataGaName":908,"dataGaLocation":903},{"button":927,"mobileIcon":932,"desktopIcon":934},{"text":928,"config":929},"/switch",{"href":930,"dataGaName":931,"dataGaLocation":903},"#contact","switch",{"altText":905,"config":933},{"src":907,"dataGaName":908,"dataGaLocation":903},{"altText":905,"config":935},{"src":936,"dataGaName":908,"dataGaLocation":903},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":938,"mobileIcon":943,"desktopIcon":945},{"text":939,"config":940},"Back to pricing",{"href":674,"dataGaName":941,"dataGaLocation":903,"icon":942},"back to pricing","GoBack",{"altText":905,"config":944},{"src":907,"dataGaName":908,"dataGaLocation":903},{"altText":905,"config":946},{"src":911,"dataGaName":908,"dataGaLocation":903},{"title":948,"titleMobile":949,"button":950,"config":955},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":567,"config":951},{"href":952,"dataGaName":953,"dataGaLocation":954},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":956,"disabled":501},"release",{"data":958},{"text":959,"source":960,"edit":966,"contribute":971,"config":976,"items":980,"minimal":1190},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":961,"config":962},"View page source",{"href":963,"dataGaName":964,"dataGaLocation":965},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":967,"config":968},"Edit this page",{"href":969,"dataGaName":970,"dataGaLocation":965},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":972,"config":973},"Please contribute",{"href":974,"dataGaName":975,"dataGaLocation":965},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":467,"facebook":977,"youtube":978,"linkedin":979},"https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[981,1028,1082,1126,1158],{"title":672,"links":982,"subMenu":997},[983,987,992],{"text":984,"config":985},"View plans",{"href":674,"dataGaName":986,"dataGaLocation":965},"view plans",{"text":988,"config":989},"Why Premium?",{"href":990,"dataGaName":991,"dataGaLocation":965},"/pricing/premium/","why premium",{"text":993,"config":994},"Why Ultimate?",{"href":995,"dataGaName":996,"dataGaLocation":965},"/pricing/ultimate/","why ultimate",[998],{"title":999,"links":1000},"Contact Us",[1001,1004,1006,1008,1013,1018,1023],{"text":1002,"config":1003},"Contact sales",{"href":855,"dataGaName":535,"dataGaLocation":965},{"text":858,"config":1005},{"href":860,"dataGaName":861,"dataGaLocation":965},{"text":863,"config":1007},{"href":865,"dataGaName":866,"dataGaLocation":965},{"text":1009,"config":1010},"Status",{"href":1011,"dataGaName":1012,"dataGaLocation":965},"https://status.gitlab.com/","status",{"text":1014,"config":1015},"Terms of use",{"href":1016,"dataGaName":1017,"dataGaLocation":965},"/terms/","terms of use",{"text":1019,"config":1020},"Privacy statement",{"href":1021,"dataGaName":1022,"dataGaLocation":965},"/privacy/","privacy statement",{"text":1024,"config":1025},"Cookie preferences",{"dataGaName":1026,"dataGaLocation":965,"id":1027,"isOneTrustButton":215},"cookie preferences","ot-sdk-btn",{"title":572,"links":1029,"subMenu":1038},[1030,1034],{"text":1031,"config":1032},"DevSecOps platform",{"href":554,"dataGaName":1033,"dataGaLocation":965},"devsecops platform",{"text":1035,"config":1036},"AI-Assisted Development",{"href":561,"dataGaName":1037,"dataGaLocation":965},"ai-assisted development",[1039],{"title":1040,"links":1041},"Topics",[1042,1047,1052,1057,1062,1067,1072,1077],{"text":1043,"config":1044},"CICD",{"href":1045,"dataGaName":1046,"dataGaLocation":965},"/topics/ci-cd/","cicd",{"text":1048,"config":1049},"GitOps",{"href":1050,"dataGaName":1051,"dataGaLocation":965},"/topics/gitops/","gitops",{"text":1053,"config":1054},"DevOps",{"href":1055,"dataGaName":1056,"dataGaLocation":965},"/topics/devops/","devops",{"text":1058,"config":1059},"Version Control",{"href":1060,"dataGaName":1061,"dataGaLocation":965},"/topics/version-control/","version control",{"text":1063,"config":1064},"DevSecOps",{"href":1065,"dataGaName":1066,"dataGaLocation":965},"/topics/devsecops/","devsecops",{"text":1068,"config":1069},"Cloud Native",{"href":1070,"dataGaName":1071,"dataGaLocation":965},"/topics/cloud-native/","cloud native",{"text":1073,"config":1074},"AI for Coding",{"href":1075,"dataGaName":1076,"dataGaLocation":965},"/topics/devops/ai-for-coding/","ai for coding",{"text":1078,"config":1079},"Agentic AI",{"href":1080,"dataGaName":1081,"dataGaLocation":965},"/topics/agentic-ai/","agentic ai",{"title":1083,"links":1084},"Solutions",[1085,1087,1089,1094,1098,1101,1105,1108,1110,1113,1116,1121],{"text":616,"config":1086},{"href":611,"dataGaName":616,"dataGaLocation":965},{"text":605,"config":1088},{"href":588,"dataGaName":589,"dataGaLocation":965},{"text":1090,"config":1091},"Agile development",{"href":1092,"dataGaName":1093,"dataGaLocation":965},"/solutions/agile-delivery/","agile delivery",{"text":1095,"config":1096},"SCM",{"href":601,"dataGaName":1097,"dataGaLocation":965},"source code management",{"text":1043,"config":1099},{"href":594,"dataGaName":1100,"dataGaLocation":965},"continuous integration & delivery",{"text":1102,"config":1103},"Value stream management",{"href":644,"dataGaName":1104,"dataGaLocation":965},"value stream management",{"text":1048,"config":1106},{"href":1107,"dataGaName":1051,"dataGaLocation":965},"/solutions/gitops/",{"text":654,"config":1109},{"href":657,"dataGaName":658,"dataGaLocation":965},{"text":1111,"config":1112},"Small business",{"href":663,"dataGaName":664,"dataGaLocation":965},{"text":1114,"config":1115},"Public sector",{"href":669,"dataGaName":670,"dataGaLocation":965},{"text":1117,"config":1118},"Education",{"href":1119,"dataGaName":1120,"dataGaLocation":965},"/solutions/education/","education",{"text":1122,"config":1123},"Financial services",{"href":1124,"dataGaName":1125,"dataGaLocation":965},"/solutions/finance/","financial services",{"title":677,"links":1127},[1128,1130,1132,1134,1137,1139,1142,1144,1146,1148,1150,1152,1154,1156],{"text":690,"config":1129},{"href":692,"dataGaName":693,"dataGaLocation":965},{"text":695,"config":1131},{"href":697,"dataGaName":698,"dataGaLocation":965},{"text":700,"config":1133},{"href":702,"dataGaName":703,"dataGaLocation":965},{"text":705,"config":1135},{"href":707,"dataGaName":1136,"dataGaLocation":965},"docs",{"text":728,"config":1138},{"href":730,"dataGaName":731,"dataGaLocation":965},{"text":1140,"config":1141},"What's new",{"href":788,"dataGaName":789,"dataGaLocation":965},{"text":723,"config":1143},{"href":725,"dataGaName":726,"dataGaLocation":965},{"text":742,"config":1145},{"href":744,"dataGaName":745,"dataGaLocation":965},{"text":750,"config":1147},{"href":752,"dataGaName":753,"dataGaLocation":965},{"text":755,"config":1149},{"href":757,"dataGaName":758,"dataGaLocation":965},{"text":760,"config":1151},{"href":762,"dataGaName":763,"dataGaLocation":965},{"text":765,"config":1153},{"href":767,"dataGaName":768,"dataGaLocation":965},{"text":770,"config":1155},{"href":772,"dataGaName":773,"dataGaLocation":965},{"text":775,"config":1157},{"href":777,"dataGaName":778,"dataGaLocation":965},{"title":791,"links":1159},[1160,1162,1164,1166,1168,1170,1174,1179,1181,1183,1185],{"text":798,"config":1161},{"href":800,"dataGaName":496,"dataGaLocation":965},{"text":803,"config":1163},{"href":805,"dataGaName":806,"dataGaLocation":965},{"text":811,"config":1165},{"href":813,"dataGaName":814,"dataGaLocation":965},{"text":816,"config":1167},{"href":818,"dataGaName":819,"dataGaLocation":965},{"text":821,"config":1169},{"href":823,"dataGaName":824,"dataGaLocation":965},{"text":1171,"config":1172},"Sustainability",{"href":1173,"dataGaName":1171,"dataGaLocation":965},"/sustainability/",{"text":1175,"config":1176},"Diversity, inclusion and belonging (DIB)",{"href":1177,"dataGaName":1178,"dataGaLocation":965},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":826,"config":1180},{"href":828,"dataGaName":829,"dataGaLocation":965},{"text":836,"config":1182},{"href":838,"dataGaName":839,"dataGaLocation":965},{"text":841,"config":1184},{"href":843,"dataGaName":844,"dataGaLocation":965},{"text":1186,"config":1187},"Modern Slavery Transparency Statement",{"href":1188,"dataGaName":1189,"dataGaLocation":965},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1191},[1192,1195,1198],{"text":1193,"config":1194},"Terms",{"href":1016,"dataGaName":1017,"dataGaLocation":965},{"text":1196,"config":1197},"Cookies",{"dataGaName":1026,"dataGaLocation":965,"id":1027,"isOneTrustButton":215},{"text":1199,"config":1200},"Privacy",{"href":1021,"dataGaName":1022,"dataGaLocation":965},[1202],{"id":1203,"title":7,"body":500,"config":1204,"content":1206,"description":500,"extension":1210,"meta":1211,"navigation":215,"path":1212,"seo":1213,"stem":1214,"__hash__":1215},"blogAuthors/en-us/blog/authors/douglas-alexandre.yml",{"template":1205},"BlogAuthor",{"name":7,"config":1207},{"headshot":1208,"ctfId":1209},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Douglas-Alexandre","yml",{},"/en-us/blog/authors/douglas-alexandre",{},"en-us/blog/authors/douglas-alexandre","9oIVDz8T3S38e4STEdDnYFoN7NNjji5DBqDlbWomMxo",[1217,1225,1232],{"title":1218,"description":1219,"heroImage":1220,"category":496,"date":1221,"authors":1222,"slug":1224,"externalUrl":500},"Why GitLab signed the Open Weights and American AI Leadership letter","GitLab supports customer choice, data privacy, and AI model neutrality, empowering teams to run secure, cost-effective, local solutions.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1785333264/ww4mlighwlvvzgnmbe6w.png","2026-07-29",[1223],"Bill Staples","open-weight-model-letter",{"title":1226,"description":1227,"heroImage":1228,"category":496,"date":1229,"authors":1230,"slug":1231,"externalUrl":500},"GitLab Act 2","A letter to our customers and our investors.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1776362275/ozbwn9tk0dditpnfddlz.png","2026-05-11",[1223],"gitlab-act-2",{"title":1233,"description":1234,"heroImage":1235,"category":496,"date":1236,"authors":1237,"slug":1239,"externalUrl":500},"GitLab named a 2026 Omdia Universe Leader","Omdia's 2026 report on AI-assisted software development ranked 19 vendors. Here is what GitLab's top scores mean for engineering teams.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-13",[1238],"Rebecca Carter","gitlab-named-a-2026-omdia-universe-leader",{"promotions":1241},[1242,1256,1268,1280],{"id":1243,"categories":1244,"header":1246,"text":1247,"button":1248,"image":1253},"ai-modernization",[1245],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1249,"config":1250},"Get your AI maturity score",{"href":1251,"dataGaName":1252,"dataGaLocation":731},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1254},{"src":1255},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1257,"categories":1258,"header":1260,"text":1247,"button":1261,"image":1265},"devops-modernization",[1259,1066],"product","Are you just managing tools or shipping innovation?",{"text":1262,"config":1263},"Get your DevOps maturity score",{"href":1264,"dataGaName":1252,"dataGaLocation":731},"/assessments/devops-modernization-assessment/",{"config":1266},{"src":1267},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1269,"categories":1270,"header":1272,"text":1247,"button":1273,"image":1277},"security-modernization",[1271],"security","Are you trading speed for security?",{"text":1274,"config":1275},"Get your security maturity score",{"href":1276,"dataGaName":1252,"dataGaLocation":731},"/assessments/security-modernization-assessment/",{"config":1278},{"src":1279},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1281,"paths":1282,"header":1285,"text":1286,"button":1287,"image":1292},"github-azure-migration",[1283,1284],"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":1288,"config":1289},"See how GitLab compares to GitHub",{"href":1290,"dataGaName":1291,"dataGaLocation":731},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1293},{"src":1267},{"header":1295,"blurb":1296,"button":1297,"secondaryButton":1302},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1298,"config":1299},"Get your free trial",{"href":1300,"dataGaName":530,"dataGaLocation":1301},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1002,"config":1303},{"href":855,"dataGaName":535,"dataGaLocation":1301},1786803761268]