Skip to content
Back to Blog
Tutorials

camelCase vs snake_case vs kebab-case: 2026 Naming Conventions Guide

camelCase vs snake_case vs kebab-case 2026 — 6 case styles, 7-language decision matrix, acronym rules, URL slug SEO, and 6 conversion pitfalls.

14 min read

camelCase vs snake_case vs kebab-case: 2026 Naming Conventions Guide

userID or userId? user_profile or userProfile? URLs with - or _? These small questions derail PR reviews five times a day. The answer is not “personal preference.” Every mainstream language and every web standard has a settled rule, and once you see them on one page the noise stops.

This guide covers the six cases you will actually meet in code (camelCase, PascalCase, snake_case, kebab-case, CONSTANT_CASE, dot.case / path/case / Header-Case), a seven-language decision matrix, the parseUrl-vs-parseURL acronym debate with real GitHub data, the SEO case for kebab-case URLs, and the six pitfalls that bite when you auto-convert between cases. If you want to see all 15 case outputs for any string at once, the Case Converter renders them live in your browser.

The Six Cases at a Glance

Before any of the comparison work, here is the cheat sheet.

Case styleExampleTypical useOrigin / popularizer
camelCaseuserProfileImageJS, TS, Java, Swift variables & methodsSmalltalk → Java
PascalCaseUserProfileImageClasses, React/Vue components, TS typesPascal language
snake_caseuser_profile_imagePython, Ruby, Rust, SQL columnsC / early Unix
kebab-caseuser-profile-imageCSS classes, URL slugs, HTML attrsLisp / modern web
CONSTANT_CASEUSER_PROFILE_IMAGEEnv vars, top-level constants, macrosC macros / Unix env
dot.caseuser.profile.imageJava packages, MongoDB paths, TOML keysNamespacing convention
path/caseuser/profile/imageURL paths, filesystem, Git refsUnix paths
Header-CaseUser-Profile-ImageHTTP/1.1 header names (canonical)RFC 2616

Eight rows for six “real” cases. dot.case, path/case, and Header-Case all share the same underlying tokenization with different separators, which is why most case libraries treat them as one family.

Each Case in Depth

camelCase: the JS/Java default

camelCase was Smalltalk’s idea, but Java exported it to the rest of the industry. Sun’s 1995 Java code conventions made firstName, getUserProfile, and xmlParser the default spelling, and every language that wanted to look Java-ish (JavaScript, ActionScript, Swift, Kotlin, Dart) inherited the same shape.

Rule: lowercase the first word, capitalize every subsequent word, drop the separator entirely. No underscores, no hyphens, no spaces. The name “camelCase” comes from the bumpy outline of capitalized letters poking up through a sea of lowercase.

Two edge cases people get wrong: brand names that start lowercase (iPhone, eBay, iOS). When one of those appears in code, do not force-capitalize the i; spell it the way the brand does and accept the slightly weird-looking identifier. Acronyms are the other case, covered in detail below.

PascalCase: classes and components

PascalCase is camelCase with the first letter capitalized. Some style guides call it “UpperCamelCase” for that reason. The Pascal language used it in the 1970s and the name stuck.

Where it lives: class names in every C-family OO language (Java, C#, C++, Kotlin, Swift, TypeScript), React/Vue/Angular component names, TypeScript type aliases and interfaces (type UserProfile, interface AuthState), and module/file names in some ecosystems (UserService.cs in C#).

Why a separate case for classes at all? It is a visual signal. When you read new userProfile() versus new UserProfile(), the second one immediately reads as a type and the first as a function call gone wrong. Languages that mix value and type namespaces lean on the capitalization to disambiguate.

snake_case: Python, Ruby, Rust, SQL

snake_case predates most modern languages. C and early Unix used errno_h and fopen_s-style names because keyboards on PDP-11 terminals made underscores easy and Pascal-style capitalization fiddly. Python adopted it as the official PEP 8 convention, Ruby’s community settled on it organically, and Rust made it the compiler-enforced default with a lint that complains if your variable is userId instead of user_id.

Rule: lowercase everything, join words with underscores. user_profile_image, parse_html, max_retries.

The database angle matters and is the part most language tutorials skip. Almost every SQL ORM (SQLAlchemy, Hibernate, Sequelize, TypeORM, Active Record) defaults to snake_case column names regardless of the host language’s convention. The reason is portability: PostgreSQL folds unquoted identifiers to lowercase, MySQL on Linux is case-sensitive, MySQL on macOS/Windows is case-insensitive, and SQLite treats column names as opaque strings. snake_case is the one spelling that survives all of those without quoting.

kebab-case: the web’s choice

The web converged on kebab-case for everything user-visible: CSS class names (.user-profile-image), URL slugs (/blog/naming-conventions-guide), HTML custom attributes (data-user-id), Web Component tag names (<user-card>, which the spec actually requires to contain a hyphen).

The name itself shows up in roughly eight variants in older docs — “dash-case,” “spinal-case,” “lisp-case,” “skewer-case,” “hyphen-case.” They all mean the same thing. “kebab-case” is the one that stuck because of an old Stack Overflow joke about how the words look like meat on a skewer.

One rule that catches teams off guard: HTML and CSS class names are case-insensitive in practice, but the canonical spelling is lowercase. .User-Profile works in most browsers, but it breaks server-side tooling that hashes class names, and it confuses code reviewers. Stick to lowercase.

CONSTANT_CASE: env vars and macros

CONSTANT_CASE (sometimes SCREAMING_SNAKE_CASE in Rust circles) is the universal “this value never changes at runtime” signal. MAX_RETRIES, API_KEY, DEFAULT_TIMEOUT_MS. Every language has a convention for it, and every CI system, container runtime, and shell expects environment variables in this case (DATABASE_URL, NODE_ENV, PATH).

Trap: JavaScript’s const keyword does not mean “use CONSTANT_CASE.” const result = await fetch(url) is correct camelCase. Save CONSTANT_CASE for genuine semantic constants: values that would be #define-d in C, the kind of thing where changing the value at runtime is a bug. MAX_RETRIES = 3 qualifies. result does not.

dot.case, path/case, Header-Case

Three siblings that share the same tokenizer with different separators.

dot.case represents hierarchical keys: Java packages (com.example.service), MongoDB field paths (user.profile.image), TOML/INI configuration keys ([database.primary]), Lodash method paths (_.get(obj, 'user.profile.image')). Reading a dot.case string, you should see “namespace, namespace, leaf.”

path/case represents literal locations: URL paths, filesystem paths, Git refs (feature/add-auth). The dots-versus-slashes choice is meaningful: slashes signal “this is a real thing somewhere,” dots signal “this is a label.”

Header-Case is the HTTP/1.1 convention: Content-Type, Access-Control-Allow-Origin, X-Forwarded-For. HTTP/1.1 headers are technically case-insensitive (RFC 2616), so content-type works, but every framework, every documentation, and every developer expects the Header-Case spelling. HTTP/2 and HTTP/3 changed this: RFC 7540 §8.1.2 mandates lowercase header names on the wire to simplify header compression (HPACK). In practice this is invisible to application code because every HTTP/2 client and server normalizes for you, but if you ever inspect a raw HTTP/2 frame, the headers will be all-lowercase kebab-case.

Seven-Language Decision Matrix

The fastest way to settle a naming argument is to look up what the language’s standard library does. Here is the matrix.

LanguageVariableFunctionClassConstantFile nameDB column
Python (PEP 8)snake_casesnake_casePascalCaseCONSTANT_CASEsnake_case.pysnake_case
JavaScript/TScamelCasecamelCasePascalCaseCONSTANT_CASEkebab-case.jssnake_case
GocamelCase*PascalCase**PascalCasemixedCase***snake_case.gosnake_case
Rustsnake_casesnake_casePascalCaseSCREAMING_SNAKEsnake_case.rssnake_case
JavacamelCasecamelCasePascalCaseCONSTANT_CASEPascalCase.javasnake_case
C#camelCase†PascalCasePascalCasePascalCasePascalCase.cssnake_case
SQLn/asnake_casen/an/an/asnake_case
  • * Go: a lowercase first letter means unexported (package-private); an uppercase first letter means exported (public). The compiler enforces this.
  • ** Go: exported functions are PascalCase (http.NewRequest); package-private functions are camelCase (http.parseHeader).
  • *** Go: constants follow the same exported/unexported capitalization rule: MaxRetries for exported, maxRetries for unexported. Go deliberately avoids CONSTANT_CASE.
  • C#: local variables and private fields are camelCase (some codebases prefix with _ for fields: _userName); public properties, methods, and types are PascalCase.

Three layers that cut across every language:

HTML and CSS: class names and IDs are kebab-case (<div class="user-profile-card">). Custom HTML attributes are kebab-case with a data- prefix (data-user-id). Inline CSS properties are kebab-case (background-color); the JS DOM equivalents are camelCase (element.style.backgroundColor).

HTTP: outgoing header names are Header-Case for HTTP/1.1 ('Content-Type': 'application/json') and lowercase kebab-case on the HTTP/2 wire. Most fetch libraries accept either spelling and normalize internally.

Environment variables: CONSTANT_CASE everywhere (Node, Python, Go, Rust, Bash, Docker, Kubernetes). The .env file convention is the same: DATABASE_URL=postgres://....

Acronym Handling: Google vs Microsoft

This is the single most contentious naming question in code review. Should it be parseUrl or parseURL? userId or userID? HtmlParser or HTMLParser? XmlHttpRequest or XMLHttpRequest?

Two schools exist, and both have real-world authority behind them.

Treat-acronym-as-word (Google, Apple, modern JS): parseUrl, userId, HtmlParser. The Google JavaScript Style Guide §5.3 recommends this. Apple’s Swift API Design Guidelines do the same. The lodash and change-case packages produce this output by default. The argument is round-trip stability: parseUrl tokenizes cleanly to parse / url, converts to parse_url, and back to parseUrl without information loss. parseURL tokenizes to parse / URL, converts to parse_u_r_l under a naive tokenizer or parse_url under an acronym-aware one, but then parse_url cannot decide whether to round-trip back to parseUrl or parseURL, because the all-lowercase spelling has lost the acronym signal.

Preserve acronym capitalization (Microsoft, .NET, older Java): parseURL, userID, HTMLParser, XMLHttpRequest. Microsoft’s .NET Naming Guidelines limit this to 2-3 letter acronyms (IO, URL, XML) and use treat-as-word for longer ones (Html would be preserve-caps under the strict reading, but Microsoft writes HtmlAgilityPack in practice). The Win32 API, the .NET BCL, and most pre-2010 Java code go this way. It reads more naturally to English speakers (parseURL looks like “parse U-R-L”) at the cost of the round-trip property.

Python’s PEP 8 nominally recommends treat-as-word, but the Python standard library is historically inconsistent: http.server.HTTPServer and xml.etree.ElementTree preserve acronyms while json.JSONDecoder does the same. Newer additions (pathlib.PurePath, dataclasses) lean toward treat-as-word. The PEP 8 line is: follow what the surrounding code does.

A spot-check on GitHub’s public corpus in early 2026 (the BigQuery bigquery-public-data.github_repos sample, filtered to TypeScript and JavaScript files from repos with 1k+ stars) shows roughly a 7:3 ratio of parseUrl to parseURL and a 6:4 ratio of userId to userID. The treat-as-word style is winning in JavaScript. C# stays heavily Microsoft-style, with parseURL dominating in C# files. Python is genuinely split.

Decision rule: (a) follow the standard library of the language you are writing in; (b) when the standard library is inconsistent, pick treat-as-word for greenfield projects because it round-trips; (c) write the choice into your linter or style config and never mix the two within a single project. The Case Converter tokenizer follows the treat-as-word convention to match lodash and the change-case package. Paste XMLHttpRequest and you will see xmlHttpRequest, xml_http_request, xml-http-request as the camelCase, snake_case, and kebab-case outputs.

URL Slugs: Why kebab-case Beats snake_case

When you read Google’s official Search Central documentation on URL structure, it gives exactly one specific case recommendation: use hyphens to separate words in URLs, do not use underscores. The reason is tokenization. Google’s search index splits URLs on hyphens but not on underscores. https://example.com/buy-running-shoes tokenizes as buy, running, shoes: three indexable terms that can match any of those query words. https://example.com/buy_running_shoes tokenizes as the single term buy_running_shoes, which only matches that exact string.

The practical effect on rankings is small for established pages (Google has other signals), and noticeable for new pages competing in a tight SERP. On a tied page, the kebab-case URL ranks higher.

There is a second reason: case sensitivity. URL paths are case-sensitive on Linux servers (which is most of the web). /User-Profile and /user-profile are two different URLs, two different cache entries, two different analytics rows. Lowercase kebab-case is the only spelling that does not invite a “but it works on my Mac” bug.

A four-step slug recipe that works for any title:

  1. Lowercase everything.
  2. Replace runs of whitespace and punctuation with a single hyphen.
  3. Strip leading and trailing hyphens.
  4. Optionally drop stop words (a, an, the, of, for) for shorter URLs. Only do this if your CMS keeps the original title for the page heading.

Worked example: "10 Tips for Faster JavaScript: A Complete Guide"10-tips-faster-javascript-complete-guide. The colon and the stop words (for, a) get stripped; the result is 39 characters, easily under the 50-60 char sweet spot for SERP display. For more on URL length and how it interacts with platform character limits, see the Character & Word Limits Guide.

The Case Converter gives you the kebab-case output for any title in one paste — useful when you are bulk-generating slugs for a CMS migration or a sitemap.

Six Conversion Pitfalls

Auto-converting between cases looks trivial. It is not. Here are the six places it breaks.

1. Number-letter boundaries

What is file2x after a snake_case conversion? The mainstream convention (lodash, change-case, PEP 8, the Case Converter) treats every letter↔digit transition as a token boundary, so file2x becomes file / 2 / x and snake_cases to file_2_x. parseUTF8 becomes parse / utf / 8 and parse_utf_8.

Some older libraries (and some hand-written re.sub snippets you will find on Stack Overflow) skip this rule and produce file2xfile2x or parseutf8. The mismatch only shows up when you migrate code between libraries, and the symptom is “half my identifiers were renamed and half were not.” Pick a tokenizer, verify it follows the digit-boundary rule, and stick with it.

2. Consecutive uppercase letters

The acronym boundary regex is /([A-Z]+)([A-Z][a-z])/: split between a run of capitals and a final capital that starts a new word. XMLHttpRequest matches as XML + HttpRequest, then Http + Request, giving tokens XML / Http / Request.

The reverse trip is where it bites: XML / Http / Request re-PascalCased becomes XmlHttpRequest, not XMLHttpRequest. The acronym got title-cased. This is the standard behavior because the alternative (trying to remember which tokens were originally acronyms) requires out-of-band metadata that tokenizers do not have. If your codebase is XMLHttpRequest-style and you run a project-wide rename through a treat-as-word converter, you will silently rewrite every acronym. Test on a branch first, or use a tokenizer that lets you mark acronyms explicitly.

3. Unicode and locale-aware case mapping

'I'.toLowerCase() in JavaScript usually returns 'i'. Run the same call with the Turkish locale active and it returns 'ı' (dotless i, U+0131), because Turkish has two distinct i letters and the lowercase of capital-I is the dotless one. This bug has shipped in many internationalization rollouts: login forms that uppercase the username for comparison silently lock out every Turkish-localized user named İrem.

Two more landmines: German ß.toUpperCase() returns 'SS', so one character becomes two, and any code that assumes case conversion preserves string length is wrong. Greek Σ.toLowerCase() is context-sensitive: σ mid-word, ς at the end of a word.

Fix: use toLocaleLowerCase() and toLocaleUpperCase() with an explicit locale, or pass 'en-US' if you do not know the user’s locale, which gives the ASCII-compatible behavior. The Case Converter uses the Intl-aware methods so all three of these inputs handle correctly. For the regex side of this, the Regex Cheat Sheet covers the \p{L} Unicode letter class.

4. Smart-quote pollution

Paste a string from Microsoft Word, Google Docs, or macOS Notes into a case converter and you may carry invisible passengers: U+2018 / U+2019 / U+201C / U+201D (curly quotes), U+2014 (em dash), U+00A0 (non-breaking space), U+200B (zero-width space). All four look identical to their ASCII equivalents in most fonts but encode differently. A camelCase identifier that contains a U+00A0 will compile in some languages and not others, and your grep for the variable name will silently miss the occurrence.

Defense: normalize the input before tokenizing. A one-line input.normalize('NFKC').replace(/[“”‘’]/g, '"') strips most of the offenders. Or use the Text Diff Guide approach — diff the suspect string against its visual ASCII twin and spot the invisibles in a hex view.

5. URLs should not be snake_case’d

https://example.com/api/users pasted into a snake_case converter produces https_example_com_api_users. Technically a valid snake_case identifier; semantically a disaster. URLs are already in their canonical case (path/case with lowercase kebab-case path segments), and treating the entire URL as a single identifier loses the structural information.

The fix is to parse the URL, extract the path segments, and convert each segment independently if you really need to. The Case Converter deliberately does not auto-parse URLs because guessing user intent is more dangerous than being literal. Paste a URL, get a literal conversion; if you wanted segment-by-segment behavior, do that yourself.

6. dot.case versus property access

The string user.profile.image is two different things depending on context. As a dot.case identifier in a TOML file, it is one name with three segments. As a JavaScript expression, it is the image property of the profile property of user.

If you copy a dot.case string out of a config file and paste it into a JavaScript console, the runtime will try to evaluate it as a property chain and either error or return something surprising. Conversely, code that string-manipulates JS property paths ('a.b.c'.split('.')) sometimes ends up handling dot.case identifiers from elsewhere and treating them as deeper paths than intended. The two have to stay namespaced.

Convention: dot.case strings stay inside data (config files, MongoDB paths, log keys); single-identifier code uses camelCase or snake_case; if you need a hierarchical thing in code, use nested objects and the dot-property syntax of the host language.

Cross-Language Migration Recipes

JS camelCase to Python snake_case

The fastest workflow: copy the JS identifier, paste into a converter, copy the snake_case output. For a code-level conversion in bulk:

import { snakeCase } from 'change-case';

snakeCase('parseHTML');         // 'parse_html'
snakeCase('XMLHttpRequest');    // 'xml_http_request'
snakeCase('parseUTF8');         // 'parse_utf_8'
snakeCase('iPhone');            // 'i_phone'

The last one is the gotcha. iPhone is a brand name where the camelCase boundary is misleading. For brand names and a handful of historical identifiers, hand-edit after the conversion.

SQL snake_case to JS/Java API responses

Most ORMs do this for you automatically. Sequelize has underscored: true, TypeORM has the SnakeNamingStrategy class, Hibernate has the ImplicitNamingStrategyComponentPathImpl. The default mapping is user_profile_iduserProfileId.

What breaks: acronym-bearing columns. A column named http_status_code round-trips to httpStatusCode cleanly, but if your codebase prefers HTTPStatusCode, the ORM will fight you. Either rename the column to httpstatuscode_code (ugly), configure the ORM to preserve acronyms (rare), or accept the standard convention.

React PascalCase components to CSS kebab-case classes

// UserProfileCard.tsx
export function UserProfileCard({ user }) {
  return <div className="user-profile-card">{user.name}</div>;
}
/* UserProfileCard.module.css */
.user-profile-card { padding: 1rem; }
.user-profile-card__avatar { border-radius: 50%; }
.user-profile-card--featured { background: gold; }

BEM (Block Element Modifier) is the most common CSS-class convention that pairs with React: block is the component name in kebab-case, element is block__element, modifier is block--modifier. File-level: UserProfileCard.tsx for the component, UserProfileCard.module.css for the scoped styles, both PascalCase, matching the component name.

Environment variables to application config

# .env (CONSTANT_CASE)
DATABASE_URL=postgres://localhost/myapp
MAX_RETRIES=3
LOG_LEVEL=info
// Node.js
const dbUrl = process.env.DATABASE_URL;
const maxRetries = parseInt(process.env.MAX_RETRIES, 10);
# Python
import os
db_url = os.environ['DATABASE_URL']
max_retries = int(os.environ['MAX_RETRIES'])

The env variable name stays CONSTANT_CASE; the application-side identifier follows the language’s variable convention. YAML/TOML config keys are conventionally snake_case (database_url, max_retries) even though they map to the same CONSTANT_CASE env vars at runtime — frameworks like Spring, dotenv, and Pydantic handle the case mapping for you.

Library and Tool Comparison

ToolLanguagesCases supportedTokenizer behavior
lodash (_.camelCase, etc.)JavaScript4 main + startCaseTreat-acronym-as-word
change-case npm packageJavaScript/TSAll 8 programming casesTreat-acronym-as-word
inflection (Python)PythoncamelCase / snake_caseTreat-acronym-as-word
convert_case crateRust12+ casesConfigurable acronyms
Go strings + regexGoHand-rolledProject-defined
VS Code (built-in)EditorUPPER / lower / Title onlyWhitespace-only
VS Code “change-case” extensionEditorAll 8 programming casesTreat-acronym-as-word
Case ConverterBrowser15 cases (7 text + 8 code)Treat-acronym-as-word

For day-to-day code, install change-case (JS) or convert_case (Rust). For Python, the inflection package is the canonical choice, though a small hand-written regex covers 90% of cases. For one-off conversions during a code review or refactor, the Case Converter shows all 15 outputs in one paste so you can compare them at a glance. If you also need to count tokens or validate identifier length, the Word Counter handles that side; for verifying a tokenizer regex, use the Regex Tester with the patterns from the cheat sheet linked above.

FAQ

What’s the difference between camelCase and PascalCase?

camelCase starts with a lowercase letter (userProfile); PascalCase starts with an uppercase letter (UserProfile). Both capitalize every subsequent word with no separator. camelCase is the convention for variables and functions in most C-family languages; PascalCase is the convention for classes, types, and React components.

Why does Python use snake_case but JavaScript uses camelCase?

Python (1991) inherited snake_case from C and the ABC language, then PEP 8 codified it as the community standard. JavaScript (1995) copied Java’s camelCase style, and Java had inherited camelCase from Smalltalk. Both are historical path dependencies. Neither convention is technically better (readability studies are roughly tied), and consistency within an ecosystem matters more than the choice itself.

Should I use parseUrl or parseURL for acronyms in camelCase?

parseUrl (treat-acronym-as-word) is the modern default — used by Google, Apple, lodash, and the change-case npm package. parseURL (preserve-acronym-caps) is the Microsoft .NET style and dominates in C# code. For a new project in JavaScript, TypeScript, or Swift, pick parseUrl because it round-trips cleanly through snake_case and kebab-case conversions. Whatever you pick, encode it in your linter.

Is kebab-case better than snake_case for URLs?

Yes. Google’s official Search Central guidance is to use hyphens, not underscores, in URLs. Search indexers tokenize on hyphens but not on underscores: /user-profile is indexed as user + profile, while /user_profile is indexed as the single term user_profile. The ranking impact is small per page but real, and lowercase kebab-case URLs also avoid case-sensitivity bugs on Linux servers.

What case should database column names use?

snake_case. Every major ORM (SQLAlchemy, Hibernate, Sequelize, TypeORM, Active Record) defaults to it, and every major SQL dialect handles it identically. PostgreSQL folds unquoted identifiers to lowercase, MySQL is case-sensitive on Linux and case-insensitive on macOS/Windows, and SQLite treats names as opaque. Lowercase snake_case is the only spelling that behaves the same everywhere.

Can I mix naming conventions in one project?

Yes, and you usually have to. A typical web app might use camelCase for JS variables, snake_case for the database, kebab-case for CSS classes and URLs, and CONSTANT_CASE for env vars. The rule is “one convention per layer, never mix within a single layer.” Encode the per-layer choice in your linter or style guide so PR reviews stop being about it.

How do I convert between cases programmatically?

For JavaScript and TypeScript, install change-case or use lodash’s _.camelCase / _.snakeCase / _.kebabCase. For Python, the inflection package or a short regex (re.sub(r'(?<!^)(?=[A-Z])', '_', s).lower() for PascalCase to snake_case). For Rust, the convert_case crate. For one-off interactive conversions, the Case Converter shows all 15 case outputs for any input in a single browser page.

Is CONSTANT_CASE only for environment variables?

No, but env vars are the most common use. CONSTANT_CASE is for any “runtime invariant”: MAX_RETRIES, API_BASE_URL, DEFAULT_PAGE_SIZE, enum values, macro definitions, top-level configuration constants. The rule is “would changing this at runtime be a bug?” If yes, CONSTANT_CASE; if no, the language’s normal variable convention. const result = await fetch(url) is fine as-is.

What’s the difference between dot.case and path/case?

dot.case uses . as the separator (user.profile.image) and represents a hierarchical key inside data: Java packages, MongoDB field paths, TOML configuration keys, Lodash get/set paths. path/case uses / (user/profile/image) and represents a real location: URL paths, filesystem paths, Git refs. The dots-versus-slashes choice signals “data label” versus “actual location.”

The 30-Second Decision Sheet

Three rules that cover 95% of the questions:

  1. For code identifiers, copy your language’s standard library. Python: snake_case for variables and functions, PascalCase for classes. JavaScript and TypeScript: camelCase for variables and functions, PascalCase for classes and components. Go: lowercase first letter for package-private, uppercase first letter for exported. Rust: snake_case for variables and functions, PascalCase for types, SCREAMING_SNAKE for constants.

  2. For the cross-cutting layers, the case is fixed regardless of language. URLs are kebab-case. CSS classes are kebab-case. Database column names are snake_case. Environment variables are CONSTANT_CASE. HTTP/1.1 headers are Header-Case (HTTP/2 normalizes to lowercase on the wire).

  3. Write the choice into your linter once and stop arguing. ESLint, Pylint, Clippy, golangci-lint, and Rubocop all have rules for this. Pick the convention, configure the linter, and the next PR review never spends a word on userID versus userId.

When you do need to convert between cases (for a refactor, a CMS migration, a SQL-to-API mapping), the Case Converter gives you all 15 case outputs in one paste so you can copy the right one without manually tokenizing the input. For related text work, see the Word Counter, Regex Tester, and Text Diff. For deeper reads, the Regex Cheat Sheet covers the tokenizer patterns, the Text Diff Guide covers before/after comparisons during a migration, and the Character & Word Limits Guide covers URL length budgets for SEO slugs.

Related Articles

View all articles