{"id":7552,"date":"2025-03-24T09:11:14","date_gmt":"2025-03-24T08:11:14","guid":{"rendered":"https:\/\/blog.bart.sk\/en\/?p=7552"},"modified":"2025-08-06T09:17:59","modified_gmt":"2025-08-06T07:17:59","slug":"how-to-write-commit-messages-that-make-sense-10-proven-tips-from-practice","status":"publish","type":"post","link":"https:\/\/blog.bart.sk\/en\/how-to-write-commit-messages-that-make-sense-10-proven-tips-from-practice\/","title":{"rendered":"How to Write Commit Messages That Make Sense: 10 Proven Tips from Practice"},"content":{"rendered":"\n<p>Every developer has their own nightmare. A bug that only shows up in production, an endless merge conflict, or a commit message like \u201cfix.\u201d You\u2019re digging through the project history, trying to figure out when the API broke, only to find vague notes like \u201cfixed,\u201d \u201cminor stuff,\u201d \u201casdf,\u201d or\u2014worst of all\u2014an entirely empty message.<\/p>\n\n\n\n<p>Years behind the keyboard have convinced me that a clear commit history saves hours of detective work and makes team collaboration a breeze. Here are my battle-tested tips to help you commit like a pro\u2014so you won\u2019t be banging your head against the desk a few months down the line.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1. One Commit, One Task<\/strong><\/h2>\n\n\n\n<p>Mixing unrelated changes into a single commit is a recipe for debugging headaches and messy code reviews. Ideally, each commit should be a standalone, logical unit.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> Got multiple changes in your working branch? Split them up before committing with <code>git add -p<\/code>.<\/p>\n\n\n\n<p>\u270b <strong>Don\u2019t:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"Added new API and fixed footer layout\"<\/code><\/pre>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"feat(api): added endpoint for order creation\" \ngit commit -m \"fix(ui): corrected footer layout\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2. Describe the Problem, Not Just That You \u201cFixed\u201d It<\/strong><\/h2>\n\n\n\n<p>Vague messages like \u201cfix\u201d or \u201cfixed\u201d won\u2019t help you when you\u2019re hunting down a bug. Explain what you solved and why it mattered.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> A brief description of the issue and solution saves time for you and your team.<\/p>\n\n\n\n<p>\u270b <strong>Don\u2019t:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"bug fix\"<\/code><\/pre>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"fix(auth): resolved login freeze on weak connections\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3. Structure Your Commit Messages Clearly<\/strong><\/h2>\n\n\n\n<p>A consistent message format makes history easy to read and changelogs a snap to generate. If your team doesn\u2019t have set rules, try the &lt;type>(&lt;scope>): &lt;description> format.<\/p>\n\n\n\n<p><strong>Examples:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>feat \u2013 new functionality<\/li>\n\n\n\n<li>fix \u2013 bug fix<\/li>\n\n\n\n<li>refactor \u2013 code tweaks without behavior changes<\/li>\n\n\n\n<li>docs \u2013 documentation updates<\/li>\n\n\n\n<li>test \u2013 adding or tweaking tests<\/li>\n\n\n\n<li>chore \u2013 small tasks (e.g., package updates)<\/li>\n\n\n\n<li>style \u2013 code formatting (e.g., indentation)<\/li>\n\n\n\n<li>perf \u2013 performance boosts<\/li>\n\n\n\n<li>ci \u2013 CI\/CD pipeline changes<\/li>\n\n\n\n<li>build \u2013 build process updates<\/li>\n<\/ul>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> Keep the first line under 72 characters and add details in the description.<\/p>\n\n\n\n<p>\u270b <strong>Don\u2019t:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"update\"<\/code><\/pre>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"feat(cart): added option to save cart for later\"<\/code><\/pre>\n\n\n\n<p><em>Description if needed: \u201cAdded cart-saving functionality on logout.\u201d<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4. Break Big Changes into Bite-Sized Pieces<\/strong><\/h2>\n\n\n\n<p>A massive commit with hundreds of lines and multiple changes is a nightmare for code reviews and rollbacks. Split it into smaller, digestible chunks.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> For a UI refactor, start with buttons, then inputs, and finally remove old code\u2014committing each step separately.<\/p>\n\n\n\n<p>\u270b <strong>Don\u2019t:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"big UI overhaul\"<\/code><\/pre>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"feat(ui): added new button styles\" \ngit commit -m \"refactor(button): removed old button component\" \ngit commit -m \"feat(ui): updated input styles\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5. Protect Sensitive Data<\/strong><\/h2>\n\n\n\n<p>API keys, passwords, database connections, and other sensitive info don\u2019t belong in your project history. Once they\u2019re in the repo, they\u2019re there forever (unless you rewrite history), and anyone with access can exploit them. Always double-check what you\u2019re pushing.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> Use <code>.gitignore<\/code> to automatically exclude files, like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>.env \u2013 environment variables (API keys, passwords)<\/li>\n\n\n\n<li>*.log \u2013 logs with potential data<\/li>\n\n\n\n<li>config\/secrets.json \u2013 configs with keys<\/li>\n\n\n\n<li>*.key, *.pem \u2013 private keys<\/li>\n\n\n\n<li>node_modules \u2013 local dependencies<\/li>\n<\/ul>\n\n\n\n<p>Avoid hardcoding values in code too (e.g., <code>const apiKey = \"xyz123\"<\/code>).<\/p>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \".env\" >> .gitignore \necho \"*.log\" >> .gitignore \necho \"config\/secrets.json\" >> .gitignore \necho \"*.key\" >> .gitignore<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6. Test Before You Commit<\/strong><\/h2>\n\n\n\n<p>Make sure your app works before committing. A broken commit can derail the project and frustrate your team. Run tests or at least manually check key features.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> If you have automated tests, always run them pre-commit.<\/p>\n\n\n\n<p>\u270b <strong>Don\u2019t:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"fix: cart bug fix\" # without testing<\/code><\/pre>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm test git commit -m \"fix(cart): fixed price recalculation with discounts\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7. Skip the Clutter<\/strong><\/h2>\n\n\n\n<p>Don\u2019t clog the repo with stuff that doesn\u2019t belong\u2014debug logs, commented-out \u201cjust in case\u201d code, or random junk. It only muddies the history.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> Check <code>git diff<\/code> before committing to ensure you\u2019re only adding relevant changes. Add unnecessary files to <code>.gitignore<\/code>.<\/p>\n\n\n\n<p>\u270b <strong>Don\u2019t:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git commit -m \"added debug logs\"<\/code><\/pre>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"debug.log\" &gt;&gt; .gitignore git commit -m \"feat(cart): added item validation\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>8. Work in a Branch, Not Directly in Main<\/strong><\/h2>\n\n\n\n<p>Committing straight to main is a gamble\u2014you risk breaking production or creating unnecessary conflicts for your team.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> Create a working branch and merge via pull request.<\/p>\n\n\n\n<p>\u2705 <strong>Do:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git checkout -b feature\/add-payment-method \ngit commit -m \"feat(payment): added Apple Pay support\" \ngit push origin feature\/add-payment-method<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>9. Use AI Wisely<\/strong><\/h2>\n\n\n\n<p>Tools like GitHub Copilot can suggest commit messages, but always review and tweak them.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> AI is your assistant, not your boss\u2014tailor its suggestions to match your changes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>10. Sync Up with Your Team<\/strong><\/h2>\n\n\n\n<p>Unified commit rules streamline collaboration and keep your change history consistent.<\/p>\n\n\n\n<p>\ud83d\udca1 <strong>Tip:<\/strong> Try setting a team template for commit messages, like [type] &lt;short description> (#task_number)\u2014e.g., [feat] Added product filter (#123). It helps you write consistent messages fast and keeps track of which task each change belongs to.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h1>\n\n\n\n<p>Commits aren\u2019t just a log of changes\u2014they\u2019re a message to the future, for your team and yourself. A clear history simplifies debugging, code reviews, and collaboration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ: How to Write Better Commits<\/h2>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>Why is a good commit history important?<\/summary>\n  <p>A clean commit log saves time when debugging, improves code reviews, and makes teamwork easier. Without it, you&#8217;ll be lost in a sea of &#8220;fixed&#8221; and &#8220;update&#8221; messages.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>What does &#8220;one commit, one task&#8221; mean?<\/summary>\n  <p>Each commit should represent a single logical change. Mixing unrelated changes makes debugging and analysis much harder.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>How should I structure my commit message?<\/summary>\n  <p>Use the format <code>&lt;type&gt;(&lt;scope&gt;): &lt;short description&gt;<\/code>. For example: <code>fix(cart): corrected price calculation<\/code>.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>Can I commit directly to main?<\/summary>\n  <p>No. Always create a working branch and use pull requests. This reduces the risk of errors and conflicts.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>Why should I avoid committing API keys or passwords?<\/summary>\n  <p>Sensitive data stays in the repository history forever. Use <code>.gitignore<\/code> and keep secrets in separate config files.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>Do I need to test before committing?<\/summary>\n  <p>Yes. Every commit should contain tested changes. Run automated tests or manually verify critical functions before committing.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>Can I use GitHub Copilot for commit messages?<\/summary>\n  <p>Yes, but always review and edit the suggestions to make sure they accurately describe your changes.<\/p>\n<\/details>\n\n\n\n<details class=\"is-layout-flow wp-block-details-is-layout-flow\">\n  <summary>Should our team define commit message rules?<\/summary>\n  <p>Definitely. Shared guidelines improve consistency and make the history easier to read and work with.<\/p>\n<\/details>\n\n","protected":false},"excerpt":{"rendered":"Every developer has their own nightmare. A bug that only shows up in production, an endless merge conflict,&hellip;","protected":false},"author":50,"featured_media":7553,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","csco_display_header_overlay":false,"csco_singular_sidebar":"","csco_page_header_type":""},"categories":[199],"tags":[708,700,229,705,704,706,321,696,274,707],"_links":{"self":[{"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/posts\/7552"}],"collection":[{"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/users\/50"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/comments?post=7552"}],"version-history":[{"count":2,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/posts\/7552\/revisions"}],"predecessor-version":[{"id":7634,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/posts\/7552\/revisions\/7634"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/media\/7553"}],"wp:attachment":[{"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/media?parent=7552"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/categories?post=7552"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bart.sk\/en\/wp-json\/wp\/v2\/tags?post=7552"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}