[{"data":1,"prerenderedAt":760},["ShallowReactive",2],{"/en-us/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake":3,"navigation-en-us":35,"banner-en-us":463,"footer-en-us":480,"Radovan Bacovic":725,"next-steps-en-us":738,"footer-source-/en-us/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake/":753},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":12,"config":25,"_id":28,"_type":29,"title":30,"_source":31,"_file":32,"_stem":33,"_extension":34},"/en-us/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake","blog",false,"",{"config":9,"title":10,"ogTitle":10,"description":11},{"noIndex":6},"How we built a structured Streamlit Framework in Snowflake","Want to transform development from chaos to compliance? Learn how we implemented governance early on rather than retrofitting when maintenance costs climb exponentially.",{"title":13,"description":11,"authors":14,"heroImage":16,"date":17,"body":18,"category":19,"tags":20},"How we built a structured Streamlit Application Framework in Snowflake",[15],"Radovan Bacovic","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097447/Blog/Hero%20Images/Blog/Hero%20Images/blog-image-template-1800x945%20%284%29_3LZkiDjHLjhqEkvOvBsVKp_1750097447404.png","2025-10-10","Recently, the GitLab Data team transformed scattered\n[Streamlit](https://streamlit.io/) applications into a unified, secure, and\nscalable solution for our Snowflake environment. To accomplish this, we\npacked Python, Snowflake, and Streamlit together with GitLab. Follow along\non this journey and discover the results we achieved, and learn how you can,\ntoo.\n\n## The challenge\n\nImagine this scenario: Your organization has dozens of Streamlit applications across different environments, running various Python versions, connecting to sensitive data with inconsistent security practices. Some apps work, others break mysteriously, and nobody knows who built what or how to maintain them.\n\n\nThis was exactly the challenge our data team faced. Applications were being created in isolation, with no standardization, no security oversight, and no clear deployment process. The result? A compliance nightmare and a maintenance burden that was growing exponentially.\n\n\n![Functional architectural design (high level)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/i50lpkrwy9bok056rdak.png)\n\n\u003Cp>\u003C/p>\n\n\u003Ccenter>\u003Ci>Functional architectural design (high level)\u003C/i>\u003C/center>\n\n## How we started\n\nWe leveraged our unique position as customer zero by building this entire framework on GitLab's own CI/CD infrastructure and project management tools. Here are the ingredients we started with: \n\n1. [GitLab](https://about.gitlab.com/platform/) (product)\n\n1. [Snowflake](https://about.gitlab.com/platform/) - our single source of truth (SSOT) for the data warehouse activities (and more than that)\n\n1. [Streamlit](https://streamlit.io/) - an open-source tool for visual applications that has pure Python code under the hood\n\nThis provided us with immediate access to enterprise-grade DevSecOps capabilities, enabling us to implement automated testing, code review processes, and deployment pipelines from the outset. By utilizing GitLab's built-in features for issue tracking, merge requests, and automated deployments (CI/CD pipelines), we can iterate rapidly and validate the framework against real-world enterprise requirements. This internal-first approach ensured our solution was battle-tested on GitLab's own infrastructure before any external implementation.\n\n### The lessons we learned\n\nThe most critical lesson we learned from building the Streamlit Application Framework in Snowflake is that **structure beats chaos every time** — implement governance early rather than retrofitting it later when maintenance becomes exponential.\n\nYou also need to clearly define roles and responsibilities, separating infrastructure concerns from application development, so that each team can focus on its strengths.\n\nSecurity and compliance cannot be afterthoughts; they must be built into templates and automated processes from day one, as it's far easier to enforce consistent standards upfront than to force them after the fact. Invest heavily in automation and CI/CD pipelines, as manual processes don't scale and introduce human error.\n\n![Architecture of the framework (general overview)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035998/qt9gfemxjnj8kjumkuh7.png)\n\n\u003Cp>\u003C/p>\n\n\u003Ccenter>\u003Ci>Architecture of the framework (general overview)\u003C/i>\u003C/center>\n\n##  How the Streamlit Application Framework changes everything\n\nThe Streamlit Application Framework turns a scattered approach into a structure. It gives developers freedom within secure guardrails, while automating deployment and eliminating maintenance complexity.\n\n### Three clear roles, one unified process\n\nThe framework introduces a structured approach with three distinct roles:\n\n1. **Maintainers** (Data team members and contributors) handle the infrastructure, including CI/CD pipelines, security templates, and compliance rules. They ensure the framework runs smoothly and stays secure.\n\n2. **Creators** (those who need to build applications) can focus on what they do best: creating visualizations, connecting to Snowflake data, and building user experiences. They have full flexibility to create new applications from scratch, add new pages to existing apps, integrate additional Python libraries, and build complex data visualisations — all without worrying about deployment pipelines or security configurations.\n\n3. **Viewers** (end users) access polished, secure applications without any technical overhead. All they need is Snowflake access.\n\n![Roles overview and their functionality](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/oatqyx3ug7vsgzishpma.png)\n\n\u003Cp>\u003C/p>\n\n\u003Ccenter>\u003Ci>Overview of roles and their functions\u003C/i>\u003C/center>\n\n## Automate everything\n\nBy implementing CI/CD, days of manual deployments and configuration headaches are gone. The framework provides:\n\n- **One-click environment preparation:** With a set of `make` commands, the environment is installed and ready in a few seconds.\n\n\n```yaml\n\n================================================================================\n\n✅ Snowflake CLI successfully installed and configured!\n\nConnection: gitlab_streamlit\n\nUser: YOU@GITLAB.COM\n\nAccount: gitlab\n\n================================================================================\n\nUsing virtualenv: /Users/YOU/repos/streamlit/.venv\n\n📚 Installing project dependencies...\n\nInstalling dependencies from lock file\n\nNo dependencies to install or update\n\n✅ Streamlit environment prepared!\n\n```\n\n- **Automated CI/CD pipelines:** Handle testing, code review, and deployment from development to production.\n\n- **Secure sandbox environments:** Provide for safe development and testing before production deployment.\n\n\n```yaml\n\n╰─$ make streamlit-rules\n\n🔍 Running Streamlit compliance check...\n\n================================================================================\n\nCODE COMPLIANCE REPORT\n\n================================================================================\n\nGenerated: 2025-07-09 14:01:16\n\nFiles checked: 1\n\n\nSUMMARY:\n\n✅ Passed: 1\n\n❌ Failed: 0\n\nSuccess Rate: 100.0%\n\n\nAPPLICATION COMPLIANCE SUMMARY:\n\n📱 Total Applications Checked: 1\n\n⚠️ Applications with Issues: 0\n\n📊 File Compliance Rate: 100.0%\n\n\nDETAILED RESULTS BY APPLICATION:\n\n...\n\n```\n\n\n- **Template-based application creation:** Ensures consistency across all applications and pages.\n\n\n```yaml\n\n╰─$ make streamlit-new-page STREAMLIT_APP=sales_dashboard STREAMLIT_PAGE_NAME=analytics\n\n📝 Generating new Streamlit page: analytics for app: sales_dashboard\n\n📃 Create new page from template:\n\nPage name: analytics\n\nApp directory: sales_dashboard\n\nTemplate path: page_template.py\n\n✅ Successfully created 'analytics.py' in 'sales_dashboard' directory from template\n\n```\n\n\n- **Poetry-based dependency management:** Prevents version conflicts and maintains clean environments.\n\n- **Organized project structure:** Has dedicated folders for applications, templates, compliance rules, and configuration management.\n\n\n```yaml\n\n├── src/\n\n│   ├── applications/     # Folder for Streamlit applications\n\n│   │   ├── main_app/     # Main dashboard application\n\n│   │   ├── components/   # Shared components\n\n│   │   └── \u003Cyour_apps>/  # Your custom application\n\n│   │   └── \u003Cyour_apps2>/ # Your 2nd custom application\n\n│   ├── templates/        # Application and page templates\n\n│   ├── compliance/       # Compliance rules and checks\n\n│   └── setup/            # Setup and configuration utilities\n\n├── tests/                # Test files\n\n├── config.yml            # Environment configuration\n\n├── Makefile              # Build and deployment automation\n\n└── README.md             # Main README.md file\n\n```\n\n\n- **Streamlined workflow:** Takes local development through testing schema to production, all automated through GitLab CI/CD pipelines.\n\n\n![GitLab CI/CD pipelines for full automation of the process](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035998/usyma2jkgiazu9iay1au.png)\n\n\u003Cp>\u003C/p>\n\u003Ccenter>\u003Ci>GitLab CI/CD pipelines for full automation of the process\u003C/i>\u003C/center>\n\n## Security and compliance by design\n\nInstead of bolting on security as an afterthought, the structured Streamlit Application Framework builds it in from the ground up. Every application adheres to the same security standards, and compliance requirements are automatically enforced. Audit trails are maintained throughout the development lifecycle.\n\nWe introduce our compliance rules and verify them with a single command. For instance, we can list which classes and methods are mandatory to use, which files you should have, and which roles are allowed and which are forbidden to share the application with. The rules are flexible and descriptive; all you need to do is define them in a YAML file:\n\n\n```yaml\n\nclass_rules:\n  - name: \"Inherit code for the page from GitLabDataStreamlitInit\"\n    description: \"All Streamlit apps must inherit from GitLabDataStreamlitInit\"\n    severity: \"error\"\n    required: true\n    class_name: \"*\"\n    required_base_classes:\n      - \"GitLabDataStreamlitInit\"\n    required_methods:\n      - \"__init__\"\n      - \"set_page_layout\"\n      - \"setup_ui\"\n      - \"run\"\n\nfunction_rules:\n  - name: \"Main function required\"\n    description: \"Must have a main() function\"\n    severity: \"error\"\n    required: true\n    function_name: \"main\"\n\nimport_rules:\n  - name: \"Import GitLabDataStreamlitInit\"\n    description: \"Must import the mandatory base class\"\n    severity: \"error\"\n    required: true\n    module_name: \"gitlab_data_streamlit_init\"\n    required_items:\n      - \"GitLabDataStreamlitInit\"\n  - name: \"Import streamlit\"\n    description: \"Must import streamlit library\"\n    severity: \"error\"\n    required: true\n    module_name: \"streamlit\"\n\nfile_rules:\n  - name: \"Snowflake configuration required (snowflake.yml)\"\n    description: \"Each application must have a snowflake.yml configuration file\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/snowflake.yml\"\n    base_path: \"\"\n  - name: \"Snowflake environment required (environment.yml)\"\n    description: \"Each application must have a environment.yml configuration file\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/environment.yml\"\n    base_path: \"\"\n  - name: \"Share specification required (share.yml)\"\n    description: \"Each application must have a share.yml file\"\n    severity: \"warning\"\n    required: true\n    file_pattern: \"**/applications/**/share.yml\"\n    base_path: \"\"\n  - name: \"README.md required (README.md)\"\n    description: \"Each application should have a README.md file with a proper documentation\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/README.md\"\n    base_path: \"\"\n  - name: \"Starting point recommended (dashboard.py)\"\n    description: \"Each application must have a dashboard.py as a starting point\"\n    severity: \"warning\"\n    required: true\n    file_pattern: \"**/applications/**/dashboard.py\"\n    base_path: \"\"\n\nsql_rules:\n  - name: \"SQL files must contain only SELECT statements\"\n    description: \"SQL files and SQL code in other files should only contain SELECT statements for data safety\"\n    severity: \"error\"\n    required: true\n    file_extensions: [\".sql\", \".py\"]\n    select_only: true\n    forbidden_statements:\n      - ....\n    case_sensitive: false\n  - name: \"SQL queries should include proper SELECT statements\"\n    description: \"When SQL is present, it should contain proper SELECT statements\"\n    severity: \"warning\"\n    required: false\n    file_extensions: [\".sql\", \".py\"]\n    required_statements:\n      - \"SELECT\"\n    case_sensitive: false\n\nshare_rules:\n  - name: \"Valid functional roles in share.yml\"\n    description: \"Share.yml files must contain only valid functional roles from the approved list\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/share.yml\"\n    valid_roles:\n      - ...\n    safe_data_roles:\n      - ...\n  - name: \"Share.yml file format validation\"\n    description: \"Share.yml files must follow the correct YAML format structure\"\n    severity: \"error\"\n    required: true\n    file_pattern: \"**/applications/**/share.yml\"\n    required_keys:\n      - \"share\"\n    min_roles: 1\n    max_roles: 10\n```\n\n\nWith one command running:\n\n\n```bash\n\n╰─$ make streamlit-rules\n\n```\n\n\nWe can verify all the rules we have created and validate that the developers (who are building a Streamlit application) are following the policy specified by the creators (who determine the policies and building blocks of the framework), and that all the building blocks are in the right place. This ensures consistent behavior across all Streamlit applications.\n\n\n```yaml\n\n🔍 Running Streamlit compliance check...\n\n================================================================================\n\nCODE COMPLIANCE REPORT\n\n================================================================================\n\nGenerated: 2025-08-18 17:05:12\n\nFiles checked: 4\n\n\nSUMMARY:\n\n✅ Passed: 4\n\n❌ Failed: 0\n\nSuccess Rate: 100.0%\n\n\nAPPLICATION COMPLIANCE SUMMARY:\n\n📱 Total Applications Checked: 1\n\n⚠️ Applications with Issues: 0\n\n📊 File Compliance Rate: 100.0%\n\n\nDETAILED RESULTS BY APPLICATION:\n\n================================================================================\n\n✅ PASS APPLICATION: main_app\n\n------------------------------------------------------------\n\n📁 FILES ANALYZED (4):\n\n✅ dashboard.py\n\n📦 Classes: SnowflakeConnectionTester\n\n🔧 Functions: main\n\n📥 Imports: os, pwd, gitlab_data_streamlit_init, snowflake.snowpark.exceptions, streamlit\n\n\n✅ show_streamlit_apps.py\n\n📦 Classes: ShowStreamlitApps\n\n🔧 Functions: main\n\n📥 Imports: pandas, gitlab_data_streamlit_init, snowflake_session, streamlit\n\n\n✅ available_packages.py\n\n📦 Classes: AvailablePackages\n\n🔧 Functions: main\n\n📥 Imports: pandas, gitlab_data_streamlit_init, streamlit\n\n\n✅ share.yml\n\n👥 Share Roles: snowflake_analyst_safe\n\n\n📄 FILE COMPLIANCE FOR MAIN_APP:\n\n✅ Required files found:\n\n✓ snowflake.yml\n\n✓ environment.yml\n\n✓ share.yml\n\n✓ README.md\n\n✓ dashboard.py\n\n\nRULES CHECKED:\n\n----------------------------------------\n\nClass Rules (1):\n\n- Inherit code for the page from GitLabDataStreamlitInit (error)\n\n\nFunction Rules (1):\n\n- Main function required (error)\n\n\nImport Rules (2):\n\n- Import GitLabDataStreamlitInit (error)\n\n- Import streamlit (error)\n\n\nFile Rules (5):\n\n- Snowflake configuration required (snowflake.yml) (error)\n\n- Snowflake environment required (environment.yml) (error)\n\n- Share specification required (share.yml) (warning)\n\n- README.md required (README.md) (error)\n\n- Starting point recommended (dashboard.py) (warning)\n\n\nSQL Rules (2):\n\n- SQL files must contain only SELECT statements (error)\n\n🗄 SELECT-only mode enabled\n\n🚨 Forbidden: INSERT, UPDATE, DELETE, DROP, ALTER...\n\n- SQL queries should include proper SELECT statements (warning)\n\n\nShare Rules (2):\n\n- Valid functional roles in share.yml (error)\n\n👥 Valid roles: 15 roles defined\n\n🔒 Safe data roles: 11 roles\n\n- Share.yml file format validation (error)\n\n------------------------------------------------------------\n\n✅ Compliance check passed\n\n-----------------------------------------------------------\n\n```\n\n\n## Developer experience that works\n\nWhether you prefer your favorite IDE, a web-based development environment, or Snowflake Snowsight, the experience remains consistent. The framework provides:\n\n- **Template-driven development:** New applications and pages are created through standardized templates, ensuring consistency and best practices from day one. No more scattered design and elements.\n\n\n```yaml\n\n╰─$ make streamlit-new-app NAME=sales_dashboard\n\n🔧 Configuration Environment: TEST\n\n📝 Configuration File: config.yml\n\n📜 Config Loader Script: ./setup/get_config.sh\n\n🐍 Python Version: 3.12\n\n📁 Applications Directory: ./src/applications\n\n🗄 Database: ...\n\n📊 Schema: ...\n\n🏗 Stage: ...\n\n🏭 Warehouse: ...\n\n🆕 Creating new Streamlit app: sales_dashboard\n\nInitialized the new project in ./src/applications/sales_dashboard\n\n```\n\n\n- **Poetry package management:** All dependencies are managed through Poetry, creating isolated environments that won't disrupt your existing Python setup.\n\n\n```toml\n\n[tool.poetry]\n\nname = \"GitLab Data Streamlit\"\n\nversion = \"0.1.1\"\n\ndescription = \"GitLab Data Team Streamlit project\"\n\nauthors = [\"GitLab Data Team \u003C*****@gitlab.com>\"]\n\nreadme = \"README.md\"\n\n\n[tool.poetry.dependencies]\n\npython = \"\u003C3.13,>=3.12\"\n\nsnowflake-snowpark-python = \"==1.32.0\"\n\nsnowflake-connector-python = {extras = [\"development\", \"pandas\", \"secure-local-storage\"], version = \"^3.15.0\"}\n\nstreamlit = \"==1.22.0\"\n\nwatchdog = \"^6.0.0\"\n\ntypes-toml = \"^0.10.8.20240310\"\n\npytest = \"==7.0.0\"\n\nblack = \"==25.1.0\"\n\nimportlib-metadata = \"==4.13.0\"\n\npyyaml = \"==6.0.2\"\n\npython-qualiter = \"*\"\n\nruff = \"^0.1.0\"\n\ntypes-pyyaml = \"^6.0.12.20250516\"\n\njinja2 = \"==3.1.6\"\n\n\n[build-system]\n\nrequires = [\"poetry-core\"]\n\nbuild-backend = \"poetry.core.masonry.api\"\n\n```\n\n- **Multi-page application support:** Creators can easily build complex applications with multiple pages and add new libraries as needed. Multi-page applications are part of the framework and a developer is focusing on the logic, not the design and structuring.\n\n\n![Multipage application example (in Snowflake)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/at1q2xgmjthkrgju4okm.png)\n\n\u003Cp>\u003C/p>\n\n\u003Ccenter>\u003Ci>Multipage application example (in Snowflake)\u003C/i>\u003C/center>\n\n\u003Cp>\u003C/p>\n\n- **Seamless Snowflake integration:** Built-in connectors and authentication handling for secure data access provide the same experience, whether in local development or directly in Snowflake.\n\n\n```yaml\n\nmake streamlit-push-test APPLICATION_NAME=sales_dashboard\n\n📤 Deploying Streamlit app to test environment: sales_dashboard\n\n...\n\n------------------------------------------------------------------------------------------------------------\n\n🔗 Running share command for application: sales_dashboard\n\nRunning commands to grant shares\n\n🚀 Executing: snow streamlit share sales_dashboard with SOME_NICE_ROLE\n\n✅ Command executed successfully\n\n📊 Execution Summary: 1/1 commands succeeded\n\n```\n\n- **Comprehensive Makefile:** All common commands are wrapped in simple Makefile commands, from local development to testing and deployment, including CI/CD pipelines.\n\n\n- **Safe local development:** Everything runs in isolated Poetry environments, protecting your system while providing production-like experiences.\n\n\n![Same experience despite the environment (example of the local development)](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760035999/phmubsb34hn2mfefjvqh.png)\n\n\u003Cp>\u003C/p>\n\n\u003Ccenter>\u003Ci>Same experience despite the environment (example of the local development)\u003C/i>\u003C/center>\n\n\u003Cp>\u003C/p>\n\n- **Collaboration via code:** All applications and components are wrapped up in one repository, which allows the entire organization to collaborate on the same resources and avoid double work and redundant setup.\n\n## How you can get started\n\nIf you're facing similar challenges with scattered Streamlit applications, here's how to begin and move quickly:\n\n1. **Assess your current state:** Inventory your existing applications and identify pain points.\n\n2. **Define your roles:** Separate maintainer responsibilities from creator and end users' needs.\n\n3. **Start with templates:** Create standardized application templates that enforce your security and compliance requirements.\n\n4. **Implement CI/CD:** Automate your deployment pipeline to reduce manual errors and ensure consistency.\n\n\n![Deploy the application in Snowflake](https://res.cloudinary.com/about-gitlab-com/image/upload/v1760036003/mzge9s1fhkhnx38y1a3i.png)\n\n\u003Cp>\u003C/p>\n\n\u003Ccenter>\u003Ci>The application deployed in Snowflake\u003C/i>\u003C/center>\n\n\n## The bigger picture\n\n\nThis framework represents more than just a technical solution — it's a paradigm shift toward treating data applications as first-class citizens in your enterprise (data) architecture.\n\nBy providing structure without sacrificing flexibility, the GitLab Data team created an environment where anyone in the company with minimal technical knowledge can innovate rapidly while maintaining the highest standards of security and compliance.\n\n\n### What's next?\n\nWe're continuing to enhance the framework based on user feedback and emerging needs. Future improvements include expanded template libraries, enhanced monitoring capabilities, more flexibility, and a smoother user experience.\n\n**The goal isn't just to solve today's problems, but to create a foundation that scales with your organization's growing data application needs.**\n\n## Summary\n\n[The GitLab Data Team](https://handbook.gitlab.com/handbook/enterprise-data/) transformed dozens of scattered, insecure Streamlit applications with no standardization into a unified, enterprise-grade framework that separates roles cleanly:\n\n\n1. **Maintainers** handle infrastructure and security.\n\n2. **Creators** focus on building applications without deployment headaches.\n\n3. **Viewers** access polished, compliant apps.\n\n\nAnd we used these building blocks:\n\n1. Automated **CI/CD** pipelines\n\n2. Fully collaborative and versioned code in **git**\n\n3. **Template-based** development\n\n4. Built-in **security** compliance, testing\n\n5. **Poetry-managed** environments\n\nWe eliminated the maintenance nightmare while enabling rapid innovation — proving that you can have both structure and flexibility when you treat data applications as first-class enterprise assets rather than throwaway prototypes.\n","engineering",[21,22,23,24],"product","features","security","tutorial",{"featured":6,"template":26,"slug":27},"BlogPost","how-we-built-a-structured-streamlit-application-framework-in-snowflake","content:en-us:blog:how-we-built-a-structured-streamlit-application-framework-in-snowflake.yml","yaml","How We Built A Structured Streamlit Application Framework In Snowflake","content","en-us/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake.yml","en-us/blog/how-we-built-a-structured-streamlit-application-framework-in-snowflake","yml",{"_path":36,"_dir":37,"_draft":6,"_partial":6,"_locale":7,"data":38,"_id":459,"_type":29,"title":460,"_source":31,"_file":461,"_stem":462,"_extension":34},"/shared/en-us/main-navigation","en-us",{"logo":39,"freeTrial":44,"sales":49,"login":54,"items":59,"search":390,"minimal":421,"duo":440,"pricingDeployment":449},{"config":40},{"href":41,"dataGaName":42,"dataGaLocation":43},"/","gitlab logo","header",{"text":45,"config":46},"Get free trial",{"href":47,"dataGaName":48,"dataGaLocation":43},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":50,"config":51},"Talk to sales",{"href":52,"dataGaName":53,"dataGaLocation":43},"/sales/","sales",{"text":55,"config":56},"Sign in",{"href":57,"dataGaName":58,"dataGaLocation":43},"https://gitlab.com/users/sign_in/","sign in",[60,104,201,206,311,371],{"text":61,"config":62,"cards":64,"footer":87},"Platform",{"dataNavLevelOne":63},"platform",[65,71,79],{"title":61,"description":66,"link":67},"The most comprehensive AI-powered DevSecOps Platform",{"text":68,"config":69},"Explore our Platform",{"href":70,"dataGaName":63,"dataGaLocation":43},"/platform/",{"title":72,"description":73,"link":74},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":75,"config":76},"Meet GitLab Duo",{"href":77,"dataGaName":78,"dataGaLocation":43},"/gitlab-duo/","gitlab duo ai",{"title":80,"description":81,"link":82},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":83,"config":84},"Learn more",{"href":85,"dataGaName":86,"dataGaLocation":43},"/why-gitlab/","why gitlab",{"title":88,"items":89},"Get started with",[90,95,100],{"text":91,"config":92},"Platform Engineering",{"href":93,"dataGaName":94,"dataGaLocation":43},"/solutions/platform-engineering/","platform engineering",{"text":96,"config":97},"Developer Experience",{"href":98,"dataGaName":99,"dataGaLocation":43},"/developer-experience/","Developer experience",{"text":101,"config":102},"MLOps",{"href":103,"dataGaName":101,"dataGaLocation":43},"/topics/devops/the-role-of-ai-in-devops/",{"text":105,"left":106,"config":107,"link":109,"lists":113,"footer":183},"Product",true,{"dataNavLevelOne":108},"solutions",{"text":110,"config":111},"View all Solutions",{"href":112,"dataGaName":108,"dataGaLocation":43},"/solutions/",[114,139,162],{"title":115,"description":116,"link":117,"items":122},"Automation","CI/CD and automation to accelerate deployment",{"config":118},{"icon":119,"href":120,"dataGaName":121,"dataGaLocation":43},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[123,127,131,135],{"text":124,"config":125},"CI/CD",{"href":126,"dataGaLocation":43,"dataGaName":124},"/solutions/continuous-integration/",{"text":128,"config":129},"AI-Assisted Development",{"href":77,"dataGaLocation":43,"dataGaName":130},"AI assisted development",{"text":132,"config":133},"Source Code Management",{"href":134,"dataGaLocation":43,"dataGaName":132},"/solutions/source-code-management/",{"text":136,"config":137},"Automated Software Delivery",{"href":120,"dataGaLocation":43,"dataGaName":138},"Automated software delivery",{"title":140,"description":141,"link":142,"items":147},"Security","Deliver code faster without compromising security",{"config":143},{"href":144,"dataGaName":145,"dataGaLocation":43,"icon":146},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[148,152,157],{"text":149,"config":150},"Application Security Testing",{"href":144,"dataGaName":151,"dataGaLocation":43},"Application security testing",{"text":153,"config":154},"Software Supply Chain Security",{"href":155,"dataGaLocation":43,"dataGaName":156},"/solutions/supply-chain/","Software supply chain security",{"text":158,"config":159},"Software Compliance",{"href":160,"dataGaName":161,"dataGaLocation":43},"/solutions/software-compliance/","software compliance",{"title":163,"link":164,"items":169},"Measurement",{"config":165},{"icon":166,"href":167,"dataGaName":168,"dataGaLocation":43},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[170,174,178],{"text":171,"config":172},"Visibility & Measurement",{"href":167,"dataGaLocation":43,"dataGaName":173},"Visibility and Measurement",{"text":175,"config":176},"Value Stream Management",{"href":177,"dataGaLocation":43,"dataGaName":175},"/solutions/value-stream-management/",{"text":179,"config":180},"Analytics & Insights",{"href":181,"dataGaLocation":43,"dataGaName":182},"/solutions/analytics-and-insights/","Analytics and insights",{"title":184,"items":185},"GitLab for",[186,191,196],{"text":187,"config":188},"Enterprise",{"href":189,"dataGaLocation":43,"dataGaName":190},"/enterprise/","enterprise",{"text":192,"config":193},"Small Business",{"href":194,"dataGaLocation":43,"dataGaName":195},"/small-business/","small business",{"text":197,"config":198},"Public Sector",{"href":199,"dataGaLocation":43,"dataGaName":200},"/solutions/public-sector/","public sector",{"text":202,"config":203},"Pricing",{"href":204,"dataGaName":205,"dataGaLocation":43,"dataNavLevelOne":205},"/pricing/","pricing",{"text":207,"config":208,"link":210,"lists":214,"feature":298},"Resources",{"dataNavLevelOne":209},"resources",{"text":211,"config":212},"View all resources",{"href":213,"dataGaName":209,"dataGaLocation":43},"/resources/",[215,248,270],{"title":216,"items":217},"Getting started",[218,223,228,233,238,243],{"text":219,"config":220},"Install",{"href":221,"dataGaName":222,"dataGaLocation":43},"/install/","install",{"text":224,"config":225},"Quick start guides",{"href":226,"dataGaName":227,"dataGaLocation":43},"/get-started/","quick setup checklists",{"text":229,"config":230},"Learn",{"href":231,"dataGaLocation":43,"dataGaName":232},"https://university.gitlab.com/","learn",{"text":234,"config":235},"Product documentation",{"href":236,"dataGaName":237,"dataGaLocation":43},"https://docs.gitlab.com/","product documentation",{"text":239,"config":240},"Best practice videos",{"href":241,"dataGaName":242,"dataGaLocation":43},"/getting-started-videos/","best practice videos",{"text":244,"config":245},"Integrations",{"href":246,"dataGaName":247,"dataGaLocation":43},"/integrations/","integrations",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":43},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":43},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":43},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":43},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":43},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":43},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":43},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":43},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":43},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":43},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":43},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":43},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":43},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":43},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":43},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":43},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":43},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":43},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":43},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":43},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":43},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":50,"config":378},{"href":52,"dataGaName":379,"dataGaLocation":43},"talk to sales",{"text":381,"config":382},"Support portal",{"href":383,"dataGaName":384,"dataGaLocation":43},"https://support.gitlab.com","support portal",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":43},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":57,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":72,"config":403},{"href":77,"dataGaName":72,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":124,"config":409},{"href":126,"dataGaName":124,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":85,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":48,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":77,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},{"freeTrial":450,"mobileIcon":455,"desktopIcon":457},{"text":451,"config":452},"Back to pricing",{"href":204,"dataGaName":453,"dataGaLocation":426,"icon":454},"back to pricing","GoBack",{"altText":428,"config":456},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":458},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":464,"_dir":37,"_draft":6,"_partial":6,"_locale":7,"title":465,"button":466,"image":471,"config":475,"_id":477,"_type":29,"_source":31,"_file":478,"_stem":479,"_extension":34},"/shared/en-us/banner","is now in public beta!",{"text":467,"config":468},"Try the Beta",{"href":469,"dataGaName":470,"dataGaLocation":43},"/gitlab-duo/agent-platform/","duo banner",{"altText":472,"config":473},"GitLab Duo Agent Platform",{"src":474},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":476},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":481,"_dir":37,"_draft":6,"_partial":6,"_locale":7,"data":482,"_id":721,"_type":29,"title":722,"_source":31,"_file":723,"_stem":724,"_extension":34},"/shared/en-us/main-footer",{"text":483,"source":484,"edit":490,"contribute":495,"config":500,"items":505,"minimal":713},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":485,"config":486},"View page source",{"href":487,"dataGaName":488,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":491,"config":492},"Edit this page",{"href":493,"dataGaName":494,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":496,"config":497},"Please contribute",{"href":498,"dataGaName":499,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":501,"facebook":502,"youtube":503,"linkedin":504},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[506,553,606,650,679],{"title":202,"links":507,"subMenu":522},[508,512,517],{"text":509,"config":510},"View plans",{"href":204,"dataGaName":511,"dataGaLocation":489},"view plans",{"text":513,"config":514},"Why Premium?",{"href":515,"dataGaName":516,"dataGaLocation":489},"/pricing/premium/","why premium",{"text":518,"config":519},"Why Ultimate?",{"href":520,"dataGaName":521,"dataGaLocation":489},"/pricing/ultimate/","why ultimate",[523],{"title":524,"links":525},"Contact Us",[526,529,531,533,538,543,548],{"text":527,"config":528},"Contact sales",{"href":52,"dataGaName":53,"dataGaLocation":489},{"text":381,"config":530},{"href":383,"dataGaName":384,"dataGaLocation":489},{"text":386,"config":532},{"href":388,"dataGaName":389,"dataGaLocation":489},{"text":534,"config":535},"Status",{"href":536,"dataGaName":537,"dataGaLocation":489},"https://status.gitlab.com/","status",{"text":539,"config":540},"Terms of use",{"href":541,"dataGaName":542,"dataGaLocation":489},"/terms/","terms of use",{"text":544,"config":545},"Privacy statement",{"href":546,"dataGaName":547,"dataGaLocation":489},"/privacy/","privacy statement",{"text":549,"config":550},"Cookie preferences",{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":106},"cookie preferences","ot-sdk-btn",{"title":105,"links":554,"subMenu":562},[555,559],{"text":556,"config":557},"DevSecOps platform",{"href":70,"dataGaName":558,"dataGaLocation":489},"devsecops platform",{"text":128,"config":560},{"href":77,"dataGaName":561,"dataGaLocation":489},"ai-assisted development",[563],{"title":564,"links":565},"Topics",[566,571,576,581,586,591,596,601],{"text":567,"config":568},"CICD",{"href":569,"dataGaName":570,"dataGaLocation":489},"/topics/ci-cd/","cicd",{"text":572,"config":573},"GitOps",{"href":574,"dataGaName":575,"dataGaLocation":489},"/topics/gitops/","gitops",{"text":577,"config":578},"DevOps",{"href":579,"dataGaName":580,"dataGaLocation":489},"/topics/devops/","devops",{"text":582,"config":583},"Version Control",{"href":584,"dataGaName":585,"dataGaLocation":489},"/topics/version-control/","version control",{"text":587,"config":588},"DevSecOps",{"href":589,"dataGaName":590,"dataGaLocation":489},"/topics/devsecops/","devsecops",{"text":592,"config":593},"Cloud Native",{"href":594,"dataGaName":595,"dataGaLocation":489},"/topics/cloud-native/","cloud native",{"text":597,"config":598},"AI for Coding",{"href":599,"dataGaName":600,"dataGaLocation":489},"/topics/devops/ai-for-coding/","ai for coding",{"text":602,"config":603},"Agentic AI",{"href":604,"dataGaName":605,"dataGaLocation":489},"/topics/agentic-ai/","agentic ai",{"title":607,"links":608},"Solutions",[609,611,613,618,622,625,629,632,634,637,640,645],{"text":149,"config":610},{"href":144,"dataGaName":149,"dataGaLocation":489},{"text":138,"config":612},{"href":120,"dataGaName":121,"dataGaLocation":489},{"text":614,"config":615},"Agile development",{"href":616,"dataGaName":617,"dataGaLocation":489},"/solutions/agile-delivery/","agile delivery",{"text":619,"config":620},"SCM",{"href":134,"dataGaName":621,"dataGaLocation":489},"source code management",{"text":567,"config":623},{"href":126,"dataGaName":624,"dataGaLocation":489},"continuous integration & delivery",{"text":626,"config":627},"Value stream management",{"href":177,"dataGaName":628,"dataGaLocation":489},"value stream management",{"text":572,"config":630},{"href":631,"dataGaName":575,"dataGaLocation":489},"/solutions/gitops/",{"text":187,"config":633},{"href":189,"dataGaName":190,"dataGaLocation":489},{"text":635,"config":636},"Small business",{"href":194,"dataGaName":195,"dataGaLocation":489},{"text":638,"config":639},"Public sector",{"href":199,"dataGaName":200,"dataGaLocation":489},{"text":641,"config":642},"Education",{"href":643,"dataGaName":644,"dataGaLocation":489},"/solutions/education/","education",{"text":646,"config":647},"Financial services",{"href":648,"dataGaName":649,"dataGaLocation":489},"/solutions/finance/","financial services",{"title":207,"links":651},[652,654,656,658,661,663,665,667,669,671,673,675,677],{"text":219,"config":653},{"href":221,"dataGaName":222,"dataGaLocation":489},{"text":224,"config":655},{"href":226,"dataGaName":227,"dataGaLocation":489},{"text":229,"config":657},{"href":231,"dataGaName":232,"dataGaLocation":489},{"text":234,"config":659},{"href":236,"dataGaName":660,"dataGaLocation":489},"docs",{"text":257,"config":662},{"href":259,"dataGaName":5,"dataGaLocation":489},{"text":252,"config":664},{"href":254,"dataGaName":255,"dataGaLocation":489},{"text":261,"config":666},{"href":263,"dataGaName":264,"dataGaLocation":489},{"text":274,"config":668},{"href":276,"dataGaName":277,"dataGaLocation":489},{"text":266,"config":670},{"href":268,"dataGaName":269,"dataGaLocation":489},{"text":279,"config":672},{"href":281,"dataGaName":282,"dataGaLocation":489},{"text":284,"config":674},{"href":286,"dataGaName":287,"dataGaLocation":489},{"text":289,"config":676},{"href":291,"dataGaName":292,"dataGaLocation":489},{"text":294,"config":678},{"href":296,"dataGaName":297,"dataGaLocation":489},{"title":312,"links":680},[681,683,685,687,689,691,693,697,702,704,706,708],{"text":319,"config":682},{"href":321,"dataGaName":314,"dataGaLocation":489},{"text":324,"config":684},{"href":326,"dataGaName":327,"dataGaLocation":489},{"text":332,"config":686},{"href":334,"dataGaName":335,"dataGaLocation":489},{"text":337,"config":688},{"href":339,"dataGaName":340,"dataGaLocation":489},{"text":342,"config":690},{"href":344,"dataGaName":345,"dataGaLocation":489},{"text":347,"config":692},{"href":349,"dataGaName":350,"dataGaLocation":489},{"text":694,"config":695},"Sustainability",{"href":696,"dataGaName":694,"dataGaLocation":489},"/sustainability/",{"text":698,"config":699},"Diversity, inclusion and belonging (DIB)",{"href":700,"dataGaName":701,"dataGaLocation":489},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":703},{"href":354,"dataGaName":355,"dataGaLocation":489},{"text":362,"config":705},{"href":364,"dataGaName":365,"dataGaLocation":489},{"text":367,"config":707},{"href":369,"dataGaName":370,"dataGaLocation":489},{"text":709,"config":710},"Modern Slavery Transparency Statement",{"href":711,"dataGaName":712,"dataGaLocation":489},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":714},[715,717,719],{"text":539,"config":716},{"href":541,"dataGaName":542,"dataGaLocation":489},{"text":544,"config":718},{"href":546,"dataGaName":547,"dataGaLocation":489},{"text":549,"config":720},{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":106},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[726],{"_path":727,"_dir":728,"_draft":6,"_partial":6,"_locale":7,"content":729,"config":732,"_id":735,"_type":29,"title":15,"_source":31,"_file":736,"_stem":737,"_extension":34},"/en-us/blog/authors/radovan-bacovic","authors",{"name":15,"config":730},{"headshot":731},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1760036179/vvj2tiujit6kopuz8mqp.png",{"template":733,"gitlabHandle":734},"BlogAuthor","rbacovic","content:en-us:blog:authors:radovan-bacovic.yml","en-us/blog/authors/radovan-bacovic.yml","en-us/blog/authors/radovan-bacovic",{"_path":739,"_dir":37,"_draft":6,"_partial":6,"_locale":7,"header":740,"eyebrow":741,"blurb":742,"button":743,"secondaryButton":747,"_id":749,"_type":29,"title":750,"_source":31,"_file":751,"_stem":752,"_extension":34},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":45,"config":744},{"href":745,"dataGaName":48,"dataGaLocation":746},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":50,"config":748},{"href":52,"dataGaName":53,"dataGaLocation":746},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":754,"content":756,"config":759,"_id":28,"_type":29,"title":30,"_source":31,"_file":32,"_stem":33,"_extension":34},{"config":755,"title":10,"ogTitle":10,"description":11},{"noIndex":6},{"title":13,"description":11,"authors":757,"heroImage":16,"date":17,"body":18,"category":19,"tags":758},[15],[21,22,23,24],{"featured":6,"template":26,"slug":27},1761814421459]