When Underscores Beat Hyphens
For URLs, hyphens win — Google has stated for years that it treats a hyphen as a word
separator and an underscore as a word joiner, so red_shoes reads as one token and
red-shoes as two.
Everywhere else, underscores are frequently the right choice:
| Context | Separator | Why |
|---|---|---|
| URL paths | Hyphen | Treated as a word break by search engines |
| Python modules and variables | Underscore | PEP 8 requires it |
| SQL tables and columns | Underscore | Hyphens require quoting |
| Filenames in scripts | Underscore | A leading hyphen looks like a command flag |
| Environment variables | Underscore | The only separator POSIX permits |
| CSS classes | Hyphen | Convention, and hyphens read better in selectors |
The Filename Argument
A file named -report.csv is genuinely dangerous in a shell: most commands parse it as an
option. rm -report.csv fails, and worse constructions do something unintended. Underscores
have no such meaning anywhere in a shell.
Double-Click Selection
In most editors and terminals, double-clicking a word stops at a hyphen and continues through an underscore. For an identifier you want selected as a unit — a table name, a variable — that behaviour is a real ergonomic argument for the underscore.
What the Generator Does
Beyond swapping the separator, converting a title to a slug means:
- Transliterating accents.
Crème brûléebecomescreme_brulee, notcrm_brle. - Removing punctuation rather than encoding it.
- Collapsing runs so no double separators appear.
- Trimming the ends, since a leading or trailing separator is always wrong.
- Lower-casing, unless the target is case-sensitive and you want it preserved.
Stop Words
Removing the, a, of and similar shortens a slug without changing what it says. For
identifiers that is usually good; for URLs it can hurt readability. The generator makes it
optional because there is no single right answer.
Slugs in a Real Content System
| Decision | Recommendation |
|---|---|
| Generate from | The title, at draft time, then let the author edit |
| Freeze on | First publish — after that, changing it costs a redirect |
| Collisions | Append -2, not a timestamp or a random suffix |
| Depth | /blog/post-name, not /blog/2026/08/category/post-name |
| Case | Lowercase, always — URLs are case-sensitive on most servers |
Redirects When One Changes
``
301 /old-slug → /new-slug
`
Permanent, kept forever, and never chained. Two hops lose more than one, and a chain of
three is treated by crawlers as a soft failure. Keep a redirect table rather than editing
the previous rule in place.
Non-Latin Titles
Transliteration is the pragmatic choice for a Latin-script audience — Привет becomes
privet. If your readers use the script, keep it: modern browsers and search engines
handle IDN and percent-encoded UTF-8 paths correctly, and /статья` is more readable to a
Russian speaker than any transliteration. The wrong answer is silently dropping the
characters, which produces an empty or mangled slug.