[{"data":1,"prerenderedAt":977},["ShallowReactive",2],{"/blog/module-level-access-in-a-django-grc-app":3,"navigation-en-us":220,"banner-en-us":646,"footer-en-us":656,"blog-post-authors-en-us-Donovan Felton":917,"blog-related-posts-en-us-module-level-access-in-a-django-grc-app":931,"blog-share-wrapper-en-us":957,"next-steps-en-us":967},{"id":4,"title":5,"authors":6,"body":8,"category":202,"date":203,"description":204,"extension":205,"externalUrl":206,"faq":206,"featured":207,"heroImage":208,"meta":209,"navigation":210,"path":211,"seo":212,"slug":214,"stem":215,"tags":216,"template":218,"updatedDate":206,"__hash__":219},"blogPosts/en-us/blog/module-level-access-in-a-django-grc-app.md","Two front doors: Module-level access in a Django GRC app",[7],"Donovan Felton",{"type":9,"value":10,"toc":192},"minimark",[11,15,18,23,31,36,39,42,46,49,52,55,59,62,65,69,80,86,101,106,117,120,124,127,130,133,159,163],[12,13,14],"p",{},"GitLab's engineering team builds a lot of our own internal tooling, but one platform in particular forced us to rethink how we handle authorization: our internal GRC tool that serves two very different customer bases under one roof — our Security Compliance team and our Internal Audit team.",[12,16,17],{},"If you're building or maintaining an internal Django tool that's starting to serve more than one audience — different teams, different data classification levels, different data that shouldn't cross streams — this post walks through the problem we hit, how we solved it, and how you can apply the same pattern to your own app.",[19,20,22],"h2",{"id":21},"the-challenge","The challenge",[12,24,25,26,30],{},"Our original tool was a single Django app built for the Security Compliance team with ",[27,28,29],"code",{},"login_required"," on every view. This was the correct approach until Internal Audit found a new use case for our tool that would include a completely separate workflow, with its own data, its own models, and its own sensitive information that Security Compliance should not see.",[12,32,33,35],{},[27,34,29],{}," answers exactly one question: Are you logged in? It doesn't answer the question that actually matters in a multi-tenant internal tool: Are you allowed to be here? An authenticated Security Compliance user hitting an Internal Audit URL waltzed right past the login check and into data they were never supposed to see.",[12,37,38],{},"If your app has ever grown a second audience after the fact, this probably sounds familiar. The question worth asking yourself right now is simple: Does every view in your app check who the user is, or just whether they're logged in? If it's the latter, you likely have the same gap we did.",[12,40,41],{},"We needed clean separation — not \"pay no attention to this button over here,\" but an enforced boundary that holds regardless of what URL someone types in.",[19,43,45],{"id":44},"the-architectural-decision","The architectural decision",[12,47,48],{},"When Internal Audit's use case landed on our roadmap, we had the classic fork in the road: Bolt it onto the existing app or restructure? We restructured.",[12,50,51],{},"The result is a core Django project — shared auth, user management, audit logging, base UI components — with two independent module apps sitting on top of it. One is for Security Compliance and one is for Internal Audit. Each module owns its own models, views, and API surface.",[12,53,54],{},"The constraint we imposed on ourselves, and the one that made this whole thing worth writing about was that core has zero knowledge of compliance or internal audit data. Either module can depend on core. Neither module should depend on the other. That constraint keeps this from becoming two apps stitched together with duct tape and super glue.",[19,56,58],{"id":57},"access-control","Access control",[12,60,61],{},"We're a compliance engineering subdivision building a tool that stores sensitive compliance and risk data. Sloppy access control here isn't just a bug, it's a serious credibility problem.",[12,63,64],{},"Our design decision was to use something already native to Django: the built-in auth system. Each module maps to exactly one group. A user can belong to one group, both, or neither, and the system enforces the right separation of duty at every layer regardless of which combination applies. The navigation UI, the Django views, and the REST API each independently enforce group membership.",[19,66,68],{"id":67},"the-implementation","The implementation",[12,70,71,72,75,76,79],{},"There is one reusable class in core that both modules inherit from: ",[27,73,74],{},"GroupRequiredMixin",". It checks group membership and raises ",[27,77,78],{},"PermissionDenied"," if the user isn't in the required group.",[12,81,82],{},[83,84,85],"strong",{},"Module-level subclasses",[12,87,88,89,92,93,96,97,100],{},"Each module gets a three-line subclass that sets ",[27,90,91],{},"required_group_name"," — a ",[27,94,95],{},"GasGroupRequiredMixin"," and an ",[27,98,99],{},"IsGasGroupMember"," for the Security Compliance side, with the same pattern repeating for Internal Audit. That's the whole cost of adding a module's enforcement layer: one new file, one new attribute. The actual checking logic lives in core and never gets touched again.",[12,102,103],{},[83,104,105],{},"The UI layer",[12,107,108,109,112,113,116],{},"Finally, there is a template filter, ",[27,110,111],{},"user_in_group",", so navigation only renders what a user is actually allowed to view. It wraps a check like ",[27,114,115],{},"user|user_in_group:'erm'"," around any nav block that should only show up for ERM members.",[12,118,119],{},"This is a UX guard, not a security boundary. Hiding a navigation link doesn't stop anyone from typing the URL directly. That's what the mixins and the Django REST Framework permission class are for. The template filter just keeps the UI honest about what a given user can see and do.",[19,121,123],{"id":122},"whats-next-for-us-and-where-you-could-start","What's next for us - and where you could start",[12,125,126],{},"Step back out to the platform level and the payoff is that this is a repeatable pattern that is module- and team-agnostic. A hypothetical third module — a vendor risk workflow, an audit management module, whatever comes next — inherits the same access control structure, the same audit trail, and the same shared authentication without a single new line of enforcement logic. The cost of a new module becomes almost entirely a business requirement, not infrastructure scaffolding.",[12,128,129],{},"That's the bet we made: Build the foundation once and let every future module ride on top of it instead of reinventing access control from scratch.",[12,131,132],{},"If you want to try this in your own app, here's roughly the order we'd suggest:",[134,135,136,147,150,156],"ol",{},[137,138,139,140,142,143,146],"li",{},"Check your views for the login-versus-authorization gap. Grep for ",[27,141,29],{}," and ",[27,144,145],{},"LoginRequiredMixin"," and ask, for each one, whether it's actually guarding sensitive data from other authenticated users — not just anonymous ones.",[137,148,149],{},"Draw the module boundary before you write code. Decide what's genuinely shared (auth, base UI, logging) versus what belongs to one audience only.",[137,151,152,153,155],{},"Write one mixin, not one per view. A single ",[27,154,74],{}," in your core app, subclassed per module, means your enforcement logic has exactly one place to be reviewed and one place to be wrong.",[137,157,158],{},"Enforce at every layer independently — views, API, and navigation — so hiding a link is never your only line of defense.",[19,160,162],{"id":161},"references","References",[164,165,166,174,185],"ul",{},[137,167,168],{},[169,170,173],"a",{"href":171,"rel":172},"https://docs.djangoproject.com/en/stable/topics/auth/default/",[],"Django authentication system — Using the Django authentication system",[137,175,176],{},[169,177,180,181,184],{"href":178,"rel":179},"https://docs.djangoproject.com/en/stable/topics/auth/default/#the-loginrequired-mixin",[],"Django class-based views — Using ",[27,182,183],{},"UserPassesTestMixin"," / permission mixins",[137,186,187],{},[169,188,191],{"href":189,"rel":190},"https://www.django-rest-framework.org/api-guide/permissions/",[],"Django REST Framework — Permissions",{"title":193,"searchDepth":194,"depth":194,"links":195},"",2,[196,197,198,199,200,201],{"id":21,"depth":194,"text":22},{"id":44,"depth":194,"text":45},{"id":57,"depth":194,"text":58},{"id":67,"depth":194,"text":68},{"id":122,"depth":194,"text":123},{"id":161,"depth":194,"text":162},"engineering","2026-10-05","A technical deep-dive on restructuring a Django internal tool to support two distinct GitLab teams: Security Compliance and Internal Audit.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097663/Blog/Hero%20Images/Blog/Hero%20Images/Workflow%201800x945_2gQoQIbY9NvjLFpXtsxtXy_1750097663612.png",{},true,"/en-us/blog/module-level-access-in-a-django-grc-app",{"config":213,"title":5,"description":204},{"noIndex":207},"module-level-access-in-a-django-grc-app","en-us/blog/module-level-access-in-a-django-grc-app",[217],"community","BlogPost","TPRiRvZ7PwtoIuCyO-POoqgwjN62mU5KdkmqwYnkLKk",{"logo":221,"freeTrial":226,"sales":231,"login":236,"items":241,"search":566,"minimal":597,"duo":616,"switchNav":625,"pricingDeployment":636},{"config":222},{"href":223,"dataGaName":224,"dataGaLocation":225},"/","gitlab logo","header",{"text":227,"config":228},"Get free trial",{"href":229,"dataGaName":230,"dataGaLocation":225},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":232,"config":233},"Request a demo",{"href":234,"dataGaName":235,"dataGaLocation":225},"/request-a-demo/","sales",{"text":237,"config":238},"Sign in",{"href":239,"dataGaName":240,"dataGaLocation":225},"https://gitlab.com/users/sign_in/","sign in",[242,323,411,416,490,544],{"text":243,"left":210,"config":244,"menu":246},"Platform",{"dataNavLevelOne":245},"platform",{"type":247,"link":248,"columns":252,"feature":312},"lists",{"text":249,"config":250},"Explore the Platform",{"href":251,"dataGaName":245,"dataGaLocation":225},"/platform/",[253,271,294],{"title":254,"items":255},"Execution & Workflows",[256,261,266],{"text":257,"config":258},"CI/CD",{"href":259,"dataGaLocation":225,"dataGaName":260},"/solutions/continuous-integration/","ci/cd",{"text":262,"config":263},"Source Code Management",{"href":264,"dataGaLocation":225,"dataGaName":265},"/solutions/source-code-management/","source code management",{"text":267,"config":268},"Agile Delivery",{"href":269,"dataGaLocation":225,"dataGaName":270},"/solutions/agile-delivery/","agile delivery",{"title":272,"items":273},"Security & Governance",[274,279,284,289],{"text":275,"config":276},"Application Security Testing",{"href":277,"dataGaLocation":225,"dataGaName":278},"/solutions/application-security-testing/","application security testing",{"text":280,"config":281},"Governance & Compliance",{"href":282,"dataGaLocation":225,"dataGaName":283},"/solutions/software-compliance/","governance and compliance",{"text":285,"config":286},"Secrets Management",{"href":287,"dataGaLocation":225,"dataGaName":288},"/gitlab-secrets-manager/","secrets management",{"text":290,"config":291},"Supply Chain Security",{"href":292,"dataGaLocation":225,"dataGaName":293},"/solutions/supply-chain/","supply chain security",{"title":295,"items":296},"Context & AI",[297,302,307],{"text":298,"config":299},"Agentic Orchestration",{"href":300,"dataGaLocation":225,"dataGaName":301},"/gitlab-duo-agent-platform/","agentic orchestration",{"text":303,"config":304},"Context Graph",{"href":305,"dataGaLocation":225,"dataGaName":306},"/gitlab-orbit/","context graph",{"text":308,"config":309},"Visibility & Measurement",{"href":310,"dataGaLocation":225,"dataGaName":311},"/solutions/visibility-measurement/","visibility and measurement",{"config":313,"title":316,"text":317,"link":318},{"background":314,"textColor":315},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1790185102/o7dn32anqcb6z8dgd3as.jpg')","#000","Why GitLab","One platform for speed with control across your software lifecycle.",{"text":319,"config":320},"Learn more",{"href":321,"dataGaName":322,"dataGaLocation":225},"/why-gitlab/","why gitlab",{"text":324,"left":210,"config":325,"menu":327},"Solutions",{"dataNavLevelOne":326},"solutions",{"type":247,"link":328,"columns":332,"feature":402},{"text":329,"config":330},"View all Solutions",{"href":331,"dataGaName":326,"dataGaLocation":225},"/solutions/",[333,351,369],{"title":334,"items":335},"Outcomes",[336,341,346],{"text":337,"config":338},"DevOps Modernization",{"href":339,"dataGaLocation":225,"dataGaName":340},"/assessments/devops-modernization-assessment/","devops modernization",{"text":342,"config":343},"Security Modernization",{"href":344,"dataGaLocation":225,"dataGaName":345},"/assessments/security-modernization-assessment/","security modernization",{"text":347,"config":348},"AI Modernization",{"href":349,"dataGaLocation":225,"dataGaName":350},"/assessments/ai-modernization-assessment/","ai modernization",{"title":352,"items":353},"By size",[354,359,364],{"text":355,"config":356},"Enterprise",{"href":357,"dataGaLocation":225,"dataGaName":358},"/enterprise/","enterprise",{"text":360,"config":361},"Small Business",{"href":362,"dataGaLocation":225,"dataGaName":363},"/small-business/","small business",{"text":365,"config":366},"Startups",{"href":367,"dataGaLocation":225,"dataGaName":368},"/solutions/startups/","startups",{"title":370,"items":371},"Industries",[372,377,382,387,392,397],{"text":373,"config":374},"Financial Services",{"href":375,"dataGaLocation":225,"dataGaName":376},"/solutions/finance/","financial services",{"text":378,"config":379},"Public Sector",{"href":380,"dataGaLocation":225,"dataGaName":381},"/solutions/public-sector/","public sector",{"text":383,"config":384},"Telecommunications",{"href":385,"dataGaLocation":225,"dataGaName":386},"/solutions/telecommunications/","telecommunications",{"text":388,"config":389},"Automotive",{"href":390,"dataGaLocation":225,"dataGaName":391},"/solutions/automotive/","automotive",{"text":393,"config":394},"Education",{"href":395,"dataGaLocation":225,"dataGaName":396},"/solutions/education/","education",{"text":398,"config":399},"Aerospace",{"href":400,"dataGaLocation":225,"dataGaName":401},"/solutions/aerospace/","aerospace",{"config":403,"title":404,"text":405,"link":406},{"background":314,"textColor":315},"GitLab Transcend","Catch our latest innovations announced at the last Transcend.",{"text":407,"config":408},"Read the blog",{"href":409,"dataGaName":410,"dataGaLocation":225},"/blog/gitlab-transcend-announcements/","gitlab transcend",{"text":412,"config":413},"Pricing",{"href":414,"dataGaName":415,"dataGaLocation":225,"dataNavLevelOne":415},"/pricing/","pricing",{"text":417,"config":418,"menu":420},"Resources",{"dataNavLevelOne":419},"resources",{"type":247,"link":421,"columns":425,"feature":481},{"text":422,"config":423},"View all resources",{"href":424,"dataGaName":419,"dataGaLocation":225},"/resources/",[426,454],{"title":427,"items":428},"Discover",[429,434,439,444,449],{"text":430,"config":431},"Docs",{"href":432,"dataGaName":433,"dataGaLocation":225},"https://docs.gitlab.com/","docs",{"text":435,"config":436},"University",{"href":437,"dataGaName":438,"dataGaLocation":225},"https://university.gitlab.com/","university",{"text":440,"config":441},"Demo Series",{"href":442,"dataGaName":443,"dataGaLocation":225},"/gitlab-technical-demo-series/","demo series",{"text":445,"config":446},"Demo Hub",{"href":447,"dataGaName":448,"dataGaLocation":225},"/demo-hub/","demo hub",{"text":450,"config":451},"Services",{"href":452,"dataGaName":453,"dataGaLocation":225},"/services/","services",{"title":455,"items":456},"Connect",[457,462,466,471,476],{"text":458,"config":459},"Blog",{"href":460,"dataGaName":461,"dataGaLocation":225},"/blog/","blog",{"text":463,"config":464},"Community",{"href":465,"dataGaName":217,"dataGaLocation":225},"/community/",{"text":467,"config":468},"Customers",{"href":469,"dataGaName":470,"dataGaLocation":225},"/customers/","customers",{"text":472,"config":473},"Partners",{"href":474,"dataGaName":475,"dataGaLocation":225},"/partners/","partners",{"text":477,"config":478},"Events",{"href":479,"dataGaName":480,"dataGaLocation":225},"/events/","events",{"config":482,"title":483,"text":484,"link":485},{"background":314,"textColor":315},"What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":486,"config":487},"Read the latest",{"href":488,"dataGaName":489,"dataGaLocation":225},"/whats-new/","whats new",{"text":491,"config":492,"menu":494},"Company",{"dataNavLevelOne":493},"company",{"type":247,"columns":495},[496],{"items":497},[498,503,509,514,519,524,529,534,539],{"text":499,"config":500},"About",{"href":501,"dataGaName":502,"dataGaLocation":225},"/company/","about",{"text":504,"config":505,"footerGa":508},"Jobs",{"href":506,"dataGaName":507,"dataGaLocation":225},"/jobs/","jobs",{"dataGaName":507},{"text":510,"config":511},"Press",{"href":512,"dataGaName":513,"dataGaLocation":225},"/press/","press",{"text":515,"config":516},"Handbook",{"href":517,"dataGaName":518,"dataGaLocation":225},"https://handbook.gitlab.com/","handbook",{"text":520,"config":521},"Leadership",{"href":522,"dataGaName":523,"dataGaLocation":225},"/company/team/e-group/","leadership",{"text":525,"config":526},"Investor relations",{"href":527,"dataGaName":528,"dataGaLocation":225},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":530,"config":531},"Trust Center",{"href":532,"dataGaName":533,"dataGaLocation":225},"/security/","trust center",{"text":535,"config":536},"AI Transparency Center",{"href":537,"dataGaName":538,"dataGaLocation":225},"/ai-transparency-center/","ai transparency center",{"text":540,"config":541},"Newsletter",{"href":542,"dataGaName":543,"dataGaLocation":225},"/company/contact/#contact-forms","newsletter",{"text":545,"config":546,"menu":547},"Contact us",{"dataNavLevelOne":493},{"type":247,"columns":548},[549],{"items":550},[551,556,561],{"text":552,"config":553},"Talk to sales",{"href":554,"dataGaName":555,"dataGaLocation":225},"/sales/","talk to sales",{"text":557,"config":558},"Support portal",{"href":559,"dataGaName":560,"dataGaLocation":225},"https://support.gitlab.com/hc/en-us","support portal",{"text":562,"config":563},"Customer portal",{"href":564,"dataGaName":565,"dataGaLocation":225},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":567,"login":568,"suggestions":575},"Close",{"text":569,"link":570},"To search repositories and projects, login to",{"text":571,"config":572},"gitlab.com",{"href":239,"dataGaName":573,"dataGaLocation":574},"search login","search",{"text":576,"default":577},"Suggestions",[578,581,584,586,590,594],{"text":579,"config":580},"GitLab Duo Agent Platform",{"href":300,"dataGaName":579,"dataGaLocation":574},{"text":582,"config":583},"Code Suggestions (AI)",{"href":300,"dataGaName":582,"dataGaLocation":574},{"text":257,"config":585},{"href":259,"dataGaName":257,"dataGaLocation":574},{"text":587,"config":588},"GitLab on AWS",{"href":589,"dataGaName":587,"dataGaLocation":574},"/partners/technology-partners/aws/",{"text":591,"config":592},"GitLab on Google Cloud",{"href":593,"dataGaName":591,"dataGaLocation":574},"/partners/technology-partners/google-cloud-platform/",{"text":595,"config":596},"Why GitLab?",{"href":321,"dataGaName":595,"dataGaLocation":574},{"freeTrial":598,"mobileIcon":603,"desktopIcon":608,"secondaryButton":611},{"text":599,"config":600},"Start free trial",{"href":601,"dataGaName":230,"dataGaLocation":602},"https://gitlab.com/-/trials/new/","nav",{"altText":604,"config":605},"Gitlab Icon",{"src":606,"dataGaName":607,"dataGaLocation":602},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":604,"config":609},{"src":610,"dataGaName":607,"dataGaLocation":602},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":612,"config":613},"Get Started",{"href":614,"dataGaName":615,"dataGaLocation":602},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":617,"mobileIcon":621,"desktopIcon":623},{"text":618,"config":619},"Learn more about GitLab Duo",{"href":300,"dataGaName":620,"dataGaLocation":602},"gitlab duo",{"altText":604,"config":622},{"src":606,"dataGaName":607,"dataGaLocation":602},{"altText":604,"config":624},{"src":610,"dataGaName":607,"dataGaLocation":602},{"button":626,"mobileIcon":631,"desktopIcon":633},{"text":627,"config":628},"/switch",{"href":629,"dataGaName":630,"dataGaLocation":602},"#contact","switch",{"altText":604,"config":632},{"src":606,"dataGaName":607,"dataGaLocation":602},{"altText":604,"config":634},{"src":635,"dataGaName":607,"dataGaLocation":602},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":637,"mobileIcon":642,"desktopIcon":644},{"text":638,"config":639},"Back to pricing",{"href":414,"dataGaName":640,"dataGaLocation":602,"icon":641},"back to pricing","GoBack",{"altText":604,"config":643},{"src":606,"dataGaName":607,"dataGaLocation":602},{"altText":604,"config":645},{"src":610,"dataGaName":607,"dataGaLocation":602},{"title":647,"button":648,"config":654},"Ship at agent speed. Prove every step. Transcend returns on October 6.",{"text":649,"config":650},"Register now",{"href":651,"dataGaName":652,"dataGaLocation":653},"/events/transcend/virtual/#register","transcend-october-2026-banner","global-banner",{"layout":655,"disabled":207},"release",{"data":657},{"text":658,"source":659,"edit":665,"contribute":670,"config":675,"items":680,"minimal":906},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":660,"config":661},"View page source",{"href":662,"dataGaName":663,"dataGaLocation":664},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":666,"config":667},"Edit this page",{"href":668,"dataGaName":669,"dataGaLocation":664},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":671,"config":672},"Please contribute",{"href":673,"dataGaName":674,"dataGaLocation":664},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":676,"facebook":677,"youtube":678,"linkedin":679},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[681,728,786,833,874],{"title":412,"links":682,"subMenu":697},[683,687,692],{"text":684,"config":685},"View plans",{"href":414,"dataGaName":686,"dataGaLocation":664},"view plans",{"text":688,"config":689},"Why Premium?",{"href":690,"dataGaName":691,"dataGaLocation":664},"/pricing/premium/","why premium",{"text":693,"config":694},"Why Ultimate?",{"href":695,"dataGaName":696,"dataGaLocation":664},"/pricing/ultimate/","why ultimate",[698],{"title":699,"links":700},"Contact Us",[701,704,706,708,713,718,723],{"text":702,"config":703},"Contact sales",{"href":554,"dataGaName":235,"dataGaLocation":664},{"text":557,"config":705},{"href":559,"dataGaName":560,"dataGaLocation":664},{"text":562,"config":707},{"href":564,"dataGaName":565,"dataGaLocation":664},{"text":709,"config":710},"Status",{"href":711,"dataGaName":712,"dataGaLocation":664},"https://status.gitlab.com/","status",{"text":714,"config":715},"Terms of use",{"href":716,"dataGaName":717,"dataGaLocation":664},"/terms/","terms of use",{"text":719,"config":720},"Privacy statement",{"href":721,"dataGaName":722,"dataGaLocation":664},"/privacy/","privacy statement",{"text":724,"config":725},"Cookie preferences",{"dataGaName":726,"dataGaLocation":664,"id":727,"isOneTrustButton":210},"cookie preferences","ot-sdk-btn",{"title":243,"links":729,"subMenu":743},[730,733,735,737,739,741],{"text":731,"config":732},"Explore the platform",{"href":251,"dataGaName":245,"dataGaLocation":664},{"text":298,"config":734},{"href":300,"dataGaName":301,"dataGaLocation":664},{"text":257,"config":736},{"href":259,"dataGaName":260,"dataGaLocation":664},{"text":303,"config":738},{"href":305,"dataGaName":306,"dataGaLocation":664},{"text":285,"config":740},{"href":287,"dataGaName":288,"dataGaLocation":664},{"text":316,"config":742},{"href":321,"dataGaName":322,"dataGaLocation":664},[744],{"title":745,"links":746},"Topics",[747,751,756,761,766,771,776,781],{"text":257,"config":748},{"href":749,"dataGaName":750,"dataGaLocation":664},"/topics/ci-cd/","cicd",{"text":752,"config":753},"GitOps",{"href":754,"dataGaName":755,"dataGaLocation":664},"/topics/gitops/","gitops",{"text":757,"config":758},"DevOps",{"href":759,"dataGaName":760,"dataGaLocation":664},"/topics/devops/","devops",{"text":762,"config":763},"Version Control",{"href":764,"dataGaName":765,"dataGaLocation":664},"/topics/version-control/","version control",{"text":767,"config":768},"DevSecOps",{"href":769,"dataGaName":770,"dataGaLocation":664},"/topics/devsecops/","devsecops",{"text":772,"config":773},"Cloud Native",{"href":774,"dataGaName":775,"dataGaLocation":664},"/topics/cloud-native/","cloud native",{"text":777,"config":778},"AI for Coding",{"href":779,"dataGaName":780,"dataGaLocation":664},"/topics/devops/ai-for-coding/","ai for coding",{"text":782,"config":783},"Agentic AI",{"href":784,"dataGaName":785,"dataGaLocation":664},"/topics/agentic-ai/","agentic ai",{"title":324,"links":787},[788,791,794,797,800,803,805,807,809,811,813,816,818,821,825,829],{"text":789,"config":790},"DevOps modernization",{"href":339,"dataGaName":340,"dataGaLocation":664},{"text":792,"config":793},"Security modernization",{"href":344,"dataGaName":345,"dataGaLocation":664},{"text":795,"config":796},"AI modernization",{"href":349,"dataGaName":350,"dataGaLocation":664},{"text":798,"config":799},"Financial services",{"href":375,"dataGaName":376,"dataGaLocation":664},{"text":801,"config":802},"Public sector",{"href":380,"dataGaName":381,"dataGaLocation":664},{"text":383,"config":804},{"href":385,"dataGaName":386,"dataGaLocation":664},{"text":388,"config":806},{"href":390,"dataGaName":391,"dataGaLocation":664},{"text":393,"config":808},{"href":395,"dataGaName":396,"dataGaLocation":664},{"text":398,"config":810},{"href":400,"dataGaName":401,"dataGaLocation":664},{"text":355,"config":812},{"href":357,"dataGaName":358,"dataGaLocation":664},{"text":814,"config":815},"Small business",{"href":362,"dataGaName":363,"dataGaLocation":664},{"text":365,"config":817},{"href":367,"dataGaName":368,"dataGaLocation":664},{"text":752,"config":819},{"href":820,"dataGaName":755,"dataGaLocation":664},"/solutions/gitops/",{"text":822,"config":823},"Software composition analysis",{"href":292,"dataGaName":824,"dataGaLocation":664},"software composition analysis",{"text":826,"config":827},"Value stream management",{"href":310,"dataGaName":828,"dataGaLocation":664},"value stream management",{"text":830,"config":831},"All solutions",{"href":331,"dataGaName":832,"dataGaLocation":664},"all solutions",{"title":417,"links":834},[835,840,845,847,849,851,853,855,857,859,861,863,865,867,870],{"text":836,"config":837},"Install",{"href":838,"dataGaName":839,"dataGaLocation":664},"/install/","install",{"text":841,"config":842},"Quick start guides",{"href":843,"dataGaName":844,"dataGaLocation":664},"/get-started/","quick setup checklists",{"text":430,"config":846},{"href":432,"dataGaName":433,"dataGaLocation":664},{"text":435,"config":848},{"href":437,"dataGaName":438,"dataGaLocation":664},{"text":440,"config":850},{"href":442,"dataGaName":443,"dataGaLocation":664},{"text":445,"config":852},{"href":447,"dataGaName":448,"dataGaLocation":664},{"text":450,"config":854},{"href":452,"dataGaName":453,"dataGaLocation":664},{"text":458,"config":856},{"href":460,"dataGaName":461,"dataGaLocation":664},{"text":463,"config":858},{"href":465,"dataGaName":217,"dataGaLocation":664},{"text":467,"config":860},{"href":469,"dataGaName":470,"dataGaLocation":664},{"text":472,"config":862},{"href":474,"dataGaName":475,"dataGaLocation":664},{"text":477,"config":864},{"href":479,"dataGaName":480,"dataGaLocation":664},{"text":540,"config":866},{"href":542,"dataGaName":543,"dataGaLocation":664},{"text":868,"config":869},"What's new",{"href":488,"dataGaName":489,"dataGaLocation":664},{"text":871,"config":872},"All resources",{"href":424,"dataGaName":873,"dataGaLocation":664},"all resources",{"title":491,"links":875},[876,878,880,882,884,886,888,890,892,896,901],{"text":499,"config":877},{"href":501,"dataGaName":493,"dataGaLocation":664},{"text":504,"config":879},{"href":506,"dataGaName":507,"dataGaLocation":664},{"text":510,"config":881},{"href":512,"dataGaName":513,"dataGaLocation":664},{"text":515,"config":883},{"href":517,"dataGaName":518,"dataGaLocation":664},{"text":520,"config":885},{"href":522,"dataGaName":523,"dataGaLocation":664},{"text":525,"config":887},{"href":527,"dataGaName":528,"dataGaLocation":664},{"text":530,"config":889},{"href":532,"dataGaName":533,"dataGaLocation":664},{"text":535,"config":891},{"href":537,"dataGaName":538,"dataGaLocation":664},{"text":893,"config":894},"Sustainability",{"href":895,"dataGaName":893,"dataGaLocation":664},"/sustainability/",{"text":897,"config":898},"Diversity, inclusion and belonging (DIB)",{"href":899,"dataGaName":900,"dataGaLocation":664},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":902,"config":903},"Modern Slavery Transparency Statement",{"href":904,"dataGaName":905,"dataGaLocation":664},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":907},[908,911,914],{"text":909,"config":910},"Terms",{"href":716,"dataGaName":717,"dataGaLocation":664},{"text":912,"config":913},"Cookies",{"dataGaName":726,"dataGaLocation":664,"id":727,"isOneTrustButton":210},{"text":915,"config":916},"Privacy",{"href":721,"dataGaName":722,"dataGaLocation":664},[918],{"id":919,"title":7,"body":206,"config":920,"content":922,"description":206,"extension":925,"meta":926,"navigation":210,"path":927,"seo":928,"stem":929,"__hash__":930},"blogAuthors/en-us/blog/authors/donovan-felton.yml",{"template":921},"BlogAuthor",{"name":7,"config":923},{"headshot":924},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1791211785/tnxlojjeltqezcspwsw5.jpg","yml",{},"/en-us/blog/authors/donovan-felton",{},"en-us/blog/authors/donovan-felton","ktL6qi00e0R1FVkOspgqcBBm0KJ0DcFCtXV8TE65aWg",[932,940,948],{"title":933,"description":934,"heroImage":935,"category":202,"date":936,"authors":937,"slug":939,"externalUrl":206},"How GitLab reduced code-per-agentic-flow ratio by 45%","Learn how Flow Registry, a component-based framework that compiles YAML configurations into fully functional LangGraph flows, helped improve scalability and more.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-09-22",[938],"Mikolaj Wawrzyniak","how-gitlab-reduced-code-per-agentic-flow-ratio",{"title":941,"description":942,"heroImage":943,"category":202,"date":944,"authors":945,"slug":947,"externalUrl":206},"Co-Create: Building GitLab with our users","How GitLab users collaborated with us to build practical product improvements in the first half of 2026.","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-09-10",[946],"Isa Huerga","co-create-h1-2026",{"title":949,"description":950,"heroImage":951,"category":202,"date":952,"authors":953,"slug":956,"externalUrl":206},"Git was built for humans — agents need an upgrade","See how GitLab's next-gen SCM scales concurrency for agents, and why a new backend must address provenance and lifecycle challenges.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773856365/gsx2c0vqlswox3ldmq88.jpg","2026-08-26",[954,955],"Kranthi Erusu","Jessica Taylor","gitlab-next-gen-scm",{"header":958,"text":959,"button":963,"disclaimer":966},"Start your free\n\n30-day GitLab trial\n",[960,961,962],"GitLab Duo Agent Platform access","Try our most advanced features","Automate complex tasks with AI",{"text":227,"config":964},{"href":965,"dataGaName":230,"dataGaLocation":461},"https://gitlab.com/-/trials/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/blog/","No credit card required.",{"header":968,"blurb":969,"button":970,"secondaryButton":975},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":971,"config":972},"Get your free trial",{"href":973,"dataGaName":230,"dataGaLocation":974},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":702,"config":976},{"href":554,"dataGaName":235,"dataGaLocation":974},1791280220921]