Guide
Naming conventions: camelCase, snake_case, and why splitting is the hard part
Converting camelCase to snake_case looks like a joining problem — pick a separator, lowercase everything, done. It isn't. The actual work is splitting the original identifier into the right words in the first place, and that step is where every case converter, including this one, has to make judgment calls.
The conventions, precisely
This tool converts between ten styles, and it's worth being exact about what each one means rather than gesturing at examples: camelCase (first word lowercase, each subsequent word capitalised, no separators), PascalCase (every word capitalised, no separators), snake_case (lowercase, underscore-separated), CONSTANT_CASE (uppercase, underscore-separated), kebab-case (lowercase, hyphen-separated), dot.case (lowercase, dot-separated), Title Case and Sentence case (which operate on the original text's punctuation and spacing rather than re-splitting it into words), and plain upper/lower casing.
Where each one actually comes from
- camelCase traces back to early object-oriented languages and was cemented by Java's naming conventions for methods and variables — it's the default in JavaScript, Java, and C# for anything that isn't a type.
- PascalCase is camelCase's sibling for things that name a type rather than a value: Java and C# classes, JavaScript constructor functions and React components. The distinction — lowercase-first for values, uppercase-first for types — is a convention almost every one of those ecosystems shares even though nothing forces it.
- snake_case comes out of the C, Python and Ruby lineage, and in Python's case it's not just custom — PEP 8 specifies it for functions and variables, and popular linters flag camelCase as a style violation.
- kebab-case is what URLs and CSS use, for a concrete reason: a hyphen is one of the unreserved characters in a URL per RFC 3986, so a slug built from hyphens never needs percent-encoding, and CSS property and custom-property names have used hyphens since long before underscores were straightforward there.
- CONSTANT_CASE signals "this is meant to be immutable" purely as a human convention — nothing in most languages' grammars enforces it, though some linters do — and it echoes the much older shell/POSIX convention of writing environment variables in all caps.
When case is not just style, but grammar
Most of the conventions above are lint rules — real, widely enforced, but not part of the language itself. Go is the sharp counterexample: whether an identifier is exported from its package is determined by the case of its first letter. Capitalized names are public API; lowercase names are package-private. That isn't a style guide recommendation, it's something the compiler checks — the one case in this list where getting the casing wrong doesn't just fail a lint rule, it changes what your code does.
The actual hard part: splitting the identifier
Joining words in a target style is trivial once you have the word list. Getting the word list is not, and this tool's approach is a good illustration of what "getting it right" actually takes. The core rule is simple: insert a boundary wherever a lowercase letter or digit is immediately followed by an uppercase letter.
userName -> user Name
user2Name -> user2 Name
apiKey -> api Key
That one rule alone mishandles a run of capitals — an acronym — sitting next to a following word, because there's no lowercase-to-uppercase transition inside a run of all-caps letters for the first rule to catch. HTTPServer needs a second, more specific rule: a run of uppercase letters immediately followed by an uppercase letter and then a lowercase letter gets split before that last capital, so the boundary lands between the acronym and the word that follows it rather than one letter too early or too late.
HTTPServer -> HTTP Server (splits before the word, keeping the acronym intact)
XMLParser -> XML Parser
After both rules run, whatever remains — spaces, underscores, hyphens, dots, any run of non-letter-non-digit characters — is treated as an existing separator and split on directly, so a name that's already partially separated (user_ID, say) gets the same treatment as one that never was.
Where splitting is genuinely ambiguous
Some inputs don't have one obviously correct split, and it's worth knowing that rather than being surprised by it. iOS splits, under the rule above, into i and OS — which looks wrong until you remember that's genuinely where the name came from: "i" plus "Operating System." The algorithm gets it right here by accident of etymology, not because it knows what an operating system is.
Contrast that with something like APIKey: is the intended split API Key, or does a naive rule see it differently? Knowing that "API" is a recognised three-letter acronym rather than an arbitrary run of capitals requires a dictionary of known acronyms — information a pure case-transition rule structurally doesn't have. This is a limitation every case converter built this way shares, this one included; a dictionary-backed approach can do better on known acronyms at the cost of not knowing about acronyms it's never been told about either.
Why letters, not just A–Z
The splitting rules above are written against Unicode letter and number categories rather than the ASCII range [A-Za-z0-9], specifically so an accented or non-Latin letter is treated as a real letter rather than being read as a separator and chopped apart. A name like café or Straße keeps its accented letters as part of the word instead of splitting on them the way punctuation would — a small detail, but the kind that silently breaks a name-processing tool the first time it meets text outside the original developer's own alphabet.
One concept, several names, at the same time
The practical reason a case converter earns a place in a toolbox rather than being a novelty: the same underlying field routinely needs several different casings simultaneously, not sequentially. A database column created_at becomes a JSON API field createdAt becomes, in some frameworks, a URL query parameter created-at — three casings for one concept, all live in the same system at once, each because the layer that owns it follows its own ecosystem's convention from the table above.
Renaming that concept means updating all three, consistently, and getting the split-then-rejoin right in each direction is precisely the mechanical problem this guide has been describing — which is also exactly why an IDE's "rename symbol" refactor tool needs the same word-splitting logic internally to offer sensible renamed suggestions across a codebase that mixes casings by design, not by accident.
Which convention where
- Variables and functions in JS, Java, C# — camelCase.
- Classes, types, and React/Vue components — PascalCase.
- Variables and functions in Python, Ruby, Rust — snake_case, and in Rust's case the compiler will warn on a non-snake_case name by default, not just a linter.
- Environment variables and genuine constants — CONSTANT_CASE, following the decades-old shell convention.
- URL paths, CSS classes, npm package names — kebab-case.
- Database table and column names — commonly snake_case, and there's a real portability reason beyond taste: several SQL engines fold unquoted identifiers to a fixed case, and the fold direction isn't consistent across engines or operating systems, so camelCase columns can silently work on one setup and break on another. Underscore-separated lowercase sidesteps the whole question.
Last updated 21 August 2026