Commit Guidelines
This guide outlines the commit message conventions for the TimeTiles project. Following these guidelines ensures consistency, improves readability, and helps with automated tooling.
Commit Message Format
Each commit message consists of a title and an optional body:
<type>(<scope>): <subject>
<body>
Title (Required)
The title is the first line of your commit message and must:
- Be no more than 72 characters
- Start with a type and optional scope
- Use the imperative mood ("add" not "adds" or "added")
- Not end with a period
Type
The type must be one of the following:
Feature & Fixes:
- feat: A new feature for the user (not a new feature for build script)
- Example:
feat(import): add support for Excel file uploads
- Example:
- fix: A bug fix for the user (not a fix to a build script)
- Example:
fix(geocoding): handle addresses with special characters
- Example:
Code Quality:
- refactor: Code changes that neither fix bugs nor add features
- Example:
refactor(api): extract validation logic into middleware
- Example:
- style: Code style changes (formatting, missing semicolons, whitespace)
- Example:
style(ui): fix indentation in button component
- Note: NOT for CSS/UI styling - use feat or fix for those
- Example:
- perf: Performance improvements
- Example:
perf(import): reduce memory usage by streaming CSV files
- Example:
Testing & Documentation:
- test: Adding missing tests or correcting existing tests
- Example:
test(events): add unit tests for date validation
- Example:
- docs: Documentation only changes
- Example:
docs(api): update endpoint examples with new fields
- Example:
Maintenance & Operations:
- build: Changes affecting build system or external dependencies
- Example:
build(deps): upgrade to Next.js 15
- Example:
build(docker): optimize production image size
- Example:
- chore: Other changes that don't modify src or test files
- Example:
chore(scripts): update seed data generation
- Example:
Special Types:
- revert: Reverts a previous commit
- Example:
revert: feat(import): add Excel support
- Body should include:
This reverts commit <hash>
- Example:
- security: Security fixes or improvements
- Example:
security(auth): fix JWT token expiration issue
- Example:
security(import): add file type validation
- Example:
Scope (Optional but Recommended)
The scope provides additional context about what part of the codebase changed. Scopes are organized into three categories:
Apps & Packages (where the code lives):
- web: Next.js web application
- docs: Documentation site (Nextra)
- ui: Shared UI components package
- config: Configuration packages (ESLint, TypeScript, etc.)
Core Features (what functionality is affected):
- import: File import system (CSV/Excel processing)
- geocoding: Address geocoding and location services
- events: Event data management
- catalogs: Catalog organization
- datasets: Dataset operations
- auth: Authentication & authorization
- cache: Caching strategies (location cache, etc.)
Technical Areas (infrastructure & tooling):
- db: Database operations, migrations, PostGIS
- api: API endpoints (REST, GraphQL, tRPC)
- jobs: Background job processing and queues
- types: TypeScript type definitions and interfaces
- testing: Test infrastructure and utilities (NOT individual test files)
- deps: External dependencies (typically used with
build
type) - docker: Docker, containerization, and orchestration
- scripts: Utility and automation scripts
- ci: CI configuration files and scripts
Subject
The subject contains a succinct description of the change:
- Use the imperative mood
- Start with lowercase (proper nouns allowed: GitHub, PostgreSQL, TypeScript, etc.)
- No period at the end
- Be clear and concise
Body (Optional but Recommended)
The body should use bullet points to clearly describe the changes. Use verbosity scaling based on the complexity of your changes:
When to Include a Body
Title-only commits (no body needed):
- Simple, self-explanatory changes
- Single-file formatting fixes
- Minor typos or corrections
- Dependency updates without breaking changes
- Examples:
fix(import): prevent duplicate events
,style(ui): format button component
Commits with bodies (bullet points recommended):
- Multiple related changes in one commit
- Complex logic changes requiring explanation
- New features with multiple aspects
- Bug fixes that needed investigation
- Performance improvements with measurable impact
- Breaking changes (always require explanation)
Body Format Guidelines
- Start each point with a dash (-)
- Keep each bullet point concise and focused (one concept per bullet)
- Use as many bullet points as needed to fully describe the changes
- Include context about why changes were made when it's not obvious
- Avoid unnecessary formatting (Git tools don't render markdown well)
- Use plain text with minimal formatting for maximum compatibility
- Mark breaking changes with "BREAKING CHANGE:" as a separate paragraph
Wrap the body at 100 characters per line.
Examples
Simple Fix
fix(import): handle empty CSV files gracefully
Feature with Description
feat(geocoding): add support for OpenCage provider
- Added OpenCage as a third geocoding provider option
- Provides better coverage for European addresses
- Returns confidence scores with geocoding results
- Follows existing provider pattern for consistency
- Includes automatic fallback when configured
- Supports all existing cache mechanisms
Breaking Change
refactor(api): change import endpoint response format
- Changed /api/import/upload response structure
- Now returns complete job object instead of just import ID
- Includes detailed progress tracking information
- Adds support for real-time status updates
- Improves error reporting with structured error objects
BREAKING CHANGE: API clients need to update to handle the new
response format. The import ID is now at response.job.importId
instead of response.importId.
Multiple Changes
fix(web): correct type errors and improve error handling
- Fixed TaskStatus type in import-integration tests
- Added proper error boundaries to import components
- Improved error messages for failed geocoding attempts
- Updated error logging to include more context
- Added retry logic for transient failures
Refactoring with Context
refactor(geocoding): simplify provider initialization
- Extracted provider configuration to separate modules
- Removed duplicate validation logic across providers
- Consolidated error handling into base provider class
- Improved type safety with stricter interfaces
- Reduced initialization time from ~200ms to ~50ms
Bug Fix with Investigation Details
fix(import): resolve memory leak in large file processing
- Identified leak in CSV parser stream handling
- Fixed by properly closing streams after processing
- Added explicit garbage collection hints for large batches
- Reduced memory usage by ~60% for files over 100MB
- Added monitoring to detect future memory issues
Writing Quality Commit Messages
Good commit messages tell a story about your changes. They should be written for future maintainers (including yourself) who need to understand the reasoning behind changes.
Mechanical vs. Thoughtful Approach
❌ Mechanical (just listing what changed):
fix(import): update import-jobs.ts and add validation
- Modified fileParsingJob function
- Added new validation checks
- Updated error handling code
- Changed batch processing logic
✅ Thoughtful (explaining why and impact):
fix(import): prevent memory leaks in large file processing
- Added stream cleanup to prevent memory accumulation
- Implemented batch size limits for files over 100MB
- Enhanced error recovery to handle partial failures gracefully
- Reduced memory usage by 60% for large CSV imports
Focus on Business Value
❌ Technical details without context:
refactor(geocoding): extract provider logic into separate classes
- Created GoogleProvider class
- Created NominatimProvider class
- Updated GeocodingService to use new providers
- Modified tests to work with new structure
✅ Business value with technical context:
refactor(geocoding): improve provider extensibility for future integrations
- Extracted provider logic into pluggable classes
- Enables easy addition of new geocoding services
- Standardized error handling across all providers
- Reduced provider initialization time by 40%
Formatting and Text Styling
Since Git tools (GitHub, GitLab, terminal git log, etc.) have varying markdown support, keep formatting minimal:
✅ Good formatting practices:
- Use plain text for maximum compatibility
- Bullet points with simple dashes (-)
- ALL CAPS for emphasis when needed (e.g., "BREAKING CHANGE:")
- Simple parentheses for examples: (fixes #123)
- Avoid bold, italic, or other markdown formatting in commit messages
❌ Avoid in commit messages:
- Bold text - not consistently rendered
- Italic text - not consistently rendered
Code blocks
- can appear as plain text with backticks- Links - become plain text in many git tools
- Complex formatting that relies on markdown parsing
Exception: These guidelines (this documentation) use markdown formatting for readability, but actual commit messages should be plain text.
Choosing the Right Type
When deciding between types, consider:
- feat vs fix: Is this adding new functionality (feat) or correcting existing behavior (fix)?
- fix vs refactor: Does this change user-facing behavior (fix) or just code structure (refactor)?
- build vs chore: Does this affect how the project builds (build) or is it general maintenance (chore)?
- style vs refactor: Is this only formatting (style) or restructuring code logic (refactor)?
- security vs fix: Is this addressing a security vulnerability (security) or a general bug (fix)?
Handling Type/Scope Overlaps
Some combinations need special attention:
docs(docs)
: Valid when updating documentation app code- Example:
docs(docs): fix broken link in navigation
- Example:
test(testing)
: Use for test infrastructure changes- Example:
test(testing): add custom vitest matchers
- Example:
test(web)
: Use for adding tests to web app- Example:
test(web): add unit tests for import flow
- Example:
- CI/CD changes with
(ci)
scope:build(ci)
: CI infrastructure and configuration changesfix(ci)
: Fix broken CI pipelines or workflowsfeat(ci)
: Add new CI features (e.g., new checks, deployment stages)chore(ci)
: Routine CI maintenance (e.g., update action versions)
build
without scope: Use for webpack, bundling, compilation changesbuild(deps)
vschore(deps)
:- Use
build(deps)
for production dependencies - Use
chore(deps)
for development-only dependencies
- Use
style
type: For code formatting only, NOT CSS/visual changes- CSS changes:
feat(ui): update button styles
- Code formatting:
style(api): fix indentation
- CSS changes:
Best Practices
- Atomic Commits: Each commit should represent one logical change
- Focus on "Why" Not "What": Explain the motivation and impact, not just what files changed
- ❌ Bad: "Updated GeocodingService.ts, added new provider, modified config"
- ✅ Good: "Add OpenCage provider for better European address coverage"
- Use Bullet Points: Structure your commit body with clear, concise bullet points
- Be Specific: Each bullet point should describe a specific change or aspect
- Avoid Mechanical Descriptions: Don't just list file changes or technical details
- ❌ Bad: "Modified 3 files, added 2 functions, updated 1 test"
- ✅ Good: "Implement rate limiting to prevent API quota exhaustion"
- Choose the Right Scope:
- Use app/package scopes for changes isolated to that codebase
- Use feature scopes for business logic changes
- Use technical scopes for infrastructure/tooling changes
- When changes span multiple areas, pick the primary scope
- Test Your Changes: Ensure tests pass before committing
- Review Before Push: Use
git diff --staged
to review changes - Amend When Needed: Use
git commit --amend
for small fixes to the previous commit - Reference Issues: Include issue numbers when applicable (e.g., "fixes #123")
- Keep Bullets Focused: One concept per bullet point for clarity
Commit Message Template
This project includes a commit message template at .gitmessage
that is automatically configured for use. When you run git commit
without the -m
flag, your editor will open with this template:
# <type>(<scope>): <subject>
# Types: feat, fix, docs, style, refactor, perf, test, build, chore, revert, security
# Scopes (optional but recommended):
# Apps/Packages: web, docs, ui, config
# Features: import, geocoding, events, catalogs, datasets, auth, cache
# Technical: db, api, jobs, types, testing, deps, docker, scripts, ci
# Subject: imperative mood, max 72 chars (proper nouns allowed: GitHub, API, ES, etc.)
# Body Guidelines:
# - Use title-only for simple, self-explanatory changes
# - Add body with bullet points for complex changes
# - Focus on WHY and business impact, not just WHAT changed
# - Use plain text formatting (no markdown bold/italic)
# List your changes as bullet points (if body needed):
# -
# -
# -
#
# Add as many bullets as needed to describe all changes
# BREAKING CHANGE:
# Describe any breaking changes here
# References: #
# Include issue or PR numbers if applicable
The template is already configured for this repository. To use a similar template globally for all your projects:
git config --global commit.template ~/.gitmessage
Tools and Automation
Commitizen
For interactive commit message creation:
npm install -g commitizen
npm install -g cz-conventional-changelog
echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc
Then use git cz
instead of git commit
.
Commit Linting
The project uses Husky to enforce commit message standards. If your commit doesn't meet the guidelines, it will be rejected with a helpful error message.
Quick Reference
Title-only commits (for simple, self-explanatory changes):
fix(import): prevent duplicate event creation
docs(api): update geocoding endpoint examples
style(ui): format button component
chore(config): update ESLint rules
test(events): add unit tests for event validation
fix(cache): resolve memory leak in location cache
feat(datasets): add bulk export functionality
refactor(types): consolidate shared type definitions
build(ci): optimize container build times
perf(geocoding): implement request batching
security(auth): fix JWT token expiration vulnerability
security(import): add file type validation to prevent malicious uploads
When to use title-only:
- Single-file changes with obvious intent
- Formatting or style fixes
- Simple dependency updates
- Minor documentation corrections
- Clear bug fixes that don't need explanation
Commits with bullet-point bodies:
feat(web): add dark mode toggle
- Added theme context provider for global state
- Implemented toggle component in header
- Persisted user preference to localStorage
- Updated all components to use theme-aware colors
perf(import): optimize batch processing
- Increased default batch size from 100 to 500
- Added streaming parser for large CSV files
- Implemented parallel processing for geocoding
- Reduced memory allocation by reusing buffers
- Improved overall import speed by ~40%
feat(web): add GraphQL API with ES module support
- Implemented GraphQL endpoint at /api/graphql
- Added ES module configuration for better tree-shaking
- Integrated with existing REST API authentication
- Supports both HTTP POST and WebSocket connections
Scope Selection Examples
# App/Package scope - when changes are isolated to one codebase
fix(ui): correct button hover state in dark mode
docs(docs): update getting started guide
style(config): format ESLint configuration
# Feature scope - when changing business logic
feat(events): add recurring event support
fix(geocoding): handle postal codes without city names
perf(import): optimize CSV parsing for large files
# Technical scope - for infrastructure/tooling
build(docker): reduce image size by 40%
test(api): add integration tests for event endpoints
build(ci): automate release notes generation
fix(ci): resolve GitHub Actions Node.js version conflicts
feat(ci): add automated dependency vulnerability scanning
chore(ci): update actions/checkout to v4
# Handling overlaps correctly
docs(docs): update Nextra to version 3.0 # Docs app code change
test(testing): implement snapshot testing utility # Test infrastructure
build(deps): upgrade PostgreSQL client to v16 # Production dependency
chore(deps): update ESLint to latest version # Dev dependency
security(import): add virus scanning for uploads # Security feature
style(config): format TypeScript config files # Code formatting
Common Mistakes to Avoid
1. Listing File Changes
❌ Bad: "Updated GeocodingService.ts, modified config.ts, added tests"
✅ Good: "Add rate limiting to prevent geocoding API quota exhaustion"
2. Technical Details Without Context
❌ Bad: "Add new function processCoordinates() and refactor validation logic"
✅ Good: "Improve coordinate validation to handle edge cases and malformed data"
3. Using Present Tense Instead of Imperative
❌ Bad: "Adding support for Excel imports"
✅ Good: "Add support for Excel imports"
4. Vague or Generic Messages
❌ Bad: "Fix bug" or "Update code" or "Improvements"
✅ Good: "Fix memory leak in CSV parser for files over 100MB"
5. Too Much Technical Detail
❌ Bad: "Refactor AbstractGeocodingProvider to implement IGeocoder interface with async/await pattern"
✅ Good: "Standardize geocoding provider interface for easier testing and maintenance"
6. Missing Business Context
❌ Bad: "Add new provider class and update service configuration"
✅ Good: "Add OpenCage provider to improve geocoding accuracy for European addresses"
Remember: Good commit messages help future maintainers (including yourself) understand why changes were made, not just what changed. Focus on business value and user impact rather than technical implementation details.