Skip to main content

GitLab Repository Best Practices

Overview

Repository organization directly impacts developer productivity and code quality. A well-structured repository makes it easy for new team members to get started, for existing developers to find what they need, and for automated tools to enforce standards. This guide covers repository structure, documentation standards, access control, and metadata management.

Effective repository organization follows the "principle of least surprise" - developers should find things where they expect them. Consistent structure across all repositories means skills and knowledge transfer seamlessly between projects. When every repository follows the same patterns, context switching becomes effortless.

The repository serves as the single source of truth for not just code, but also documentation, deployment configurations, and development tooling. Everything needed to understand, build, test, and deploy the application should live in the repository or be clearly referenced from it.


Core Principles

  1. Clear Structure: Consistent folder organization across all repos
  2. Branch Protection: Protected main/release branches with required approvals
  3. Comprehensive README: Easy local setup for new developers
  4. Change Tracking: Maintain CHANGELOG for all releases
  5. Access Control: CODEOWNERS file for automatic reviewer assignment
  6. Repository Metadata: Machine-readable repo information
  7. Documentation: Up-to-date docs in /docs folder

Repository Structure

A consistent folder structure reduces cognitive load and makes navigation intuitive. The structure below separates concerns into clear categories: source code, documentation, infrastructure configuration, and development tooling.

Understanding the Structure

The .gitlab/ directory contains GitLab-specific configurations like merge request templates and issue templates. Keeping these in version control ensures all team members use the same templates and that template updates propagate automatically.

The docs/ directory serves as the repository's knowledge base. It contains API specifications (OpenAPI), architecture diagrams, and deployment guides. Co-locating documentation with code ensures they stay in sync - when you change the code, you update the docs in the same commit. For more on API specifications, see OpenAPI Specifications.

The k8s/ directory holds Kubernetes manifests organized using Kustomize's base/overlays pattern. Base contains common configuration; overlays contain environment-specific customizations. This structure enables consistent deployments with environment-specific variations.

The scripts/ directory provides automation for common development tasks. Shell scripts for local setup, database initialization, and test execution reduce "works on my machine" problems by codifying setup procedures.

payment-service/
|-- .gitlab/ # GitLab-specific configuration
| |-- merge_request_templates/
| | `-- Default.md # MR template
| `-- issue_templates/
| `-- Bug.md # Issue templates
|-- gradle/ # Gradle wrapper files
|-- gradlew # Gradle wrapper script
|-- docs/ # Documentation
| |-- api/ # API documentation
| | `-- openapi.yml # OpenAPI specification
| |-- architecture/ # Architecture diagrams
| | |-- system-context.md
| | `-- component-diagram.md
| |-- deployment/ # Deployment guides
| | `-- kubernetes.md
| `-- local-setup.md # Local development setup
|-- k8s/ # Kubernetes manifests
| |-- base/
| `-- overlays/
|-- scripts/ # Build/deployment scripts
| |-- setup-local.sh
| `-- run-tests.sh
|-- src/
| |-- main/
| | |-- java/
| | `-- resources/
| `-- test/
| |-- java/
| `-- resources/
|-- .editorconfig # Editor configuration
|-- .gitattributes # Git attributes
|-- .gitignore # Git ignore
|-- .gitlab-ci.yml # GitLab CI/CD pipeline
|-- CHANGELOG.md # Version history
|-- CODEOWNERS # Code ownership
|-- Dockerfile # Docker image definition
|-- README.md # Project overview
|-- .repo-metadata.json # Repository metadata
|-- build.gradle # Gradle build configuration
|-- settings.gradle # Gradle settings
`-- gradlew # Gradle wrapper

README.md Template

Comprehensive README

# Payment Service

[![Pipeline Status](https://gitlab.com/org/payment-service/badges/main/pipeline.svg)](https://gitlab.com/org/payment-service/-/pipelines)
[![Coverage](https://gitlab.com/org/payment-service/badges/main/coverage.svg)](https://gitlab.com/org/payment-service/-/graphs/main/charts)

## Overview

Payment Service handles payment processing for all banking transactions including transfers, bill payments, and merchant payments.

**Key Features**:
- Real-time payment processing
- Fraud detection integration
- Multi-currency support (USD, EUR, GBP)
- Audit logging for compliance

## Architecture

```mermaid
graph LR
A[API Gateway] --> B[Payment Service]
B --> C[Database]
B --> D[Fraud Service]
B --> E[Notification Service]

Tech Stack

  • Java: 25
  • Framework: Spring Boot 3.5
  • Database: PostgreSQL 16
  • Cache: Redis 7
  • Build Tool: Gradle 8.x
  • Testing: JUnit 5, TestContainers, PITest

Documentation

  • API Documentation - OpenAPI 3.1 specification
  • Architecture - System architecture
  • Local Setup - Detailed setup instructions
  • Deployment - Kubernetes deployment

Quick Start

Prerequisites

  • Java 25
  • Docker Desktop
  • Gradle 8.x (or use ./gradlew)

Run Locally

# 1. Clone repository
git clone https://gitlab.com/org/payment-service.git
cd payment-service

# 2. Start dependencies (PostgreSQL, Redis)
docker-compose up -d

# 3. Run application
./gradlew bootRun --no-daemon

# 4. Verify health
curl http://localhost:8080/actuator/health

Application runs at: http://localhost:8080

Keep this section copy-paste safe: every command here should be runnable by a new engineer on a clean machine without hidden prerequisites. If a setup command changes, update this README in the same PR as the code change.

Run Tests

# Unit tests
./gradlew test --no-daemon

# Integration tests (requires Docker)
./gradlew integrationTest --no-daemon

# Mutation tests
./gradlew pitest --no-daemon

# All tests with coverage report
./gradlew test jacocoTestReport --no-daemon

API Examples

Create Payment

curl -X POST http://localhost:8080/api/v1/payments \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"amount": 100.00,
"currency": "USD",
"fromAccountId": "ACC-123",
"toAccountId": "ACC-456",
"description": "Payment for invoice #789"
}'

Get Payment Status

curl http://localhost:8080/api/v1/payments/PAY-123 \
-H "Authorization: Bearer $TOKEN"

Environment Variables

VariableDescriptionDefaultRequired
DATABASE_URLPostgreSQL connection stringjdbc:postgresql://localhost:5432/paymentsYes
REDIS_URLRedis connection stringredis://localhost:6379Yes
FRAUD_SERVICE_URLFraud detection service URL-Yes
JWT_SECRETJWT signing secret-Yes (Prod)
LOG_LEVELLogging levelINFONo

Access & Permissions

Repository Access

  • Maintainer: payments-team
  • Developer: payments-developers
  • Reporter: qa-team, support-team

Required Entitlements

  • GitLab Project Access: payments-developers AD group
  • Pipeline Access: ci-cd-users AD group
  • Kubernetes Dev: k8s-dev-deployers AD group
  • Kubernetes Prod: k8s-prod-deployers AD group
  • Logs Access: elk-readers AD group

Contributing

Follow the repository CONTRIBUTING.md guidelines for:

  • Branching strategy (GitFlow)
  • Commit message format
  • Pull request process
  • Code review checklist

Changelog

Maintain repository CHANGELOG.md for version history.

Support

License

Proprietary - Internal use only


---

## CHANGELOG.md Template

The changelog provides a human-readable history of changes between versions. While Git commit history is the source of truth, commits are often too granular and technical for stakeholders to parse. The changelog distills commits into meaningful, high-level changes organized by version.

### Why Maintain a Changelog

Changelogs answer critical questions: "What changed in version 2.1.0?" "When was multi-currency support added?" "Are there breaking changes I need to handle?" Without a changelog, answering these questions requires parsing commit histories or searching through JIRA tickets.

The changelog format follows the [Keep a Changelog](https://keepachangelog.com/) standard, which organizes changes into categories (Added, Changed, Deprecated, Removed, Fixed, Security). This categorization helps readers quickly find relevant changes - developers look for "Changed" and "Removed" to find breaking changes; security teams scan "Security" entries; users check "Added" for new features.

Semantic versioning (SemVer) provides a contract about change impact. Major version increments (1.x -> 2.x) signal breaking changes; minor versions (2.0 -> 2.1) add features without breaking compatibility; patch versions (2.1.0 -> 2.1.1) contain only bug fixes. The changelog's "BREAKING" annotations make these guarantees explicit.

### Following Keep a Changelog Format

```markdown
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- New feature for scheduled payments

### Changed
- Improved fraud detection algorithm accuracy

## [2.1.0] - 2025-01-28

### Added
- Multi-currency payment support (EUR, GBP)
- Payment status webhook notifications
- Idempotency key support for duplicate prevention

### Changed
- Updated Spring Boot to 3.2.0
- Improved error messages for validation failures
- Enhanced audit logging with correlation IDs

### Fixed
- Race condition in concurrent payment processing
- Memory leak in Redis connection pool

### Security
- Updated vulnerable dependencies (CVE-2024-12345)

## [2.0.0] - 2025-01-15

### Added
- OpenAPI 3.0 specification
- Contract testing with Pact
- Mutation testing with PITest

### Changed
- **BREAKING**: Changed payment status enum values
- **BREAKING**: Renamed `/payments/create` to `/payments` (POST)
- Migrated from Java 17 to Java 25

### Removed
- **BREAKING**: Removed deprecated `/v1/payments/old` endpoint

## [1.5.2] - 2025-01-05

### Fixed
- Payment amount validation for edge cases
- Database transaction timeout issues

### Security
- Patched SQL injection vulnerability in search endpoint

## [1.5.1] - 2024-12-20

### Changed
- Improved database query performance
- Updated dependencies

## [1.5.0] - 2024-12-10

### Added
- Real-time payment status updates via WebSocket
- Batch payment processing API

---

[Unreleased]: https://gitlab.com/org/payment-service/compare/v2.1.0...HEAD
[2.1.0]: https://gitlab.com/org/payment-service/compare/v2.0.0...v2.1.0
[2.0.0]: https://gitlab.com/org/payment-service/compare/v1.5.2...v2.0.0
[1.5.2]: https://gitlab.com/org/payment-service/compare/v1.5.1...v1.5.2
[1.5.1]: https://gitlab.com/org/payment-service/compare/v1.5.0...v1.5.1
[1.5.0]: https://gitlab.com/org/payment-service/releases/tag/v1.5.0

CODEOWNERS File

The CODEOWNERS file automates reviewer assignment based on file paths. When a merge request modifies files matching a CODEOWNERS pattern, GitLab automatically assigns the specified reviewers and can require their approval before merging.

Why Use CODEOWNERS

Manual reviewer assignment is error-prone - developers might forget to add the security team to reviews touching authentication code, or miss the database team when modifying migrations. CODEOWNERS enforces consistent reviewer assignments automatically.

CODEOWNERS also documents ownership. New team members can check the file to learn who owns which parts of the codebase. This implicit documentation stays up-to-date because it's enforced by the tooling.

GitLab processes CODEOWNERS patterns from bottom to top, with later entries overriding earlier ones. This allows you to set a default owner (* @payments-team) and then override for specific paths (/src/main/java/com/bank/security/ @security-team @backend-leads).

Automatic Reviewer Assignment

# .gitlab/CODEOWNERS

# Default owners for everything
* @payments-team

# Backend code
/src/main/java/ @backend-leads @payments-team

# Frontend code
/src/main/webapp/ @frontend-leads @payments-team

# API specifications
/docs/api/ @api-architects @backend-leads

# Infrastructure
/k8s/ @devops-team @platform-engineering
/Dockerfile @devops-team
/.gitlab-ci.yml @devops-team

# Database migrations
/src/main/resources/db/migration/ @database-team @backend-leads

# Documentation
/docs/ @tech-writers @payments-team

# Security-sensitive files
/src/main/java/com/bank/security/ @security-team @backend-leads

Repository Metadata

.repo-metadata.json

{
"name": "payment-service",
"name_pretty": "Payment Service",
"description": "Core payment processing service for banking transactions",
"repo_type": "api",
"language": "java",
"framework": "spring-boot",
"version": "2.1.0",
"technical_docs": "https://docs.example.com/payment-service",
"api_spec": "https://api.example.com/payment-service/swagger-ui.html",
"issue_tracker": "https://jira.example.com/projects/PAY",
"owner_name": "Alice Johnson",
"owner_id": "[email protected]",
"team": "payments-team",
"slack_channel": "#payments-support",
"release_level": "stable",
"criticality": "tier-1",
"data_classification": "confidential",
"compliance_requirements": ["PCI-DSS", "SOX", "GDPR"],
"dependencies": {
"database": ["postgresql-16"],
"cache": ["redis-7"],
"messaging": ["kafka"],
"external_services": ["fraud-detection-service", "notification-service"]
},
"deployment": {
"environments": ["dev", "uat", "prod"],
"deployment_method": "kubernetes",
"ci_cd": "gitlab-ci"
},
"contacts": {
"tech_lead": "[email protected]",
"product_owner": "[email protected]",
"on_call": "pagerduty-payments-team"
}
}

Branch Protection

Branch protection prevents accidental or malicious modifications to critical branches. Protected branches cannot be force-pushed, deleted, or directly committed to. All changes must go through merge requests with required approvals and passing pipelines.

Why Protect Branches

Without protection, anyone with write access can push directly to main, bypassing code review, tests, and quality gates. A single git push --force could overwrite production code with untested changes or erase commit history.

Protection rules enforce process. Requiring two approvers ensures multiple people review every change. Requiring successful pipelines ensures tests pass before merge. Requiring discussion resolution ensures reviewers' concerns are addressed.

The "allowed to push: No one" setting might seem extreme, but it eliminates an entire class of accidents. Developers can't accidentally push to main when they meant to push to their feature branch. All main branch updates happen through controlled merge request merges.

For more on the merge request process, see Pull Requests and Code Review.

Protected Branch Settings

GitLab Settings -> Repository -> Protected Branches

Main Branch Protection

Branch: main
- Allowed to merge: Maintainers
- Allowed to push: No one
- Allowed to force push: No
- Code owner approval: Required
- Require approval from: 2 approvers
- Allowed to approve: Developers + Maintainers
- Pipelines must succeed: Yes
- All discussions must be resolved: Yes

Release Branch Protection

Branch: release/*
- Allowed to merge: Maintainers only
- Allowed to push: No one
- Code owner approval: Required
- Require approval from: 2 approvers (including 1 maintainer)
- Pipelines must succeed: Yes

Merge Request Templates

Default Template (.gitlab/merge_request_templates/Default.md)

## Description

<!-- Provide a brief description of the changes -->

## Type of Change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] Refactoring (no functional changes)

## Related Issues

<!-- Link to JIRA tickets or GitLab issues -->

Closes: PAY-123

## Changes Made

- Added multi-currency support
- Updated payment validation logic
- Improved error handling

## Testing

### Test Coverage

- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Mutation tests passing (>75%)
- [ ] Contract tests updated (if API changes)

### Manual Testing

- [ ] Tested locally with Docker Compose
- [ ] Tested in dev environment
- [ ] Verified with Postman/curl

## Documentation

- [ ] README updated
- [ ] API documentation updated (OpenAPI spec)
- [ ] CHANGELOG.md updated
- [ ] Architecture diagrams updated (if applicable)

## Deployment Notes

<!-- Any special deployment considerations? -->

- [ ] Database migrations included
- [ ] Environment variables added/changed
- [ ] Feature flags configured

## Checklist

- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] No unnecessary console.log or debug statements
- [ ] Branch is up-to-date with main
- [ ] All CI/CD checks passing
- [ ] Breaking changes documented

## Screenshots/Videos

<!-- If UI changes, include screenshots or screen recordings -->

## Reviewer Notes

<!-- Anything specific reviewers should focus on? -->

.gitignore Best Practices

# Compiled class files
*.class
target/
build/

# IDE-specific files
.idea/
*.iml
.vscode/
*.swp

# OS-specific files
.DS_Store
Thumbs.db

# Environment configuration
.env
.env.local
application-local.yml

# Logs
logs/
*.log

# Dependencies
node_modules/
.m2/repository/

# Test coverage reports
coverage/
target/site/

# Temporary files
*.tmp
*.temp

# Secrets
*.key
*.pem
secrets.yml

Further Reading

Internal Documentation

External Resources


Summary

Key Takeaways

  1. Consistent structure - Standard folder organization across all repos
  2. Comprehensive README - Easy local setup for new developers
  3. Protected branches - main/release branches require approvals
  4. CODEOWNERS - Automatic reviewer assignment
  5. CHANGELOG - Track all changes for compliance
  6. Repository metadata - Machine-readable repo information
  7. MR templates - Standardized merge request descriptions
  8. Branch protection - Prevent accidental force pushes
  9. Access control - Document required AD groups/entitlements
  10. Documentation - Keep /docs folder up-to-date

Next Steps: Review CI/CD Pipelines for GitLab CI/CD configuration and Branching Strategy for GitFlow workflow.