Developer Tools

camelCase vs snake_case vs kebab-case: A Developer's Guide

PAYATE Team· · 15 min read ·Updated Oct 18, 2026
camelCase vs snake_case vs kebab-case: A Developer's Guide

Last reviewed: October 1, 2026

Short answer: camelCase, snake_case and kebab-case are three ways to join several words into one name without spaces. camelCase capitalizes each word after the first (userProfileId), snake_case joins lowercase words with underscores (user_profile_id) and kebab-case joins them with hyphens (user-profile-id). None is better in general. Use the one your language or platform expects: camelCase for JavaScript, Java and C# locals, snake_case for Python, Ruby and Rust functions and for most SQL schemas, kebab-case for CSS, HTML attributes and URLs.

The rule that matters most is consistency inside each layer, plus a deliberate, automated mapping where layers meet.

This guide defines each style, shows what the official style guides of the major ecosystems actually say, and covers the awkward parts: acronyms, digits, case-insensitive systems and converting names between layers. When you need to rename a batch of identifiers or turn a heading into a slug, a free browser-based case converter saves retyping; the rest of this article explains which target style to choose and what the conversion does to your words.

In this guide

  1. What case styles are and why consistency matters
  2. The styles defined
  3. What each language's style guide recommends
  4. CSS, HTML, URLs, SQL, JSON and environment variables
  5. Where kebab-case cannot be used
  6. Acronyms, numbers and underscores
  7. Converting between styles
  8. Mapping names between layers
  9. Case sensitivity pitfalls
  10. Enforcing conventions and renaming safely
  11. Common mistakes
  12. Cheat sheet
  13. FAQ
  14. Sources

What case styles are and why consistency matters

A case style is a rule for writing a multi-word name as a single token: how words are separated and which letters are capitalized. Identifiers cannot contain spaces, so every codebase needs some such rule.

Consistency matters for three practical reasons. First, readers use the shape of a name as a signal: OrderItem looks like a type, orderItem like a variable and ORDER_ITEM like a constant. Second, predictable names are searchable. Third, some systems give case real meaning: Effective Go states that a name is visible outside its package only if its first character is upper case.

The styles defined

Every style answers two questions: what goes between words (nothing, underscore, hyphen, dot) and which letters are upper case.

StyleExampleAlso calledTypical use
camelCaseuserProfileIdlowerCamelCase, mixedCaseVariables and functions in JavaScript, Java, C# parameters; JSON keys
PascalCaseUserProfileIdUpperCamelCase, CapWords, StudlyCapsClasses and types almost everywhere; public members in C#; exported names in Go
snake_caseuser_profile_idlower_case_with_underscoresPython, Ruby and Rust functions and variables; SQL tables and columns
SCREAMING_SNAKE_CASEUSER_PROFILE_IDCONSTANT_CASE, UPPER_SNAKE_CASEConstants; environment variables
kebab-caseuser-profile-idhyphenated, dash-caseCSS properties and classes, HTML attributes, URL slugs
Train-CaseUser-Profile-IdCapitalized kebab-caseThe customary spelling of HTTP header names such as Content-Type
dot.caseuser.profile.idNamespaced keys in configuration files and package names

MDN's glossary notes that in camel case the very first letter may be lower case (lower camel case) or upper case (upper camel case, also known as Pascal case).

Two related styles apply to text, not identifiers: sentence case capitalizes only the first word and proper nouns, and title case capitalizes the main words of a heading.

What each language's style guide recommends

Most ecosystems agree on PascalCase for types and disagree on everything else. The table summarizes the guide for each language; details follow.

Language (guide)Variables, functionsClasses, typesConstantsNotes
Python (PEP 8)snake_caseCapWordsUPPER_CASE_WITH_UNDERSCORESModules: short, all lowercase
JavaScript (MDN, Google)camelCasePascalCaseCONSTANT_CASEFile names lowercase with _ or - (Google)
TypeScript (Google)lowerCamelCaseUpperCamelCaseCONSTANT_CASENo leading or trailing underscores
Java (Google)lowerCamelCaseUpperCamelCaseUPPER_SNAKE_CASEPackages all lowercase, no underscores
C# (.NET guidelines)camelCase for parametersPascalCasePascalCasePublic methods and properties are PascalCase too
Go (Effective Go)mixedCapsMixedCapsMixedCapsUpper-case first letter means exported
Rust (API Guidelines)snake_caseUpperCamelCaseSCREAMING_SNAKE_CASEModules and macros snake_case
Ruby (community guide)snake_caseCamelCaseSCREAMING_SNAKE_CASEFiles and directories snake_case
PHP (PSR-1, PSR-12)camelCase methodsStudlyCapsUPPER_CASE with underscoresNo rule for property names

Points the table cannot show:

  • Python. PEP 8 says function and variable names should be lowercase with words separated by underscores, classes use CapWords, and modules get short, all-lowercase names.
  • JavaScript and TypeScript. MDN's code style guide and the Google JavaScript Style Guide agree on camelCase for variables and functions and PascalCase for classes. The Google TypeScript guide adds UpperCamelCase for interfaces, types and enums and says identifiers must not use an underscore as a prefix or suffix.
  • Java. The Google Java Style Guide concatenates lowercase words in package names, as in com.example.deepspace.
  • C#. The .NET conventions use PascalCasing for all public member, type and namespace names and camelCasing only for parameters, and say not to use underscores to separate words. A C# method is GetUserName(), not getUserName().
  • Go. Effective Go says to use MixedCaps or mixedCaps rather than underscores. Package names are lower case, single-word names. Owner is exported; owner is not.
  • Rust. The API Guidelines follow RFC 430: UpperCamelCase for types, traits and enum variants, snake_case for functions, methods, modules and macros.
  • Ruby. There is no single official guide; the table reflects the widely used community Ruby Style Guide.
  • PHP. PSR-1 intentionally avoids any recommendation on property names, asking only for consistency. PSR-12 builds on PSR-1 and says names must not be prefixed with a single underscore to indicate protected or private visibility.

CSS, HTML, URLs, SQL, JSON and environment variables

Outside programming languages the conventions are driven by syntax rules and case sensitivity: kebab-case on the web platform, snake_case in SQL, upper snake case for environment variables.

CSS classes and custom properties

CSS property names such as background-color and font-family are kebab-case, so class names and custom properties usually follow suit: .nav-item, --main-color. Custom properties start with two dashes and, per MDN, are case sensitive: --my-color and --My-color are different properties.

HTML attributes and data-* attributes

HTML attributes such as aria-label are kebab-case. The HTML Standard allows attribute names to be written in any mix of upper and lower case, but lowercase is the norm. Custom data-* attributes have a documented bridge to JavaScript: the dataset property removes the data- prefix and converts the rest from kebab-case to camelCase.

<div id="user" data-user-id="42" data-date-of-birth="1990-01-31"></div>

const el = document.querySelector("#user");
el.dataset.userId;        // "42"
el.dataset.dateOfBirth;   // "1990-01-31"
el.dataset.lastLogin = "today";   // creates data-last-login="today"

MDN recommends that data attribute names contain no capital letters, and warns that a hyphen followed by a non-letter, as in data-test-1, does not map cleanly to dataset.

URLs and slugs

Use lowercase kebab-case for URL paths. Google Search Central recommends hyphens instead of underscores to separate words in URLs because it helps users and search engines identify the concepts in the URL. It also notes that Google treats /APPLE and /apple as distinct URLs, so pick one case, and lowercase is the practical choice.

SQL identifiers

Use snake_case for tables and columns. The reason is technical, not aesthetic: unquoted SQL identifiers are case-insensitive, so camelCase is lost unless you quote every reference (details in the case sensitivity section). The PostgreSQL manual describes the common convention of writing key words in upper case and names in lower case, as in UPDATE my_table SET a = 5;.

Environment variables

Use SCREAMING_SNAKE_CASE, as in DATABASE_URL. POSIX says the environment variable names used by its standard utilities consist solely of uppercase letters, digits and the underscore, and do not begin with a digit. It reserves names containing lowercase letters for applications, so lowercase names are legal, but the upper-case convention is what tools and people expect.

JSON field names

There is no single standard, so choose one style per API and document it. The JSON:API recommendations say member names should be camel-cased. Google's API design guidance (AIP-140) defines protobuf fields in lower_snake_case and maps them to an appropriate convention in JSON. Many public APIs use snake_case keys instead. Any of these is fine; mixing them in one API is not.

File names

Follow the ecosystem: the Google JavaScript guide requires lowercase file names that may include underscores or dashes, and the Ruby community guide uses snake_case files. Avoid file names that differ only by case.

Where kebab-case cannot be used

Kebab-case cannot be an identifier in most programming languages because the hyphen is the minus operator. MDN's glossary makes the same point: hyphens represent subtraction, which makes kebab casing unsuitable for variable names.

const user-name = "Ada";     // SyntaxError
total = user-count;          // parsed as: user minus count

// Kebab-case keys are still usable as strings:
const headers = { "content-type": "application/json" };
headers["content-type"];     // works
headers.content-type;        // parsed as: headers.content minus type

That is why kebab-case lives where names are not expression syntax: CSS, HTML attributes, URLs and file names. When such a name crosses into code it is converted, as dataset does, or quoted as a string key.

Acronyms, numbers and underscores

Acronyms are where style guides genuinely disagree, so follow the guide for your language rather than a universal rule.

GuideRuleExample
Google Java, JavaScript, TypeScriptTreat acronyms as ordinary wordsnewCustomerId, XmlHttpRequest, loadHttpUrl
Rust API GuidelinesAcronyms count as one word in UpperCamelCaseUuid, not UUID
.NETTwo-letter acronyms stay upper case; longer ones are capitalized like wordsIOStream, HtmlTag; Id, not ID
Python PEP 8Capitalize all letters of an acronym in CapWordsHTTPServerError
Ruby community guideKeep acronyms upper case in class namesXMLParser

So is it userID or userId? Under the Google guides and .NET it is userId. Platform names do not always follow either school: MDN points out that XMLHttpRequest mixes both approaches, upper-casing XML but not Http. The treat-as-words rule has a practical advantage: names convert cleanly. HttpServer splits into two words unambiguously, while HTTPServer needs a smarter splitter and HTTPSURL cannot be split by a machine at all.

Numbers. Identifiers cannot start with a digit in most languages, and digits create ambiguous word boundaries: is ipv6Address "ipv6 address" or "ipv 6 address"? The Google Java guide, for example, gives turnOn2sv for "Turn on 2SV". Expect converters to disagree on names like sha256Hash.

Leading underscores and dunders. In Python, underscores carry meaning that sits on top of the case style. PEP 8 and the language reference describe _name as a weak internal-use marker that from module import * skips, name_ as a way to avoid clashing with a keyword, __name inside a class as triggering name mangling, and __name__ ("dunder" names such as __init__) as reserved for the language. Other guides differ: Google's TypeScript guide forbids underscore prefixes and suffixes, and PSR-12 says an underscore prefix has no meaning. A case converter should preserve leading underscores rather than treat them as separators.

Converting between styles

Every conversion is the same two steps: split the name into words, then rejoin the words in the target style. Rejoining is trivial; splitting is where the edge cases are.

The algorithm

  1. Split on explicit separators: underscores, hyphens, dots and spaces.
  2. Split on case boundaries: between a lowercase letter or digit and an uppercase letter (user|Id), and between a run of capitals and a capital that starts a lowercase word (XML|Http).
  3. Lowercase all words.
  4. Rejoin: capitalize as the target style requires and insert its separator.
function splitWords(name) {
  return name
    .replace(/([a-z0-9])([A-Z])/g, "$1 $2")        // fooBar -> foo Bar
    .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")     // XMLHttp -> XML Http
    .split(/[\s_.-]+/)
    .filter(Boolean)
    .map(w => w.toLowerCase());
}

const cap = w => w[0].toUpperCase() + w.slice(1);

const toSnake  = s => splitWords(s).join("_");
const toKebab  = s => splitWords(s).join("-");
const toConst  = s => splitWords(s).join("_").toUpperCase();
const toPascal = s => splitWords(s).map(cap).join("");
const toCamel  = s => splitWords(s)
  .map((w, i) => (i === 0 ? w : cap(w))).join("");

Worked examples

InputWordssnake_casecamelCasekebab-case
userProfileIduser, profile, iduser_profile_iduserProfileIduser-profile-id
background-colorbackground, colorbackground_colorbackgroundColorbackground-color
MAX_RETRY_COUNTmax, retry, countmax_retry_countmaxRetryCountmax-retry-count
XMLHttpRequestxml, http, requestxml_http_requestxmlHttpRequestxml-http-request
HTTPServerhttp, serverhttp_serverhttpServerhttp-server
userIDuser, iduser_iduserIduser-id

Tricky inputs

  • Round trips are lossy. userID becomes user_id and comes back as userId. Acronym capitalization is not stored in snake_case, so it cannot be restored.
  • Brand-style capitals. The splitter above turns iOS into "i, os" (i_os) and IPv6Address into "i, pv6, address". Names like these need an exception list or manual review.
  • Digits. With the rules above parseHTML5Doc splits as "parse, html5, doc". Other tools put digits in their own word (html_5). Check what your tool does before a bulk rename.
  • Leading and trailing underscores. _private and __init__ lose their underscores in a naive split. Strip and re-attach them, or skip such names.

Mapping names between layers

A typical web application uses three or four styles at once, and that is normal. Each layer follows its own convention and a serializer converts at the boundary.

LayerStyleExample
Database columnsnake_casecreated_at
JSON API fieldcamelCase (or snake_case, by API policy)createdAt
Frontend JavaScriptcamelCaseorder.createdAt
URL pathkebab-case/order-items/42
Environment variableSCREAMING_SNAKE_CASEORDER_API_URL

Most serialization libraries and object-relational mappers handle this in one of two ways: a global naming policy that converts every field with the split-and-rejoin algorithm, or a per-field override where you state the external name explicitly. Practical advice:

  • Convert in exactly one place per boundary, not ad hoc in each handler.
  • Use explicit per-field names for anything with acronyms or digits, since automatic conversion is lossy there.
  • Convert keys, never values. A blanket key transformer applied to user-supplied maps (for example a dictionary keyed by product codes) will corrupt data.

Case sensitivity pitfalls

Names that differ only by case are distinct in some systems and identical in others, and bugs appear where the two kinds meet.

File systems

The MySQL manual summarizes the usual situation: file names are not case-sensitive on Windows, are case-sensitive on most Unix systems, and on macOS the default file system is not case-sensitive. An import of ./UserService that resolves to userService.js on a developer laptop can therefore fail on a Linux build server. Keep file names in one lowercase style and make import paths match exactly.

SQL: PostgreSQL vs MySQL

In PostgreSQL, key words and unquoted identifiers are case-insensitive, and unquoted names are folded to lower case. Quoting an identifier makes it case-sensitive. The manual's example: FOO, foo and "foo" are the same identifier, but "Foo" and "FOO" are different from those and from each other. It also notes that the SQL standard folds unquoted names to upper case instead, and advises to always quote a particular name or never quote it.

CREATE TABLE orders ("createdAt" timestamp);

SELECT createdAt FROM orders;     -- error: column "createdat" does not exist
SELECT "createdAt" FROM orders;   -- works, but must be quoted forever

In MySQL, column, index and stored routine names are not case-sensitive on any platform, but database and table names correspond to directories and files, so their case sensitivity depends on the operating system and the lower_case_table_names setting. The manual recommends always creating and referring to databases and tables using lowercase names for portability. Both databases point to the same conclusion: lowercase snake_case identifiers avoid the whole problem.

HTTP headers and URLs

RFC 9110 states that HTTP field names are case-insensitive, so Content-Type, content-type and CONTENT-TYPE are the same header. Never compare header names with a case-sensitive string match. For URIs, the same RFC says the scheme and host are case-insensitive while all other components are compared case-sensitively, which is why /Docs and /docs can be different pages.

Enforcing conventions and renaming safely

Conventions checked by a linter in continuous integration hold; conventions that are only written down drift.

  • ESLint camelcase flags underscores inside identifiers, while allowing leading and trailing underscores and all-caps constants. Options such as properties and ignoreDestructuring help when you consume snake_case API data.
  • typescript-eslint naming-convention lets you assign formats (camelCase, PascalCase, snake_case, UPPER_CASE and strict variants) to selectors such as variables, classes or interfaces.
  • Pylint invalid-name (C0103) reports Python names that do not match the expected style for their kind.
  • Stylelint selector-class-pattern checks CSS class names against a regular expression you supply, which is how you enforce kebab-case classes.

When you rename existing code to fix a convention:

  1. Use your editor's or language server's rename refactoring rather than search and replace, so only real references change.
  2. Search for the old name as a string too: templates, configuration, SQL and documentation are not covered by code-aware renames.
  3. Treat anything that crosses a boundary (JSON fields, database columns, environment variables, URLs, public functions) as a breaking change. Add the new name, support both for a while, then remove the old one.
  4. For a file rename that only changes case, check the result on a case-sensitive system before merging.

Common mistakes

MistakeWhy it hurtsFix
Carrying one language's style into another (camelCase functions in Python, snake_case methods in Java)Code clashes with the standard library and every dependencyFollow the target language's guide
camelCase column names in PostgreSQLFolded to lower case unless quoted everywheresnake_case columns, map in the data layer
Underscores in URL slugsGoes against Google's recommendation to separate words with hyphensLowercase kebab-case slugs
Mixed userID, userId and user_id in one APIClients must special-case every fieldOne policy per API, enforced by a serializer
Comparing HTTP header names case-sensitivelyHeader names are case-insensitiveNormalize to lowercase before comparing

Cheat sheet

You are naming a...UseExample
JavaScript or TypeScript variable or functioncamelCasefetchUserOrders
Class or type (most languages)PascalCaseOrderRepository
Python, Ruby or Rust function or variablesnake_casefetch_user_orders
Constant (Python, Java, JavaScript, Rust)SCREAMING_SNAKE_CASEMAX_RETRY_COUNT
Environment variableSCREAMING_SNAKE_CASEDATABASE_URL
SQL table or columnsnake_caseorder_items.created_at
CSS class or custom propertykebab-case.order-item, --brand-color
HTML data attributekebab-case (camelCase in dataset)data-order-id
URL sluglowercase kebab-case/blog/case-styles-guide

FAQ

What is the difference between camelCase and PascalCase?

Only the first letter. camelCase starts lower case (orderItem); PascalCase starts upper case (OrderItem). PascalCase is also called UpperCamelCase, and most languages reserve it for classes and types.

Should I use camelCase or snake_case in Python?

snake_case for functions, methods and variables, CapWords for classes and upper case with underscores for constants, as PEP 8 specifies.

Should JSON keys be camelCase or snake_case?

JSON itself does not care. The JSON:API recommendations suggest camelCase, which also matches JavaScript clients; plenty of APIs use snake_case. Pick one for the whole API and convert at the serializer.

Why can't I use kebab-case for variable names?

Because the hyphen is the subtraction operator in most languages, so user-name is parsed as user minus name. Kebab-case works only where names are not part of expression syntax, such as CSS, HTML attributes and URLs.

Is it userId or userID?

It depends on the guide. Google's Java, JavaScript and TypeScript guides and the .NET guidelines give userId. Whichever you choose, apply it everywhere; the inconsistency costs more than either option.

Are hyphens or underscores better in URLs?

Hyphens. Google Search Central recommends hyphens rather than underscores to separate words in URLs.

How do I convert camelCase to snake_case?

Insert a separator at each boundary between a lowercase letter or digit and an uppercase letter, handle runs of capitals so that XMLHttp becomes "XML Http", then lowercase and join with underscores. userProfileId becomes user_profile_id.

Why does my camelCase column not work in PostgreSQL?

PostgreSQL folds unquoted identifiers to lower case, so createdAt is looked up as createdat. You must either quote the name in every query or, better, name the column created_at.

Conclusion

Case styles are conventions with consequences: visibility in Go, identifier folding in SQL, distinct URLs, broken imports on case-sensitive file systems. Follow the official guide for each language, use kebab-case for the web platform and URLs, snake_case for SQL and upper snake case for environment variables, and convert at layer boundaries in one automated place. For one-off renames a case converter tool does the mechanical part; it sits alongside other free developer tools.

Sources referenced in this guide

Try the tools

P
Written by PAYATE Team

The PAYATE team builds free tools for merchants and developers.

More PAYATE tools