Camel case writes a phrase without spaces and marks each new word with a capital letter: userLoginCount. PEP 8 explains the name in one line: “CamelCase – so named because of the bumpy look of its letters” (PEP 8, Naming Styles). The idea is simple. The trouble starts with acronyms such as HTTP and ID, with digits such as the 64 in base64, and with converting a name from one style to another. Style guides do not agree on these cases, and converters guess.

This guide covers the two kinds of camel case, the other cases you will meet next to them, what five official style guides require, and how a converter splits an existing identifier. Every converter output below comes from the Text Case Converter on this site, and the library outputs were run on 2026-10-02.

camelCase and PascalCase

There are two variants, and the only difference is the first letter.

NameAlso calledFirst letterExample
camelCaselowerCamelCase, mixedCase (PEP 8)lower caseparseResponseBody
PascalCaseUpperCamelCase, CapWords (PEP 8)upper caseParseResponseBody

“Camel case” on its own usually means the lower variant, but not always: PEP 8 uses CamelCase as another name for CapWords, which starts with a capital. When a team document says “use camel case”, check which one it means.

Both forms rely on letter case to show word boundaries. That works for English words written in lower case. It fails as soon as a word is already written in capitals (HTTP, ID), contains a capital in the middle (iOS, YouTube), or ends in a digit (base64). The rest of this guide is mostly about those three cases.

The Other Cases Next to It

Most codebases mix camel case with three or four other styles. The same three words in each:

Styleuser login count becomesTypical use
camelCaseuserLoginCountJavaScript and Java variables and methods, most JSON APIs
PascalCaseUserLoginCountClasses and types in almost every language, .NET methods
snake_caseuser_login_countPython and Rust functions, SQL columns
CONSTANT_CASEUSER_LOGIN_COUNTConstants, environment variables
kebab-caseuser-login-countCSS properties, HTML attributes, URL slugs, CLI flags

kebab-case cannot be used for identifiers in most languages because - is the minus operator, so it lives in places that are not code: CSS, HTML, file names and URLs.

What Each Style Guide Requires

The table below lists only what each official guide states. Where a guide says nothing, the cell says so.

GuideVariables and functionsTypes and classesConstantsSource
Python (PEP 8)lower_case_with_underscores; mixedCase “only in contexts where that’s already the prevailing style”CapWordsMAX_OVERFLOW stylePEP 8 Naming Conventions
GoMixedCaps or mixedCaps, never underscores; an upper-case first letter exports the nameMixedCapsSame rule as other namesEffective Go: Names, MixedCaps
.NETPascalCase for public members; camelCase “used only for parameter names”PascalCasePascalCaseFramework Design Guidelines: Capitalization
Google JavaScriptlowerCamelCaseUpperCamelCaseCONSTANT_CASEGoogle JavaScript Style Guide §6.2
Rustsnake_caseUpperCamelCaseSCREAMING_SNAKE_CASERust API Guidelines C-CASE

Two details are easy to miss. In Go, case is not only style: “the visibility of a name outside a package is determined by whether its first character is upper case” (Effective Go). Renaming parseConfig to ParseConfig makes it public. In .NET, camelCase is reserved for parameters, so a public method getUser is wrong even though it is valid C#.

Acronyms: Where the Guides Disagree

The guides split into two camps. One keeps acronyms in capitals, the other treats an acronym as an ordinary word.

GuideRuleTheir example
PEP 8”capitalize all the letters of the acronym”HTTPServerError, not HttpServerError
Go Code Review CommentsInitialisms keep a consistent caseServeHTTP, not ServeHttp; appID, not appId
.NETTwo-letter acronyms stay upper case; longer ones capitalize only the first letter; Id and Ok are wordsIOStream, HtmlTag, Id
Google JavaScriptLowercase everything, including acronyms, then capitalize the first letter of each wordxmlHttpRequest, newCustomerId
RustAcronyms count as one wordUuid, not UUID; Stdin, not StdIn

Sources: PEP 8, Go Code Review Comments: Initialisms, .NET capitalization, Google JavaScript Style Guide §6.3, Rust API Guidelines.

A converter has to pick one camp. The Text Case Converter follows the Google and Rust camp, the same as lodash and change-case: an acronym becomes an ordinary capitalized word.

InputConverter’s PascalCase or camelCaseWhat the guide for that name wants
HTTPServerErrorHttpServerErrorPEP 8: HTTPServerError
ServeHTTPServeHttpGo: ServeHTTP
appIDappIdGo: appID; .NET and Google: appId
IOStreamIoStream (camelCase ioStream).NET: IOStream, camelCase ioStream

For Python and Go code, fix acronyms by hand after converting. The snake_case results are fine in every camp: http_server_error and app_id are what PEP 8 itself would write for a function or variable.

Digits Inside Identifiers

Digits have no case, so a converter has to decide whether 64 in base64Encode is part of base or a word of its own. Libraries disagree, and the difference only shows in the separated styles:

InputText Case Converterlodash 4.18.1 snakeCasechange-case 5.4.4 snakeCase (default)
base64Encodebase_64_encodebase_64_encodebase64_encode
html5Parserhtml_5_parserhtml_5_parserhtml5_parser
item3ditem_3_ditem_3_ditem3d

The converter separates digits from letters, which is what lodash does and what change-case does with its separateNumbers option. Neither choice is wrong, but it matters when you convert in both directions: item3d comes back from snake_case as item3D, with a capital D that was never there. Rust’s guide has a rule for this case: in snake_case “a word should never consist of a single letter unless it is the last word”, so btree_map rather than b_tree_map. A mechanical converter gives b_tree_map for BTreeMap, so names like that need a manual check.

How a Converter Splits an Existing Identifier

Converting from prose is easy because spaces mark the words. Converting an existing identifier means guessing the words from letter case. The Text Case Converter uses the same rules as change-case’s split with separateNumbers:

  1. Remove apostrophes, so don't stays one word.
  2. Break between a lower-case letter or digit and an upper-case letter: userLogin → user, Login.
  3. Break before the last capital of a run of capitals that is followed by a lower-case letter: XMLHttp → XML, Http.
  4. Break between letters and digits in both directions.
  5. Treat every run of spaces, punctuation and symbols as a separator.

These rules work for ordinary names and fail on brand names that mix case on purpose. Google’s style guide lists five phrases with their correct lowerCamelCase forms, which makes a good test:

Prose (Google §6.3)Google’s lowerCamelCaseText Case Converter
XML HTTP requestxmlHttpRequestxmlHttpRequest
new customer IDnewCustomerIdnewCustomerId
inner stopwatchinnerStopwatchinnerStopwatch
supports IPv6 on iOS?supportsIpv6OnIossupportsIPv6OnIOs
YouTube importeryouTubeImporteryouTubeImporter

Four of five match. The fourth fails because rule 3 reads IPv6 as I + Pv + 6 and iOS as i + OS; the snake_case output, supports_i_pv_6_on_i_os, shows the split. Google’s algorithm does split words that already look like camel case (its example is AdWords → ad words), but it exempts iOS, which “defies any convention”, and its result treats IPv6 as one word. A converter cannot know which brand names are exceptions. Spaces and lower case in the input solve it: typing supports ipv6 on ios gives supportsIpv6OnIos.

Round Trips Lose Information

Converting camelCase to snake_case and back is not lossless. Case is information, and snake_case throws it away:

HTTPServerError  → http_server_error → HttpServerError
appID            → app_id            → appId
item3d           → item_3_d          → item3D

This matters most for serializers that convert names automatically, for example a Python backend that stores snake_case fields and sends camelCase JSON. A field named user_id becomes userId, and the client sends userId back, so the round trip works. But if the API contract says userID, no automatic rule recovers it from user_id. Keep an explicit alias for every field whose name contains an acronym or a digit, and test the mapping in both directions.

Writing the Conversion Yourself

The widely copied one-line regex for camelCase → snake_case breaks on acronyms. This Python snippet compares it with a two-step version; the outputs in the comment are what Python 3.12 prints:

import re

def naive(name):
    return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()

def two_step(name):
    s = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', name)  # XMLHttp → XML_Http
    return re.sub(r'([a-z\d])([A-Z])', r'\1_\2', s).lower()  # userId → user_Id

for name in ['HTTPServerError', 'getHTTPResponseCode', 'base64Encode']:
    print(naive(name), two_step(name))
# h_t_t_p_server_error http_server_error
# get_h_t_t_p_response_code get_http_response_code
# base64_encode base64_encode

The two-step version handles acronyms but keeps digits attached, like change-case’s default. In JavaScript, the converter’s rules fit in a few lines and need no library; this is the same splitting the tool uses, without the accent and apostrophe handling:

function words(s) {
  return s
    .replace(/([a-z\d])([A-Z])/g, '$1 $2')       // userLogin → user Login
    .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')    // XMLHttp → XML Http
    .replace(/([A-Za-z])(\d)/g, '$1 $2')         // base64 → base 64
    .replace(/(\d)([A-Za-z])/g, '$1 $2')
    .split(/[^A-Za-z\d]+/)
    .filter(Boolean);
}
const cap = (w) => w[0].toUpperCase() + w.slice(1).toLowerCase();
const snake = (s) => words(s).join('_').toLowerCase();
const camel = (s) => words(s).map((w, i) => (i ? cap(w) : w.toLowerCase())).join('');
const kebab = (s) => words(s).join('-').toLowerCase();

console.log(snake('HTTPServerError')); // http_server_error
console.log(camel('XML HTTP request')); // xmlHttpRequest
console.log(kebab('base64Encode'));     // base-64-encode

If you already depend on lodash, _.snakeCase and _.camelCase give the same results as the converter for every input in this article except Müller's algorithm: lodash removes the accent (mullersAlgorithm), while the converter keeps it (müllersAlgorithm).

A Checklist for Mixed-Convention Projects

  • Write down which camel case your project means, lower or upper, and where each style applies (database, API, code, CSS).
  • Pick one acronym rule per language and follow that language’s guide: capitals in Python class names and Go, one capital in .NET, JavaScript and Rust.
  • Convert from prose with spaces when you can. Splitting an existing identifier is a guess, and brand names (iOS, IPv6, YouTube) are where it fails.
  • Keep explicit aliases for names that contain acronyms or digits instead of trusting an automatic round trip.
  • Check single-letter words (b_tree_map) and digit splits (item_3_d) by hand after a bulk rename.

To convert a single name into all nine styles at once, paste it into the Text Case Converter. For URL slugs, which also need accents removed and symbols dropped, use Slugify instead.