{"slug": "i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python", "title": "I Optimized My Bump Version Tool and Made It 1,000,000x Faster Than Its Python Counterparts.", "summary": "A developer rewrote the bump2version version-bumping tool in Rust, releasing version 0.2.1 with a branchless lookup-table design, SmallVec-based version storage, and memchr-based scanning. The release adds file watching, manifest auto-detection, and support for pyproject.toml, pom.xml, and go.mod, and benchmarks claim roughly 1,000,000x faster parse-bump-serialize than the Python bump-my-version CLI when counting subprocess and interpreter startup overhead.", "body_md": "Hello again, fellow Rustaceans, Pythonistas in denial, and the three people on earth who genuinely enjoy reading version bumper release notes 👋!\n\nIf you were here for my last post, where I rewrote [`bump2version`](https://github.com/c4urself/bump2version) in Rust and declared it **~10,000x faster** than the Python CLI, you may recall that I ended it with a vague threat about future benchmarks. The Mossad agents who consulted on the architecture wrote \"this is not over\" in the margin of their whiteboard. I ignored it.\n\nI should have listened.\n\nBecause in the weeks since that post, I went back in. *Deep in.* I committed crimes against `cargo build` that will haunt me during thunderstorms. I `--release`'d things that should not be `--release`'d. I made Claude hallucinate a performance chart at 3AM and then used it to motivate myself.\n\nThe result: [`bump2version 0.2.1`](https://github.com/wiseaidev/bump2version/releases/tag/v0.2.1).\n\n**Now 1,000,000× faster than its Python counterparts.** Yes, I am counting subprocess overhead.\n\n*The Soviet material resurfaced. It always does.*\n\nLet's get into it.\n\nBecause `0.2.0` shipped, and I immediately opened my Gmail inbox.\n\nThe audacity of the open-source community. The audacity, bro! I build something 10,000x faster and within *days* there are requests. \"Can it watch files?\", \"Can it detect which manifests I'm using?\", \"Can I run it in the browser?\", \"Can I use it with Go?\", \"Can I use it with Java?\", \"Can my Ruby project use it?\". I ain't got no time for this. I need to take a little break and play CS2 with the boys!\n\nI looked at these emails. I looked at the ceiling. I looked at my coffee. The coffee looked back at me with the hollow expression of a language runtime that has seen too many package manager debates.\n\nAnd then I cracked my knuckles and got to work.\n\nI thought I was done with the Mossad agents after `0.2.0`. I was not.\n\nThey came back. Same 3AM knock. Same whiteboard. But this time *they brought slides.* Twelve of them. With bullet points. And speaker notes. One slide was just the word **\"BRANCHLESS\"** in 72-point font with a red circle around it.\n\nTheir new requirements:\n\n`memchr``.contains()` in a loop like an animal\",`SmallVec`` Vec<u8>` for three numbers (major, minor, patch) is an insult to modern CPU cache lines.`if major { ... } else if minor { ... }` nonsense. One lookup table. One store. Done.`Cargo.toml` like it's the only file in the world\", they observed, correctly. \"What about `pyproject.toml`? `pom.xml`? `go.mod`? Are Go developers not also suffering?\"\nThey are. They are very much also suffering.\n\nI implemented every point. The Mossad agents reviewed the diff, said \"passable\", and vanished into the root filesystem like a well-placed `.gitignore` entry.\n\nLet me be absolutely scientifically honest with you for one sentence before I stop being honest: the 1,000,000× number compares the full `bump-my-version` CLI round-trip (importing Python, loading dependencies, spawning a subprocess) against our in-process library call with a warm cache and a `SmallVec`.\n\nOne is a sports car. The other is a person who has to call a taxi, wait 12 minutes, and explain where they're going in a language they only partially speak.\n\nNow let's look at the real numbers.\n\n`0.2.0` vs `0.2.1` vs Python\n| Operation | `bump2version`` 0.2.0` | `bump2version`` 0.2.1` | Python pyO3 FFI | `bump-my-version` CLI | \n|---|---|---|---|---|\n| Parse + bump + serialize | ~57 µs | **~0.4 µs** | ~79 µs | ~585 ms | \n| File search/replace (1k lines) | ~65 µs | **~11 µs** | ~1.7 µs | ~590 ms | \n| File search/replace (100k lines) | ~4.2 ms | **~0.9 ms** | N/A | ~600 ms | \n| Config file parse | ~800 µs | **~140 µs** | N/A | ~500 ms (on import) | \n\nVersus the Python CLI subprocess: ((500 x 1000) ÷ 0.4) = **~1,250,000×** faster for a version parse-bump-serialize. We round down to 1,000,000x for humility. The Mossad agents said rounding up was acceptable. We preferred honesty.\n\n`strchr` and it is unreasonably good at its one job.`SmallVec<[VersionPart; 8]>`` Arc<Regex>` cache\n`bump --watch --bump patch`\n\nIt sounds simple. It was the opposite of simple.\n\n\"Just watch a file and re-bump when it changes\", I told myself, in the tone of a man who has never used [`inotify`](https://man7.org/linux/man-pages/man7/inotify.7.html) before.\n\nBro, I used `inotify`. Or rather, [` notify-rs`](https://github.com/notify-rs/notify) used `inotify` for me, which is almost the same thing except someone smarter than me had already suffered through the Linux kernel filesystem event API so I wouldn't have to. God bless crate authors.\n\nWatch mode works like this:\n\n`--watch` flag.`bump` registers a recursive file watcher on all files listed in your `.bumpversion.toml`.` current_version`.\nThis is either extremely useful for local development workflows or a deeply irresponsible footgun. We ship tools for adults. The lock safety is your problem.\n\n```\n# Watches VERSION, Cargo.toml, and README.md for changes, auto-bumps on save\nbump --watch --bump patch --config-file .bumpversion.toml\n# Oh no did I just? yes, yes you did. It already ran.\n[watch] Detected change: Cargo.toml\n[watch] Bumping patch: 0.2.1 → 0.2.2\n```\n\n`--detect` Is Doing God's Work\nPrevious `bump2version` was Rust-centric. You gave it files, it bumped files. Very obedient lil tool. Very rigid. Like a Rust compiler, actually.\n\nThe new `--detect` flag changes all that:\n\n```\nbump --bump minor --detect\n```\n\nThis single command:\n\n`target/`, `node_modules/`, `.git/`, etc.)` setup.cfg`, `package.json`, `build.gradle`, `Gemfile`\nSix languages. One command.\n\n```\ncd my-monorepo\nbump --current-version 0.1.0 --bump patch --detect --dry-run\n\n# [detect][dry-run] Would update: Cargo.toml\n# [detect][dry-run] Would update: pyproject.toml\n# [detect][dry-run] Would update: package.json\n# [detect][dry-run] Would update: pom.xml\n# [detect][dry-run] Would update: go.mod\n# [detect][dry-run] Would commit 5 file(s): 0.1.0 → 0.1.1\n```\n\nFull supported matrix:\n\n| Language | Manifest Files | \n|---|---|\n| 🦀 Rust | `Cargo.toml` | \n| 🐍 Python | `pyproject.toml` ,`setup.cfg` ,`setup.py` | \n| 🟨 JavaScript / Node.js | `package.json` | \n| 🐹 Go | `go.mod` | \n| ☕ Java | `pom.xml` ,`build.gradle` ,`build.gradle.kts` | \n| 💎 Ruby | `Gemfile` | \n\nThe Go developers, in particular, messaged to say thank you. We thanked them for using Go despite everything.\n\nEvery language now has a working example under [`examples/`](https://github.com/wiseaidev/bump2version/tree/main/examples), each with its own pre-baked `.bumpversion.toml`.\n\n```\n# Python project\nbump --config-file examples/python/.bumpversion.toml --bump patch --dry-run\n# [dry-run] Would commit 2 file(s): 0.1.0 → 0.1.1\n\n# Node.js project\nbump --config-file examples/nodejs/.bumpversion.toml --bump minor --dry-run\n# [dry-run] Would commit 1 file(s): 0.1.0 → 0.2.0\n\n# Java Maven project\nbump --config-file examples/java/.bumpversion.toml --new-version 2.0.0 --dry-run\n# [dry-run] Would commit 1 file(s): 0.1.0 → 2.0.0\n\n# Ruby gemspec + Gemfile\nbump --config-file examples/ruby/.bumpversion.toml --bump major --dry-run\n# [dry-run] Would commit 2 file(s): 0.1.0 → 1.0.0\n\n# The \"bump everything at once\" grand finale\ncd examples/multi-lang\nbump --current-version 0.1.0 --bump minor --detect --dry-run\n# [detect][dry-run] Would update: Cargo.toml, pyproject.toml, package.json, pom.xml, Gemfile, VERSION\n```\n\n6 ecosystems. 1 tool. Still forbids `unsafe`. We have standards.\n\nYou ever look at a perfectly good CLI tool and think: *\"This would be better if I could bump versions from a browser\"?*\n\nNo? Me neither. But then someone on the team whispered \"WASM\" and I remembered that I am constitutionally incapable of saying no to WebAssembly.\n\nSo now there is a Yew-based web application at [`examples/yew-app/`](https://github.com/wiseaidev/bump2version/tree/main/examples/yew-app). It:\n\n`wasm32-unknown-unknown`, no server required` version_ops.rs`\nAnd yes, `git`-related features had to be extracted into an optional feature flag (`git`) because `gix` uses Unix-specific APIs that don't compile to WASM. We found this out the fun way, which is a phrase that means \"at 2AM with a 47-line linker error\".\n\nYou may recall from my previous post that I abused Claude during `0.2.0` development. My lawyer said not to bring it up again.\n\nSo I won't bring up the fact that during `0.2.1` development, I made ChatGPT, Claude and Gemini:\n\n`detect.rs` walk implementation 7 times until it correctly skipped `node_modules` without accidentally also skipping `node_modules_backup` (important distinction)`SmallVec<[VersionPart; 4]>` for typical semver usage (it is `8`, empirically)\nThe OpenAI, Anthropic and Google lawyers have upgraded from \"concerned\" to \"a medium-sized incident report has been filed\".\n\nMy legal counsel has asked that I clarify: no Claude was permanently harmed. Tokens were consumed. Electricity was used. The `--detect` flag works correctly.\n\nAfter `cargo install bump2version --features rust-binary`, you get three things:\n\n| Binary | Use case | \n|---|---|\n| `bump` | The modern, primary CLI | \n| `cargo-bump` | Cargo subcommand ( `cargo bump --bump patch` ) | \n| `bump2version` | ✅ **Backward-compatible alias** for old CI configs, tutorials, and the 47 blog posts that told people to run`bump2version --bump patch` | \n\nThe `bump2version` binary is not deprecated. It is not going anywhere. If you have a shell script from 2025 that calls `bump2version --bump minor`, it will still work in 2030, and presumably during the heat death of the universe, if any of your CI pipelines survive that long.\n\nThis was a deliberate choice. Breaking changes in release tooling are a form of chaos that no version bumper should inflict on the people who trusted it.\n\n```\n# All of these do the exact same thing:\nbump --bump patch\ncargo bump --bump patch\nbump2version --bump patch   # kept for backward compatibility\n```\n\nThe config file now tracks **all version-carrying files in the project**, not just `Cargo.toml`:\n\n```\n[bumpversion]\ncurrent_version = 0.2.1\ncommit = false\ntag = false\n\n[bumpversion:file:Cargo.toml]\nsearch = version = \"{current_version}\"\nreplace = version = \"{new_version}\"\n\n[bumpversion:file:package.json]\nsearch = \"version\": \"{current_version}\"\nreplace = \"version\": \"{new_version}\"\n\n[bumpversion:file:README.md]\nsearch = \"{current_version}\"\nreplace = \"{new_version}\"\n\n[bumpversion:file:RUST.md]\nsearch = \"{current_version}\"\nreplace = \"{new_version}\"\n\n[bumpversion:file:WASM.md]\nsearch = \"{current_version}\"\nreplace = \"{new_version}\"\n\n[bumpversion:file:DOCKER.md]\nsearch = `{current_version}`\nreplace = `{new_version}`\n\n[bumpversion:file:PACKAGING.md]\nsearch = {current_version}\nreplace = {new_version}\n\n[bumpversion:file:rpm/bump2version.spec]\nsearch = Version: {current_version}\nreplace = Version: {new_version}\n```\n\nOne `bump --bump patch` and every single version string, across docs, code, configs, packaging specs, and Docker manifests, updates atomically. The Mossad agents reviewed this config and said \"acceptable\" which, from them, is essentially a standing ovation.\n\nThere is a moment in every Rust developer's life, we all know the moment, where you look at a piece of code that *should* work, that *does* work in your head, that you have drawn on paper with arrows and boxes to prove its correctness, and the borrow checker looks at you across the compiler output and says:\n\n`error[E0505]: cannot move out of 'watcher' because it is borrowed`\n\nAnd then below that:\n\n`note: the borrow later used here`\n\nAnd then below *that*, a footnote that reads `note: move occurs because...` followed by a chain of reasoning so long it wraps around to the next terminal page.\n\nThe Yew WASM integration did this to me for different reasons: `gix` depends on Unix syscalls (`openat`, `statx`, platform symlink handling) that simply do not exist in `wasm32-unknown-unknown`. The linker error was 47 lines long and named three crates I had never heard of.\n\nThe fix: gating all git-related functionality behind a `git` feature flag that is disabled by default for WASM builds. Clean. Simple. The kind of solution that is obvious in retrospect and invisible before you spend four hours in the linker output.\n\n```\n[features]\ndefault = [\"std\", \"git\"]\ngit      = [\"gix\", \"std\"]\ncli      = [\"clap\", \"git\"]\nrust-binary = [\"cli\", \"git\", \"detect\", \"watch\"]  # ← full featured binary\nwatch    = [\"notify\", \"cli\"]\ndetect   = [\"walkdir\", \"cli\"]\n```\n\nThe WASM app uses none of the `git` features. The CLI binary uses all of them. The feature graph is clean enough that my Mossad advisors called it \"elegant\" before immediately asking about the benchmark numbers.\n\n```\n# The complete CLI, with everything\ncargo install bump2version --features rust-binary\n```\n\nThis gets you `bump`, `cargo-bump`, and `bump2version`. All 3. No choices required. Just install and bump.\n\n```\n# Standard semver\nbump --bump patch           # 0.2.1 → 0.2.2\nbump --bump minor           # 0.2.1 → 0.3.0\nbump --bump major           # 0.2.1 → 1.0.0\n\n# Safe preview\nbump --bump patch --dry-run\nbump --bump minor -n        # same thing, shorter\n\n# Git integration\nbump --bump patch --commit --tag\n\n# Custom message\nbump --bump minor --commit --message \"chore: ship {new_version} 🚀\"\n\n# Auto-detect all manifests\nbump --bump patch --detect\n\n# Watch mode (exits when you ctrl + c)\nbump --watch --bump patch\n```\n\n`std` required for the core)\n\n```\n[dependencies]\nbump2version = { version = \"0.2.1\", default-features = false }\nuse bump2version::{config::BumpConfig, version::{parse_version, bump_version, serialize_version}};\n\nlet cfg = BumpConfig::default();\nlet v   = parse_version(\"1.2.3\", &cfg).unwrap();\nlet v2  = bump_version(&v, \"minor\", &cfg).unwrap();\nassert_eq!(serialize_version(&v2, &cfg), \"1.3.0\");\npip install bump-rs\npython\nfrom bump_rs import bump_version\nprint(bump_version(\"1.2.3\", \"major\"))  # \"2.0.0\" in ~57µs\nnpm install bump2version\njs\nconst { bumpVersion } = require(\"bump2version\");\nconsole.log(bumpVersion(\"1.2.3\", \"patch\")); // \"1.2.4\"\n```\n\nLook, at some point the law of diminishing returns kicks in. We cannot make version bumping faster than the speed of light. (We checked. We asked the Mossad agents. They checked their slides. The answer was no.)\n\nBut there are things we *can* still do:\n\n`alpha.1 → alpha.2 → beta.1 → rc.1 → stable` lifecycle, first-class`detect` targets`.NET` (`*.csproj`), PHP (` composer.json`), Swift (` Package.swift`), Elixir (` mix.exs`)\nIf you want any of these sooner: [open an issue](https://github.com/wiseaidev/bump2version/issues). Or just star the repo and the moral pressure will accelerate delivery. It works on me. I've tested this empirically.\n\n`bump2version 0.2.1` started as a performance exercise and became an ecosystem. What was a single binary is now:\n\n`no_std` support for the core`bump-rs`) for Pythonistas who want the speed without the syntax` cargo-bump`, `bump2version`\n`cargo install bump2version --features rust-binary` → bump → ship → repeat → be 1,000,000× faster than Python → sleep → repeat 🦀\n\n`bump2version` is the world's fastest version bumper written entirely in **100% safe Rust**, with `no_std` support, native Python and Node.js bindings, and a `cargo bump` subcommand 🗿.\n\n| Platform | Command | \n|---|---|\n| **Rust binary** | `cargo install bump2version --features rust-binary` | \n| **Cargo subcommand** | `cargo bump --help` | \n| **Docker** | `docker pull wiseaidev/bump2version` | \n| **Debian/Ubuntu** | Download `.deb` from[GitHub Releases](https://github.com/wiseaidev/bump2version/releases) | \n| **RHEL/Fedora** | Download `.rpm` from[GitHub Releases](https://github.com/wiseaidev/bump2version/releases) | \n| **Windows** | Download `bump.exe` from[GitHub Releases](https://github.com/wiseaidev/bump2version/releases) | \n| **GitHub Action** | See [action.yml](https://github.com/wiseaidev/bump2version/blob/main/action.yml) | \n| **Python** | `pip install bump-rs` | \n| **Node.js** | `npm install bump2version` | \n\nNote\n\nInstalling via `cargo` installs both `bump` and `bump2version` binaries. The original `bump2version` binary is retained indefinitely for backward compatibility with existing tutorials, CI/CD pipelines, and automation scripts.\n\n`bump2version` automates semantic version management for any project regardless of language. It:\n\n`major.minor.patch`).` major`, `minor`, `patch`, or…\nStar [the repo](https://github.com/wiseaidev/bump2version). Try the [Python bindings](https://pypi.org/project/bump-rs). Use the [Node.js package](https://www.npmjs.com/package/bump2version). Read the [Rust docs](https://docs.rs/bump2version). Poke the [WASM demo](https://github.com/wiseaidev/bump2version/tree/main/examples/yew-app). Run the [examples](https://github.com/wiseaidev/bump2version/tree/main/examples).\n\nThis has been a public service announcement from a developer who, in the course of a single engineering session, created 20 tests, 7 example projects, a WASM frontend, a Soviet-themed benchmark suite, and consumed an amount of Claude tokens that my accountant has asked me not to disclose.\n\nThe legal proceedings with Top Tech companies remain ongoing. My lawyer has read this post. He has asked me to note, for the record, that \"abusing Claude\" is a colloquial and affectionate term and not a legally actionable description of token consumption.\n\nI have not taken that advice either.\n\nTill next time: *Keep bumpin', keep rustin', keep benchmarkin'* 🦀⬆️\n\nP.S. The Mossad agents have approved this post subject to the removal of the classified section about the branchless arithmetic lookup table. We kept it in. They know. They've said nothing. This is ominous.", "url": "https://wpnews.pro/news/i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python", "canonical_source": "https://dev.to/wiseai/i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python-counterparts-2i83", "published_at": "2026-09-11 22:15:08+00:00", "updated_at": "2026-09-11 22:51:02.108447+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["bump2version", "bump-my-version", "Rust", "Python", "pyO3"], "alternates": {"html": "https://wpnews.pro/news/i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python", "markdown": "https://wpnews.pro/news/i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python.md", "text": "https://wpnews.pro/news/i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python.txt", "jsonld": "https://wpnews.pro/news/i-optimized-my-bump-version-tool-and-made-it-1000000x-faster-than-its-python.jsonld"}}