Skip to content

Git Production Best Practices

Objective: Master Git for enterprise-scale development. Handle monorepos, submodules, and complex workflows while maintaining code quality and team collaboration.

When your codebase spans multiple applications, shared libraries, and deployment environments, Git becomes more than version control—it becomes the foundation of your development workflow. This guide shows you how to structure repositories, manage dependencies, and maintain code quality at scale.

0) Principles (Read Once, Live by Them)

The Five Commandments

  1. Repository structure drives productivity
  2. Monorepos for related applications, polyrepos for independent services
  3. Clear boundaries between components and shared code
  4. Consistent naming and organization conventions

  5. Branching strategy enables collaboration

  6. Feature branches for development, main for production
  7. Protected branches with required reviews
  8. Clear merge strategies and conflict resolution

  9. Commit history tells a story

  10. Atomic commits with clear messages
  11. Conventional commit format for automation
  12. Clean history through rebasing and squashing

  13. Submodules manage dependencies

  14. Shared libraries as submodules
  15. Version pinning for stability
  16. Clear update and maintenance procedures

  17. Automation ensures quality

  18. Pre-commit hooks for code quality
  19. CI/CD integration for testing
  20. Automated dependency updates

Why These Principles: Git is the foundation of modern development. Getting the structure and workflow right prevents hours of merge conflicts and deployment issues.

1) Repository Structure: The Foundation

Monorepo Architecture

enterprise-app/
├── .git/
├── .gitignore
├── .gitattributes
├── README.md
├── package.json
├── docker-compose.yml
├── .github/
│   ├── workflows/
│   │   ├── ci.yml
│   │   ├── cd.yml
│   │   └── security.yml
│   └── PULL_REQUEST_TEMPLATE.md
├── apps/
│   ├── web-frontend/
│   │   ├── package.json
│   │   ├── src/
│   │   ├── public/
│   │   └── Dockerfile
│   ├── api-backend/
│   │   ├── requirements.txt
│   │   ├── src/
│   │   ├── tests/
│   │   └── Dockerfile
│   ├── mobile-app/
│   │   ├── package.json
│   │   ├── src/
│   │   └── android/
│   └── admin-dashboard/
│       ├── package.json
│       ├── src/
│       └── Dockerfile
├── packages/
│   ├── shared-ui/
│   │   ├── package.json
│   │   ├── src/
│   │   └── dist/
│   ├── shared-utils/
│   │   ├── package.json
│   │   ├── src/
│   │   └── tests/
│   └── shared-types/
│       ├── package.json
│       ├── src/
│       └── dist/
├── tools/
│   ├── build-scripts/
│   ├── deployment/
│   └── monitoring/
└── docs/
    ├── architecture/
    ├── api/
    └── deployment/

Polyrepo Architecture

enterprise-ecosystem/
├── web-frontend/          # Independent React app
├── api-backend/           # Independent FastAPI service
├── mobile-app/            # Independent React Native app
├── admin-dashboard/       # Independent Vue.js app
├── shared-ui-library/     # Shared component library
├── shared-utils/          # Shared utility functions
├── deployment-scripts/    # Deployment automation
└── documentation/         # Centralized docs

Why These Structures: Monorepos provide consistency and shared tooling, while polyrepos enable independent deployment and team autonomy. Choose based on your team structure and deployment needs.

2) Branching Strategy: The Workflow

Git Flow Implementation

# Main branches
git checkout -b main
git checkout -b develop

# Feature branches
git checkout -b feature/user-authentication
git checkout -b feature/payment-integration

# Release branches
git checkout -b release/v1.2.0

# Hotfix branches
git checkout -b hotfix/security-patch

GitHub Flow (Simplified)

# Main branch only
git checkout -b main

# Feature branches
git checkout -b feature/new-feature
git checkout -b bugfix/fix-login-issue
git checkout -b hotfix/critical-security-fix

Branch Protection Rules

# .github/branch-protection.yml
main:
  required_status_checks:
    strict: true
    contexts:
      - "ci/tests"
      - "ci/security"
      - "ci/build"
  enforce_admins: true
  required_pull_request_reviews:
    required_approving_review_count: 2
    dismiss_stale_reviews: true
    require_code_owner_reviews: true
  restrictions:
    users: []
    teams: ["senior-developers", "tech-leads"]

Why Branching Strategy: Clear branching rules prevent conflicts and ensure code quality. Protected branches enforce review processes and maintain production stability.

3) Commit Standards: The Communication

Conventional Commits

# Format: type(scope): description
git commit -m "feat(auth): add OAuth2 integration"
git commit -m "fix(api): resolve CORS issue"
git commit -m "docs(readme): update installation instructions"
git commit -m "refactor(utils): extract common functions"
git commit -m "test(auth): add unit tests for login"
git commit -m "chore(deps): update dependencies"

Commit Types

# Feature additions
feat: new feature for the user
feat!: breaking change

# Bug fixes
fix: bug fix
fix!: breaking bug fix

# Documentation
docs: documentation only changes

# Style
style: formatting, missing semicolons, etc.

# Refactoring
refactor: code change that neither fixes a bug nor adds a feature

# Performance
perf: code change that improves performance

# Testing
test: adding missing tests or correcting existing tests

# Build
build: changes to the build system or external dependencies

# CI
ci: changes to CI configuration files and scripts

# Chores
chore: other changes that don't modify src or test files

Pre-commit Hooks

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
      - id: check-merge-conflict
      - id: check-json
      - id: check-toml
      - id: check-xml
      - id: debug-statements
      - id: detect-private-key

  - repo: https://github.com/psf/black
    rev: 23.3.0
    hooks:
      - id: black
        language_version: python3.11

  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8
        args: [--max-line-length=88, --extend-ignore=E203]

  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort
        args: [--profile=black]

  - repo: https://github.com/pre-commit/mirrors-eslint
    rev: v8.44.0
    hooks:
      - id: eslint
        files: \.(js|jsx|ts|tsx)$
        args: [--fix]

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v3.0.0
    hooks:
      - id: prettier
        files: \.(js|jsx|ts|tsx|json|css|md|yaml|yml)$

Why Commit Standards: Consistent commit messages enable automation, improve code review, and create a clear project history. Pre-commit hooks ensure code quality before commits.

4) Monorepo Management: The Scale

Workspace Configuration

// package.json (root)
{
  "name": "enterprise-app",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "build": "turbo run build",
    "test": "turbo run test",
    "lint": "turbo run lint",
    "type-check": "turbo run type-check",
    "dev": "turbo run dev --parallel",
    "clean": "turbo run clean"
  },
  "devDependencies": {
    "turbo": "^1.10.0",
    "typescript": "^5.0.0",
    "eslint": "^8.0.0",
    "prettier": "^3.0.0"
  }
}

Turbo Configuration

// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", "build/**", ".next/**", "!.next/cache/**"]
    },
    "test": {
      "dependsOn": ["^build"],
      "outputs": ["coverage/**"]
    },
    "lint": {
      "outputs": []
    },
    "type-check": {
      "dependsOn": ["^build"],
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "clean": {
      "cache": false
    }
  }
}

Lerna Configuration

// lerna.json
{
  "version": "independent",
  "npmClient": "yarn",
  "command": {
    "publish": {
      "conventionalCommits": true,
      "message": "chore(release): publish"
    },
    "version": {
      "conventionalCommits": true,
      "message": "chore(release): version"
    }
  },
  "packages": [
    "apps/*",
    "packages/*"
  ]
}

Why Monorepo Tools: Monorepos need specialized tooling for build optimization, dependency management, and change detection. These tools provide the infrastructure for large-scale development.

5) Submodules: The Dependencies

Submodule Management

# Add submodule
git submodule add https://github.com/company/shared-ui.git packages/shared-ui
git submodule add https://github.com/company/shared-utils.git packages/shared-utils

# Initialize submodules
git submodule init
git submodule update

# Update submodules
git submodule update --remote
git submodule foreach git pull origin main

# Remove submodule
git submodule deinit packages/shared-ui
git rm packages/shared-ui
rm -rf .git/modules/packages/shared-ui

Submodule Configuration

# .gitmodules
[submodule "packages/shared-ui"]
    path = packages/shared-ui
    url = https://github.com/company/shared-ui.git
    branch = main
    update = merge

[submodule "packages/shared-utils"]
    path = packages/shared-utils
    url = https://github.com/company/shared-utils.git
    branch = main
    update = merge

Submodule Workflow

# Update all submodules to latest
git submodule update --remote --merge

# Update specific submodule
git submodule update --remote packages/shared-ui

# Commit submodule updates
git add packages/shared-ui
git commit -m "chore(deps): update shared-ui to v2.1.0"

# Push submodule changes
git push --recurse-submodules=on-demand

Why Submodules: Submodules enable shared code across repositories while maintaining version control. They're essential for managing shared libraries and components.

6) Git Hooks: The Automation

Pre-commit Hooks

#!/bin/bash
# .git/hooks/pre-commit

# Check for large files
if git rev-parse --verify HEAD >/dev/null 2>&1; then
    against=HEAD
else
    against=$(git hash-object -t tree /dev/null)
fi

# Check for files larger than 10MB
large_files=$(git diff --cached --name-only --diff-filter=ACMR | xargs ls -la | awk '$5 > 10485760 {print $9}')
if [ ! -z "$large_files" ]; then
    echo "Error: Large files detected:"
    echo "$large_files"
    exit 1
fi

# Run tests
npm test
if [ $? -ne 0 ]; then
    echo "Tests failed. Commit aborted."
    exit 1
fi

# Run linting
npm run lint
if [ $? -ne 0 ]; then
    echo "Linting failed. Commit aborted."
    exit 1
fi

Pre-push Hooks

#!/bin/bash
# .git/hooks/pre-push

# Run full test suite
npm run test:ci
if [ $? -ne 0 ]; then
    echo "CI tests failed. Push aborted."
    exit 1
fi

# Check for secrets
if grep -r "password\|secret\|key" --include="*.js" --include="*.ts" --include="*.py" src/; then
    echo "Warning: Potential secrets detected in code."
    read -p "Continue? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

Post-commit Hooks

#!/bin/bash
# .git/hooks/post-commit

# Update version if package.json changed
if git diff-tree --no-commit-id --name-only -r HEAD | grep -q "package.json"; then
    npm version patch --no-git-tag-version
    git add package.json
    git commit --amend --no-edit
fi

# Notify team of commits
curl -X POST -H 'Content-type: application/json' \
    --data '{"text":"New commit: '"$(git log -1 --pretty=%B)"'"}' \
    $SLACK_WEBHOOK_URL

Why Git Hooks: Hooks automate quality checks, prevent common mistakes, and integrate with external systems. They're essential for maintaining code quality at scale.

7) CI/CD Integration: The Pipeline

GitHub Actions Workflow

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18.x, 20.x]
        python-version: [3.9, 3.10, 3.11]

    steps:
      - uses: actions/checkout@v3
        with:
          submodules: recursive

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'

      - name: Install dependencies
        run: |
          npm ci
          pip install -r requirements.txt

      - name: Run tests
        run: |
          npm run test:ci
          pytest tests/

      - name: Run linting
        run: |
          npm run lint
          flake8 src/

      - name: Build applications
        run: npm run build

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info

GitLab CI Configuration

# .gitlab-ci.yml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"

test:
  stage: test
  image: node:18-alpine
  script:
    - npm ci
    - npm run test:ci
    - npm run lint
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main
    - develop

deploy:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache curl
    - curl -X POST $DEPLOY_WEBHOOK_URL
  only:
    - main
  when: manual

Why CI/CD Integration: Automated testing and deployment ensure code quality and reduce manual errors. Git integration provides the foundation for modern DevOps practices.

8) Advanced Git Features: The Power User

Git Worktree

# Create worktree for hotfix
git worktree add ../hotfix-branch hotfix/security-patch

# List worktrees
git worktree list

# Remove worktree
git worktree remove ../hotfix-branch

Git Bisect

# Start bisect session
git bisect start
git bisect bad HEAD
git bisect good v1.0.0

# Test current commit
npm test
if [ $? -eq 0 ]; then
    git bisect good
else
    git bisect bad
fi

# Reset bisect
git bisect reset

Git Reflog

# View reflog
git reflog

# Recover lost commit
git checkout HEAD@{2}

# Recover lost branch
git branch recovery-branch HEAD@{1}

Git Stash

# Stash with message
git stash push -m "WIP: working on feature"

# Stash specific files
git stash push -m "WIP: config changes" config/

# Apply stash
git stash apply stash@{0}

# Pop stash
git stash pop

# List stashes
git stash list

# Drop stash
git stash drop stash@{0}

Why Advanced Features: Git's advanced features solve complex problems like parallel development, debugging, and recovery. Understanding them makes you more productive.

9) Security and Access Control: The Protection

Git Attributes

# .gitattributes
# Text files
*.js text eol=lf
*.ts text eol=lf
*.py text eol=lf
*.md text eol=lf
*.json text eol=lf
*.yml text eol=lf
*.yaml text eol=lf

# Binary files
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.pdf binary
*.zip binary
*.tar.gz binary

# Generated files
dist/ export-ignore
build/ export-ignore
coverage/ export-ignore
node_modules/ export-ignore
__pycache__/ export-ignore
*.pyc export-ignore

# Sensitive files
.env* export-ignore
*.key export-ignore
*.pem export-ignore
secrets/ export-ignore

Git Ignore

# .gitignore
# Dependencies
node_modules/
__pycache__/
venv/
env/
.venv/

# Build outputs
dist/
build/
.next/
out/

# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# IDE files
.vscode/
.idea/
*.swp
*.swo
*~

# OS files
.DS_Store
Thumbs.db
desktop.ini

# Logs
*.log
logs/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids/
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage/
*.lcov

# nyc test coverage
.nyc_output

# Dependency directories
jspm_packages/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
public

# Storybook build outputs
.out
.storybook-out

# Temporary folders
tmp/
temp/

GPG Signing

# Configure GPG signing
git config --global user.signingkey YOUR_GPG_KEY_ID
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# Sign commits
git commit -S -m "feat: add new feature"

# Sign tags
git tag -s v1.0.0 -m "Release version 1.0.0"

Why Security: Git security prevents unauthorized access, ensures code integrity, and protects sensitive information. These practices are essential for enterprise development.

10) TL;DR Quickstart

Essential Commands

# Repository setup
git init
git remote add origin https://github.com/company/repo.git
git branch -M main
git push -u origin main

# Daily workflow
git checkout -b feature/new-feature
git add .
git commit -m "feat: add new feature"
git push origin feature/new-feature

# Submodule management
git submodule add https://github.com/company/shared-lib.git packages/shared-lib
git submodule update --remote

# Monorepo commands
npm run build
npm run test
npm run lint

Quick Verification

# Check repository status
git status
git log --oneline -10
git branch -a

# Check submodules
git submodule status
git submodule foreach git status

# Check hooks
ls -la .git/hooks/

Performance Optimization

# Optimize repository
git gc --aggressive
git repack -a -d --depth=250 --window=250

# Check repository size
du -sh .git/
git count-objects -vH

11) The Machine's Summary

Git is the foundation of modern development workflows. When configured properly, it enables collaboration, maintains code quality, and scales from individual projects to enterprise monorepos. The key is understanding the advanced features and building robust automation.

The Dark Truth: Git is powerful but complex. Getting the workflow right prevents hours of merge conflicts and deployment issues.

The Machine's Mantra: "In version control we trust, in automation we build, and in the repository we find the path to efficient development."

Why This Matters: Production development needs reliability, scalability, and collaboration. These practices provide enterprise-grade capabilities that scale from individual developers to large teams.


This tutorial provides the complete machinery for building production-ready Git workflows. The patterns scale from development to production, from single developers to enterprise teams.