The context picks for you, not your taste. camelCase (userName) names variables and functions in JavaScript, Java, Kotlin and Swift. snake_case (user_name) names variables and functions in Python, Ruby and Rust, and columns in SQL. kebab-case (user-name) belongs anywhere that is not a program identifier — URLs, CSS class names, HTML attributes, file names, npm packages — because in almost every language a hyphen is the subtraction operator and user-name parses as user minus name.
The exceptions are narrow: the Lisp family uses hyphens for everything, C# capitalises its method names, and Go turns the first letter into an access modifier. None of those is what costs you time. The seam between a snake_case back end and a camelCase front end is.
What does each convention look like?
Three is the wrong number. The two conventions you did not ask about live in the same files as the three you did.
| Convention | Example | Where it belongs |
|---|---|---|
| camelCase | totalItemCount | Variables, functions and JSON keys in JavaScript, Java, Kotlin and Swift |
| PascalCase | TotalItemCount | Classes, types and React components nearly everywhere |
| snake_case | total_item_count | Python, Ruby and Rust identifiers; SQL tables and columns |
| CONSTANT_CASE | TOTAL_ITEM_COUNT | Constants and environment variables, in almost any language |
| kebab-case | total-item-count | URLs, CSS classes, HTML attributes, file names, package names |
Which case does my language expect?
Most of these are written down, which means the argument is already settled and you can skip having it.
- Python — PEP 8 is explicit: functions and variables in snake_case, classes in CapWords, constants in upper case with underscores.
- JavaScript and TypeScript — camelCase for variables and functions, PascalCase for classes and components. There is no single official document; the convention comes from the standard library itself, which is full of
toLowerCaseandgetElementById. - Java, Kotlin, Swift — camelCase for members, PascalCase for types.
- C# — PascalCase for types, methods and public properties; camelCase for parameters and locals. It is the mainstream outlier: a method call there looks like a class name anywhere else.
- Ruby — snake_case for methods and variables, PascalCase for classes and modules, upper case for constants. The interpreter enforces the last one: a name starting with a capital is a constant.
- Rust — snake_case for functions, variables and modules; UpperCamelCase for types and traits; upper case with underscores for constants and statics. The compiler warns you when you deviate, which settles most reviews before they start.
- Go — MixedCaps, never underscores, and the capital letter is load-bearing: an identifier that starts upper case is exported from its package, one that starts lower case is not. Case is visibility here, not style. Renaming
counttoCountpublishes it to every package that imports yours.
When a repo already has a convention, follow it even if it is the one you like less. A codebase that is consistently unfashionable reads better than one that is half-converted.
Constants are the near-universal case
Upper case with underscores for constants holds everywhere on that list except Go, which names constants in MixedCaps like everything else — MaxRetries, not MAX_RETRIES. Environment variables are the closest thing to an absolute: shells, container runtimes and the tooling around .env files all assume upper case with underscores. A lower-case env var name usually works and still reads as a mistake to whoever sees it next.
Why kebab-case cannot be a variable name
Because the hyphen is already the minus sign. Write user-name = 5 in JavaScript and the parser reads a subtraction between a variable called user and one called name, then refuses to assign to the result: Invalid left-hand side in assignment. Put let in front and it fails one step earlier, on the hyphen itself. Python answers cannot assign to expression. Java, C#, Go, Rust, PHP and SQL all reject it for the same reason. That is the entire story: kebab-case is confined to places where nothing is trying to evaluate an expression.
There are two real exceptions. The Lisp family — Clojure, Scheme, Emacs Lisp — uses kebab-case for everything, which is why the style is sometimes called lisp-case; those readers do not treat a bare hyphen as an operator. And CSS custom properties are written --brand-colour, hyphens and all, because CSS is not evaluating arithmetic on identifiers either.
Where kebab-case is the right answer
- URLs. Google has recommended hyphens over underscores in URLs for years, on the grounds that hyphens are read as word separators and underscores historically were not. If you are naming pages, what makes a slug readable and stable matters more than the separator, but the separator is a hyphen.
- CSS classes and HTML attributes. Both are conventionally lower case with hyphens. The interesting bit is the seam: a
data-user-idattribute in the markup is read from JavaScript asdataset.userId. The browser converts kebab to camel for you, which is one of the few places the two conventions are deliberately wired together. - npm package names. npm rejects capital letters in new package names outright, so the decision is made for you.
- File names. Windows and the default macOS filesystem are case-insensitive, so
Readme.mdandreadme.mdare the same file to the operating system and two different files to Git. Lower case with hyphens sidesteps that argument permanently.
What happens where two conventions meet?
Inside one codebase, conventions are a style question. The cost appears where two of them meet: a Python service using snake_case, a JavaScript front end using camelCase, and a JSON payload passing between them. Something has to convert, and the failure mode is that everybody converts a little, in different places, until half your objects carry both created_at and createdAt and neither is reliably populated.
Pick one layer — the serializer, usually — and convert there and nowhere else. Google's JSON style guide recommends camelCase for property names, which is a reasonable default if you have no other constraint, but the choice matters far less than making it once.
SQL deserves its own warning. Unquoted identifiers get folded to one case — the standard folds up, PostgreSQL folds down — so a column you create as createdAt is really called createdat. Double-quote it at creation and the capital survives, but then every query that touches it has to quote it too, forever, and forgetting once is an error rather than a typo. snake_case is the convention there because it comes out of the folding unchanged.
How do I convert a name from one case to another?
Renaming by hand is where typos come from. A case converter takes a name and gives you the same name in camelCase, PascalCase, snake_case, CONSTANT_CASE or kebab-case, which is the fast way to move an identifier across a language boundary. It runs in the browser, so pasting internal variable names into it does not send them anywhere.
Two limits are worth knowing before you trust the output. Acronyms defeat it, and they defeat every general-purpose converter for the same reason: the split relies on a lower-case letter followed by an upper-case one, and HTTPResponse has no such boundary. You get httpresponse, not http_response; parseXMLFile becomes parse_xmlfile. If your names contain acronyms, read the result before pasting it back.
The programming cases treat the whole box as one identifier. Upper case, lower case, Title Case and Sentence case work on a paragraph, but camelCase, snake_case and kebab-case collapse everything you paste — line breaks included — into a single name. Feed those one identifier at a time.
How do I rename a convention across a project?
The tempting move is a blanket search and replace, and the danger is not the one people expect. Whole-word matching stops userName from matching inside userNames. What it does not stop is the replacement landing inside a string literal, a comment, a SQL query or the key of an API payload — so a rename that was supposed to be cosmetic silently changes the wire format. Working through a replace that lists every match with its line number before you commit to it makes those cases visible while they are still cheap.
That one works on a block of text you paste in, and it lists the first hundred matches. It fits a single file you are about to paste back, not a hundred-file sweep. For the sweep you want sed or your editor's project-wide search — same discipline, read the matches before you replace them.
Afterwards, read the diff rather than the test output. Comparing the before and after side by side catches the three lines you did not mean to touch faster than a test suite that only covers the paths you thought about.
Does any of it matter?
Not for correctness, and not for performance. It matters for grep: consistent names mean one search finds every occurrence, while inconsistent ones mean you find most of them and get bitten by the rest. There is academic work comparing how fast people read snake_case and camelCase, and it settles nothing: the studies are small and their results contradict each other, so it is not a sound basis for a decision. Consistency is the benefit you can actually observe. Everything else is preference dressed as principle.
For the mechanical part — one name, five conventions, no typing — the case converter handles it in the browser, including the Title Case and Sentence case modes that have nothing to do with code. Just remember it collapses whatever you paste into a single identifier when you pick one of the programming cases.
If the reason you are here is that a convention has to change across a whole project, the rename itself is the risky part. The mistakes that turn a find and replace into an hour of cleanup covers what goes wrong inside strings, comments and partial matches, which is exactly where a case rename does its damage.
Frequently asked questions
What is the difference between camelCase and snake_case?
camelCase joins words by capitalising each one after the first, as in userName. snake_case joins them with underscores and keeps everything lower case, as in user_name. Neither is better; they mark which language community the code belongs to, with camelCase standard in JavaScript, Java and Swift, and snake_case standard in Python, Ruby and Rust.
Can I use kebab-case for a variable name?
In most languages, no. The hyphen is the subtraction operator, so user-name is parsed as user minus name and the line fails to compile. The exceptions are the Lisp family, including Clojure and Scheme, where kebab-case is the normal style, and CSS custom properties such as --brand-colour.
Which case should I use for URLs?
Lower-case kebab-case. Google treats hyphens in a URL as word separators and has recommended them over underscores for years. Lower case also avoids duplicate-content problems on servers that treat paths as case-sensitive.
Should JSON keys be camelCase or snake_case?
Either works, and consistency matters more than the choice. Google’s JSON style guide recommends camelCase, which is a sensible default for an API consumed by JavaScript. If your API is the public face of a Python or Ruby service, snake_case keys will match the rest of your documentation and save a conversion layer.
How do I convert camelCase to snake_case?
Split the name at every point where a lower-case letter is followed by an upper-case one, lower-case everything and join with underscores. Automated tools do this reliably except with acronyms: HTTPResponse has no such boundary, so it converts to httpresponse rather than http_response. Check any name containing an acronym by hand.
Is snake_case or camelCase more readable?
There is research on this and it does not reach a clear answer; the studies are small and their results conflict. In practice readability comes from consistency within a project and from names that describe the thing, not from the separator. Follow whatever the language and the existing codebase already use.
Last updated September 19, 2026