Toolium

Naming Cases: The Politics of camelCase vs snake_case

Hemanth Gedda6 min read

The naming convention argument is the closest thing programmers have to a religious war that nobody actually wins. JavaScript camelCases. Python snake_cases. CSS uses kebab-case. SQL is usually snake_case but uppercase is also common. Each language picked its convention partly for technical reasons (kebab-case would be ambiguous with subtraction in most languages, for example) and partly because the original designers had aesthetic preferences. There is no objectively right answer, but there are technical edge cases where converting between conventions goes wrong, and that is what this post is about.

The five conventions you actually need to know

  • camelCase. First word lowercase, subsequent words capitalized: getUserName. JavaScript, Java, TypeScript variables and functions.
  • PascalCase. Every word capitalized: UserProfile. Class and type names in most languages, React component names.
  • snake_case. All lowercase, words separated by underscores: get_user_name. Python, Ruby, Rust, SQL.
  • kebab-case. All lowercase, words separated by hyphens: user-profile. CSS class names, HTML attributes, URL slugs, CLI flags. Often called "dash-case" or "lisp-case."
  • SCREAMING_SNAKE_CASE. All uppercase with underscores: MAX_RETRY_COUNT. Constants in most languages, environment variables in shell scripts.

Outside these five, there is dotted.case (config files, some object access patterns), Train-Case (HTTP headers like Content-Type), and lowercase nospaces (some legacy systems). They show up less often.

The XMLHttpRequest boundary problem

This is the bug that ate the first version of a case converter I built. The user pasted "XMLHttpRequest" expecting it to convert to "xml_http_request" in snake_case. The converter produced "xmlhttp_request." It also produced "xml-http-request" for kebab-case, which was equally wrong - the expected output for the XMLHttpRequest case is "xml-http-request" (three pieces, not two).

The cause: my original word-boundary detector looked for transitions from lowercase to uppercase ("camelCase" → "camel" + "Case"). That works for camelCase. It does not work for runs of consecutive uppercase letters followed by a lowercase letter ("XMLHttp" - the boundary is between the second M and the H, not between the X and the M).

The fix is to use a more sophisticated splitter that recognizes three boundary signals: lowercase-to-uppercase (camelCase boundary), uppercase-followed-by-uppercase-then-lowercase (XMLHttp boundary), and any non-letter character (separator). The Toolium converter now handles this correctly. XMLHttpRequest becomes "xml_http_request" in snake_case, "xmlHttpRequest" in camelCase, "XmlHttpRequest" in PascalCase. Each is what you would expect.

Why each language picked what it picked

The shortened history:

  • C used snake_case. Standard library functions like strncpy and printf are mostly all-lowercase, but C's standard library is small and predates strong conventions. C++ picked up snake_case for the standard library (std::vector, std::shared_ptr).
  • Smalltalk used camelCase. When Java was designed in the 1990s, the designers explicitly modeled after Smalltalk's conventions.
  • Java's camelCase spread to JavaScript when Netscape designed JavaScript to look familiar to Java programmers (even though the language is structurally totally different).
  • Python's snake_case was a deliberate aesthetic choice by Guido van Rossum, who found camelCase visually ugly. The PEP 8 style guide codifies it.
  • CSS's kebab-case is partly because hyphens look like word boundaries in natural language and partly because property names interact with selectors in ways where underscore would have been ambiguous.
  • HTML attributes use kebab-case because they predate the JavaScript ecosystem - onclick is lowercase no-spaces, but newer attributes like data-user-id use kebab-case.

The boundary problem in cross-language code

If you build a web app where the database is Postgres (snake_case), the API is JSON (often snake_case or camelCase depending on convention), the JavaScript code is camelCase, and the CSS is kebab-case, you have multiple convention boundaries to manage.

The common approaches:

  1. Translate at the boundary. The API returns snake_case JSON; the JavaScript SDK converts to camelCase before exposing it to the application code. Then when sending back to the API, the SDK converts back to snake_case. Most modern API client libraries do this automatically.
  2. Pick one convention and stick with it everywhere. Some codebases use snake_case in JavaScript to match the database. This violates JavaScript conventions but eliminates the translation step. Rails apps used to do this.
  3. Use camelCase for the database too. Postgres allows quoted column names with any case, including camelCase. This is rare because it makes raw SQL ugly, but it works.

Whatever you pick, be consistent. The cognitive cost of mixed conventions in the same file is real.

The "what about Unicode" question

Case conversion in non-Latin scripts is its own deep topic. Turkish has a dotted and dotless 'i' that uppercase to different characters than English 'i'. German eszett (ß) uppercases to "SS" or to the uppercase eszett (Ⱥ), depending on style. Greek has multiple uppercase forms for sigma.

The Toolium converter uses toLocaleUpperCase and toLocaleLowerCase, which respect your browser's language setting. For most users this works correctly. If you are doing case conversion of non-English identifiers (which is rare in code but happens in user-facing content), be aware that the behavior depends on locale.

Which to use, by context

The defaults I follow:

  • JavaScript/TypeScript code: camelCase for variables and functions, PascalCase for classes and types/interfaces, SCREAMING_SNAKE for constants.
  • React components: PascalCase for component names, camelCase for prop names. (Files containing React components are usually PascalCase too: UserCard.tsx.)
  • Python code: snake_case for everything except classes (PascalCase) and constants (SCREAMING_SNAKE).
  • CSS classes: kebab-case. Or BEM (block__element--modifier) if you are doing serious CSS architecture.
  • URLs: kebab-case for path segments and slugs.
  • Environment variables: SCREAMING_SNAKE_CASE.
  • JSON keys in API responses: snake_case is more common in older APIs; camelCase in newer ones. Pick one and document it.