Skip to main content

1. Commit Structure

Emoji format: Emoji and type are separated by a space — ✨ feat(auth): ... not ✨ feat(auth): .... The space is required for the commitlint parser to correctly extract the type.

Quick Decision Tree


2. Types

Note on emoji prefixes: Emojis make types visually scannable in git log and GitHub UI. The space between emoji and type is required — commitlint’s parser uses it to split the emoji from the type text for validation.

3. Scope

Scope = the module, layer, or domain that changed.
Rule: If you removed the scope and someone read the commit, would they know where to look in the codebase? If no → add scope.
Rule: Type answers what kind of change, scope answers where. They should never overlap. This is why perf is excluded from Frontend scopes — the ⚡️perf type already carries that intent. Use a specific scope instead: ⚡️ perf(router), ⚡️ perf(bundle), ⚡️ perf(render)

Backend / API Scopes

Frontend / Web App Scopes

CLI Tool / Library Scopes


4. Description vs Body

Imperative Tense Rule

Write the description as a command: “If applied, this commit will… [description]“

When to Add a Body


① Issue / Ticket References

See Git Collaboration Policy for when to use Fixes vs Refs.

② Breaking Changes

BREAKING CHANGE: is a special keyword — tools like semantic-release use it to bump the major version automatically.

③ Co-authors


6. Full Examples by Project Type

Backend / API

Frontend / Web App

CLI Tool / Library


7. Tooling — commitlint + Husky

Install

commitlint.config.ts

This config auto-generates the emoji regex from your type definitions — no manual maintenance needed. Based on the official commitlint emoji example and the approach used by LobeHub.

Husky (local enforcement)

GitHub Actions (CI enforcement for all contributors)

CI enforcement ensures every contributor’s commits are validated, not just yours.

Summary