Skip to main content

100% vibed

I'll Be Backspace

The comment linter that says what everybody's thinking.


Friend, Can We Talk About Your Comments?

Let me paint you a picture. You open a file. You're a busy person. And there, sitting on top of a two-line function like a wedding cake on a bicycle, is this:

# Sync pulls files; it does NOT recreate containers. That's enough for
# config a service reads live (Traefik file-watches dynamic/, Dashy
# re-reads conf.yml per request), but a compose change needs an `up` to
# take effect. Verified 2026-07-29: fixing postgres's data mount and
# force-syncing left the same container id running the old mount, so the
# crash loop continued. Hence the two-step below.
sync(service)
compose_up(service)

Six lines of comment. Two lines of code. Somewhere in there is one useful fact, and it is doing hard time.

Now — what if I told you there was a better way?

$ backspace deploy.py
deploy.py:2:5: error: comment-code-ratio: 6 comment lines describe 2 lines of code (ratio 3.0, max 1.5)
    2 | Sync pulls files; it does NOT recreate containers. That's enough for
    3 | config a service reads live (Traefik file-watches dynamic/, Dashy
      | ... 3 more lines
    7 | crash loop continued. Hence the two-step below.
      = help: A comment longer than the code it describes usually restates the
             code. Say what the code cannot.

1 file checked, 1 violation

That's it. That's the product. And folks, it is fast.

What's In The Box

  • Nineteen languages out of the box. Python, Rust, Go, TypeScript, JavaScript, Bash, C/C++, Java, Kotlin, Swift, Ruby, PHP, Lua, SQL, YAML, TOML, HCL, Dockerfile, Makefile. Adding another is one TOML file.
  • It knows a string from a comment. s = "# not a comment" does not fool it. Neither do raw strings, template literals, nested block comments, or JavaScript regex literals. This is the part everyone else gets wrong.
  • Diff-aware. Point it at a mature repo and it won't hand you four hundred findings on code you didn't write. It checks what you touched.
  • Docstrings are safe. Your API documentation is supposed to be long. We leave it alone unless you ask.
  • A --json mode built for robots. Ships the offending comment text right in the payload, so your agent can fix it without opening the file again.
  • Configurable six ways from Sunday, and — here's the kicker — a config show command that tells you which layer set every single value. No guessing. Never guessing.

Get It Installed. Right Now. Today.

Pre-commit — and this is where it really sings:

repos:
  - repo: https://github.com/phin-tech/ill-be-backspace
    rev: v0.1.3
    hooks:
      - id: backspace

Or any of these, whichever suits your operation:

$ uvx --from ill-be-backspace backspace   # try before you buy
$ pipx install ill-be-backspace           # a wheel, no Rust required
$ brew install phin-tech/tap/backspace

The package is ill-be-backspace; the command it installs is backspace.

Kick The Tires

$ backspace                       # current directory, changed lines only
$ backspace src/ --all            # the whole lot
$ backspace --diff=main           # everything your branch changed
$ backspace src/ --json           # for machines
$ backspace --stats               # who's the worst offender?

Exit codes are 0 clean, 1 violations, 2 you've configured something strange. Your CI will know exactly what to do.

Four Rules. That's All It Takes.

Rule What it catches
block-too-long More than max_lines (5) consecutive lines of comment.
comment-code-ratio A comment longer than the code beneath it. This is the one that finds the real offenders.
comment-restates-code A comment that only says what the code already says. Opt-in — see below.
banned-phrase Words and regexes you pick — see below. There's an llm-tells preset for Verified 2026-…, Note that, it does NOT, and friends. Opt-in — we're not here to preach.

backspace explain <rule> if you want the long version.

Make It Yours

It reads config from wherever you already keep it — .backspace.toml, pyproject.toml, package.json, or Cargo.toml. No new file unless you want one.

max_lines = 5
exclude = ["**/vendor/**"]

[rules.banned-phrase]
preset = "llm-tells"

[languages.go]
max_lines = 8          # licence headers, we understand

[[overrides]]
paths = ["tests/**"]
max_lines = 15         # tests get a little room to breathe

The One-Line Essay

A block budget can't catch this, because it's one line:

# Set the user's name to the provided value if it is not None, otherwise keep the existing one.
user.name = name or user.name

max_line_words measures each line on its own:

max_line_words = 12

Twelve is not a number I made up. Across 907 real single-line comments, the median is 7 words and the 95th percentile is 12 — so this flags the top 3% and leaves everything else alone. Terse comments like # Cached: the upstream rate-limits at 10rps. sail straight through.

Just Show Me What I Wrote

Sometimes you don't want a verdict, you want a look. --audit lists every comment and always exits 0:

$ backspace --diff --audit
src/api.py:41 (line, 1 line, 18 words)
   41 | Set the user's name to the provided value if it is not None, otherwise…

src/api.py:58 (line, 2 lines, 9 words)
   58 | Retry once: the upstream 502s on cold start.
   59 | Anything longer and the client has already timed out.

1 file checked, 2 comments

Pair it with --json and hand it to your agent: "here is every comment you just added, go read them again." That's a review aid, not a gate — which is why it never fails a build.

The Comment That Says Nothing

Here's the one that goes right for the throat:

# increment the retry counter
retry_counter += 1

That comment has no job. comment-restates-code catches it by splitting the identifiers below into words — retry_counter becomes retry + counter — and measuring how much of the comment's vocabulary was already sitting there in the code.

Turn it on:

select = ["block-too-long", "comment-code-ratio", "comment-restates-code"]

[rules.comment-restates-code]
threshold = 0.8      # overlap at or above this counts as restating
min_words = 6        # shorter comments have too little to measure

It skips section banners, code samples and data shapes, because those name the code on purpose.

Why it's opt-in. A good "why" comment has to name the things it's talking about, so it picks up overlap honestly. Tuned against ~7,400 real files it fires about fifteen times, and a couple of those are arguable. It's a sharp tool with a real false-positive rate, not a safe default — which is why you have to ask for it.

Ban Your Own Words

Got a word you never want to see again? Put it in ~/.config/ill-be-backspace.toml and it follows you into every repo you touch:

[rules.banned-phrase]
words = ["substrate", "delve into", "leverage", "seamless", "robust"]

words are literal, not regexes — they're escaped for you, matched whole-word and case-insensitively. So substrate won't fire on substrates, and c++ is a word rather than a syntax error. If you want a regex, use extend = ['Verified \d{4}-\d{2}-\d{2}'].

Your Words Are Safe

Here's the important part. A project adds to your list, it does not replace it:

# some repo's .backspace.toml
[rules.banned-phrase]
words = ["synergy"]

You still get substrate, delve into, leverage, seamless, robust — plus synergy. The only thing that wipes your list is a project explicitly saying patterns = [...], which is the documented "start from scratch" switch.

key takes what it does
words plain text escaped, whole-word, case-insensitive — start here
extend regex appended to whatever's accumulated
patterns regex replaces the accumulated list
preset "llm-tells" the built-in bundle

Who Set What, And Where

Values stack up in this order, each one beating the last:

defaults → ~/.config/ill-be-backspace.toml → project config
         → [languages.*] → [[overrides]] → CLI flags → inline directives

Every layer only changes the keys it names. Set max_lines in a project and your personal require_suppression_reason still applies.

And when you can't remember why a value is what it is, just ask:

$ backspace config show src/api/handlers.py
src/api/handlers.py  (python)
  user config:    /Users/you/.config/ill-be-backspace.toml
  project config: .backspace.toml

  max_lines                  = 15            overrides[0]
  max_ratio                  = 1.50          default
  banned_phrases             = 6 pattern(s)  user config
  severity                   = error         command line

No guessing. Never guessing.

Full schema in skill/reference.md.

Sometimes The Long Comment Is Right

Wire formats. Protocol quirks. We're reasonable people:

# backspace: ignore[block-too-long] — frame layout is fixed by RFC 9114 §4.2

backspace: ignore for everything, backspace: ignore-file near the top of a file for the whole thing. Set require_suppression_reason = true and it'll insist you explain yourself — which, frankly, you should.

For The Robots

There's a Claude Code skill in skill/. Drop it in and your agent writes shorter comments in the first place, which beats catching them at commit time every day of the week:

$ cp -r skill ~/.claude/skills/backspace

Under The Hood

A clanker-rolled character state machine, one pass, no parser generators, no grammar downloads. Language definitions are plain TOML embedded at compile time — and you can add your own at runtime, same schema, no recompile.

Two things it's honest about: JavaScript regex-literal detection and Python docstring position are heuristics, not a parser. They're tested against the cases that matter and documented where they aren't perfect.

256 tests. It passes its own lint. We wouldn't dare ship it otherwise.

Contributing

New language? One file in languages/, one line in src/lang/mod.rs, a good_ and a bad_ fixture in tests/fixtures/. That's the whole ceremony.

$ cargo test
$ cargo run -- --all .

License

MIT. Go build something.

Metadata

Release files for ill-be-backspace 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ill-be-backspace 0.1.3
File Size Uploaded
ill_be_backspace-0.1.3.tar.gz 2.6 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for ill-be-backspace 0.1.3
File
ill_be_backspace-0.1.3-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
ill_be_backspace-0.1.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
ill_be_backspace-0.1.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
ill_be_backspace-0.1.3-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
ill_be_backspace-0.1.3-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 11.5 MB

Release files / ill_be_backspace-0.1.3.tar.gz

Download URL ill_be_backspace-0.1.3.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
51a20773a6e6d07fe71682949ea29592d0df3bb4940b600b25db53ab9a6dfb2e
BLAKE2b-256 checksum
How to use checksums
f656eb342c1de8a8d671fc7cbf130adca6cfb457ec3cc95c5d289da89e9f2a9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release files / ill_be_backspace-0.1.3-py3-none-win_amd64.whl

Download URL ill_be_backspace-0.1.3-py3-none-win_amd64.whl
Size 1.8 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
9033879fcc8605c63b587992a3199e1d0229afef1f8059f40505a36435efbec9
BLAKE2b-256 checksum
How to use checksums
b793141307a062619e774275a6e311e4365d3b15c58681ff5060d5bab9f4f635
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release files / ill_be_backspace-0.1.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL ill_be_backspace-0.1.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.9 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
a4f9c0544be8a57668ef22fb8df9efe001916c52387c6255daf903ca25bb3886
BLAKE2b-256 checksum
How to use checksums
e9737e95eae569abb5e586f7bcc00c88d579fe1656db13f9a7f02619e9db320d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release files / ill_be_backspace-0.1.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL ill_be_backspace-0.1.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.8 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
3d27d7a59a8c36f1a1797548c2f51a1b2e0372c37f1784ba7ef82832147dfa88
BLAKE2b-256 checksum
How to use checksums
03ec05b064703724102aada2eef617395c12a73dff989179a481acdec1665596
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release files / ill_be_backspace-0.1.3-py3-none-macosx_11_0_arm64.whl

Download URL ill_be_backspace-0.1.3-py3-none-macosx_11_0_arm64.whl
Size 1.7 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
47c13ed5670a855c031e1be7a3f3541fc554c5872473750ce25ddcdf28c5f2b6
BLAKE2b-256 checksum
How to use checksums
d149ef71f5486e2ab25f82cc1d60847846e76cf365f0d19267872b6d912529a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release files / ill_be_backspace-0.1.3-py3-none-macosx_10_12_x86_64.whl

Download URL ill_be_backspace-0.1.3-py3-none-macosx_10_12_x86_64.whl
Size 1.8 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
dda36cb9a694b6ded650c8fc6cb18b4f8bfd0170b17ffa285b8fe0cbb6455727
BLAKE2b-256 checksum
How to use checksums
99ccadbacc6310532ce7b411ad3932843b9219e3131721e40e8d1dbc23cf12c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.14.1

Release history Release notifications | RSS feed

0.2.0

6 release files

This release

0.1.3 This release

6 release files

0.1.2

6 release files

0.1.1

6 release files

0.1.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page