camelCase, snake_case or kebab-case?
Naming Conventions by Language
Use whatever the language or format you are writing in expects, because the convention is part of how readers and tools parse your code. In practice: camelCase for variables and functions in JavaScript, TypeScript, Java, Kotlin, Swift and C# locals; snake_case for variables and functions in Python, Ruby, Rust and SQL; PascalCase for types and classes almost everywhere; UPPER_SNAKE_CASE for constants and environment variables; kebab-case for URLs, CSS classes, HTML attributes, command-line flags and most file names on the web. Where two conventions meet, such as a Python service returning JSON to a JavaScript client, pick one for the boundary, document it, and convert at the edge.
The five cases
| Name | Example | Word boundary | Typical home |
|---|---|---|---|
| camelCase | retryCount |
Uppercase letter | JS/TS/Java/Swift/Kotlin variables and methods |
| PascalCase (UpperCamelCase) | RetryCount |
Uppercase letter | Types, classes, components in most languages |
| snake_case | retry_count |
Underscore | Python, Ruby, Rust, SQL, C standard library |
| UPPER_SNAKE_CASE (CONSTANT_CASE) | RETRY_COUNT |
Underscore | Constants, environment variables, C macros |
| kebab-case | retry-count |
Hyphen | URLs, CSS, HTML attributes, CLI flags, file names |
kebab-case cannot be used for identifiers in most programming languages because retry-count parses as retry minus count. That single fact explains most of the table: kebab-case lives wherever names are strings or markup rather than code.
By language
| Language | Variables, functions | Types, classes | Constants | Modules, packages, files | Enforced by |
|---|---|---|---|---|---|
| JavaScript / TypeScript | camelCase |
PascalCase (also React components) |
UPPER_SNAKE for module-level constants |
kebab-case.ts common; component files often PascalCase.tsx |
Linters only |
| Python | snake_case |
PascalCase |
UPPER_SNAKE |
snake_case.py; hyphens make a module unimportable |
PEP 8, linters |
| Java / Kotlin | camelCase |
PascalCase |
UPPER_SNAKE (static final) |
lowercase reverse-domain packages; file name must equal the public class |
Compiler (file name), style guides |
| C# | locals and parameters camelCase; methods, properties PascalCase |
PascalCase; interfaces IPascalCase |
PascalCase |
Namespaces PascalCase |
.NET design guidelines, analyzers |
| Go | camelCase if unexported, PascalCase if exported |
same rule | same rule (no UPPER_SNAKE) |
package names short, lowercase, no underscores; files snake_case.go |
The compiler: case controls visibility |
| Rust | snake_case |
PascalCase (types, traits, enum variants) |
UPPER_SNAKE (const, static) |
snake_case modules |
Compiler warnings |
| Ruby | snake_case |
PascalCase |
UPPER_SNAKE (any capitalised name is a constant) |
snake_case.rb |
Interpreter (constants), RuboCop |
| PHP (PSR-1) | methods camelCase |
PascalCase |
UPPER_SNAKE |
File per class, PascalCase.php |
PSR standards |
| Swift | camelCase |
PascalCase |
camelCase (no special constant case) |
PascalCase.swift |
API design guidelines |
| SQL | snake_case columns and tables |
n/a | n/a | n/a | Case folding (see below) |
Three of those entries have consequences beyond style:
- Go uses the case of the first letter as its visibility modifier.
func parseConfigis private to its package;func ParseConfigis exported. Renaming for style changes the API. - Ruby treats any identifier starting with a capital letter as a constant, and warns when you reassign it.
- Go file names carry meaning in their suffixes:
_test.gomarks test files, and_linux.goor_amd64.gorestricts a file to a platform. A file namedserver_linux.gofor style reasons will silently disappear from macOS builds.
Outside programming languages
| Context | Convention | Why |
|---|---|---|
| URLs and slugs | kebab-case |
Search engines treat hyphens as word separators and underscores as joiners; hyphens are also unambiguous when underlined as a link |
| CSS classes, custom properties | kebab-case (--accent-color) |
CSS property names are kebab-case; BEM adds block__element--modifier |
HTML data- attributes |
kebab-case |
The DOM maps data-user-id to element.dataset.userId automatically |
| CLI flags | --kebab-case |
POSIX and GNU convention (--dry-run) |
| Environment variables | UPPER_SNAKE |
POSIX portable names are uppercase letters, digits and underscore |
| SQL identifiers | snake_case, lowercase |
Unquoted names fold to lowercase in PostgreSQL and uppercase in Oracle and the standard; lowercase snake_case survives both |
| JSON APIs | camelCase or snake_case |
No standard; camelCase matches JavaScript consumers, snake_case matches Python and Ruby producers. Consistency matters more than the choice |
| YAML configuration | Follows the tool | Kubernetes uses camelCase keys, Compose files snake_case, many CI systems kebab-case |
| Git branches, Docker images | kebab-case, lowercase |
Image repository names must be lowercase |
The SQL row is worth expanding. A column created as "createdAt" in PostgreSQL must be quoted with that exact case in every query; unquoted createdAt folds to createdat and fails. That is why ORMs map createdAt in code to created_at in the database, and why the SQL style guide recommends lowercase snake_case.
Acronyms: the part nobody agrees on
The hardest naming question is not which case to use but how to treat initialisms like ID, URL, HTTP and XML inside a camelCase or PascalCase name. The official style guides disagree:
| Concept | Java (Google style) | C# (.NET guidelines) | Go | Rust | Swift | Python (PEP 8) |
|---|---|---|---|---|---|---|
| user ID variable | userId |
userId |
userID |
user_id |
userID |
user_id |
| HTTP server type | HttpServer |
HttpServer |
HTTPServer |
HttpServer |
HTTPServer |
HTTPServer |
| XML HTTP request type | XmlHttpRequest |
XmlHttpRequest |
XMLHTTPRequest |
XmlHttpRequest |
XMLHTTPRequest |
XMLHTTPRequest |
| IO stream type | IoStream |
IOStream (two-letter acronyms stay upper) |
IOStream |
IoStream |
IOStream |
IOStream |
| parse URL function | parseUrl |
ParseUrl |
parseURL |
parse_url |
parseURL |
parse_url |
Two philosophies are in play. Treating an acronym as a word (XmlHttpRequest) keeps word boundaries visible and makes mechanical conversion reliable. Keeping acronyms uppercase (XMLHTTPRequest) preserves how the term is normally written, at the cost of ambiguity: XMLHTTPRequest could be split as XMLHTTP Request. Even standard libraries are inconsistent; the JDK has both HttpURLConnection and URLEncoder, and the browser's own XMLHttpRequest mixes both styles in one name.
For new code, the word-style rule is easier to live with: every word, including acronyms, gets exactly one leading capital. It round-trips cleanly between cases, which matters as soon as a name travels from a database column to a JSON field to a class property.
Converting between cases
Converting user id, HTTPResponseCode or max_retry_count to another case means first splitting the name into words, then joining them with the target convention. The Case Converter does both, one name per line:
user id
HTTPResponseCode
XMLHttpRequest
max_retry_count
created-atWith Case set to snake_case, the result is:
user_id
http_response_code
xml_http_request
max_retry_count
created_atThe splitter recognises a run of capitals followed by a capitalised word (HTTPResponse becomes http and response), which handles the common cases. Some names defeat any mechanical splitter because the information needed is not in the string: userIDs comes out as user_i_ds, IPv6Address as i_pv6_address, and OAuth2Token as o_auth2_token. A converter cannot know that IDs is a plural acronym or that OAuth is one word. Convert in bulk, then review the names containing acronyms, digits or mixed-case brand names by hand; this is another argument for the acronym-as-word style, whose names convert without surprises.
Beyond case: words that carry meaning
Case is only half of a name. A few word-level conventions are close to universal and worth adopting alongside whichever case you use:
- Booleans read as questions:
isActive,has_children,shouldRetry. A bareactivecould be a flag, a timestamp or a list. - Units in the name:
timeout_ms,size_bytes,created_at_s. This prevents the most common numeric bugs, such as passing seconds where milliseconds are expected, described in the Unix timestamps guide. - Plurals for collections:
usersfor a list,userfor one item,user_idsfor a list of identifiers. - No type prefixes: Hungarian notation (
strName,iCount) predates type-aware editors and goes stale when the type changes. - Suffixes by convention:
...Erroror...Exceptionfor error types,...Testortest_...for tests, so tools and readers can find them.
Practical rules
- Follow the host language's convention inside its code, even when you prefer another. Readers of Python expect
snake_case; fighting it costs more than it saves. - At boundaries (API payloads, database columns, configuration keys), choose one convention per boundary, document it, and convert in one place: a serializer setting, an ORM naming strategy, or a mapping layer.
- Treat acronyms as words in camelCase and PascalCase unless your language's guide says otherwise (Go, Swift, Python and C# for two-letter acronyms do).
- Never rely on case alone to distinguish two names in systems that fold case: SQL, Windows and macOS file systems, email addresses, DNS names.
- Use
UPPER_SNAKE_CASEonly for true constants and environment variables, so that it keeps meaning "this does not change at runtime".