Why Naming Conventions Matter
Naming conventions do more than make code look consistent. They carry semantic information — a SCREAMING_SNAKE_CASE variable signals a constant, a PascalCase identifier signals a class or type, and a leading underscore hints at something private. When the entire team follows the same convention, you can infer meaning from shape alone without reading documentation or jumping to the definition.
Inconsistent naming creates cognitive friction. If half your codebase uses camelCase for functions and the other half uses snake_case, every reader has to actively parse each identifier instead of relying on pattern recognition. Studies on code readability consistently show that consistent style — regardless of which style — reduces the time developers need to understand unfamiliar code by 15-20%.
Beyond readability, naming conventions serve as boundaries between systems. When you see snake_case in a JavaScript project, it usually means the data came from a Python API or a PostgreSQL database. When you see kebab-case, it is probably a CSS class name or a URL segment. These visual cues help developers locate the origin of data as it flows through a system without tracing every variable.
Automated tooling depends on conventions too. Linters (ESLint, Pylint, RuboCop) enforce naming rules. Code generators transform between formats — an OpenAPI schema with snake_case fields generates camelCase TypeScript interfaces. ORMs map between database column naming (typically snake_case) and application object naming (typically camelCase or PascalCase). If your naming is inconsistent, these tools either break or produce confusing output.
camelCase and PascalCase
camelCase (also called lowerCamelCase) starts with a lowercase letter and capitalizes the first letter of each subsequent word: getUserName, isValid, httpRequest. It is the dominant convention in JavaScript, TypeScript, Java, and C# for local variables, function names, and object properties. The name comes from the bumpy shape of the capital letters in the middle — like a camel's humps.
PascalCase (also called UpperCamelCase) follows the same rule but capitalizes the first letter too: UserAccount, HttpClient, EventEmitter. In most languages, PascalCase signals a class, interface, type, or constructor. This distinction is enforced by convention rather than syntax — JavaScript does not care whether your function starts with upper or lowercase, but other developers will assume a PascalCase function is meant to be called with new.
The choice between camelCase and PascalCase usually maps to the distinction between instances and types. A class is PascalCase (UserService), and an instance of that class is camelCase (userService). A React component is PascalCase (SearchBar), but its props object is camelCase (searchBar). TypeScript interfaces are PascalCase (RequestOptions), but the objects implementing them are camelCase (requestOptions). This consistency makes the relationship between types and values immediately visible.
One subtle variation: some codebases prefix interfaces with "I" (IUserRepository) or suffix types with "Type" (ResponseType). The TypeScript handbook explicitly advises against the "I" prefix — it is a holdover from C# and Hungarian notation that adds noise without value in a structurally-typed language. Modern TypeScript prefers just the PascalCase name (UserRepository) and relies on context (import position, usage) to distinguish interfaces from classes.
snake_case and SCREAMING_SNAKE_CASE
snake_case uses lowercase letters with underscores separating words: user_name, get_active_users, http_response_code. It is the standard in Python (PEP 8), Ruby, Rust, and most database systems (PostgreSQL column names, SQL keywords aside). It is also the conventional format for environment variables, configuration keys, and file names in many Unix-oriented projects.
SCREAMING_SNAKE_CASE (also called UPPER_SNAKE_CASE or CONSTANT_CASE) is snake_case but fully capitalized: MAX_RETRIES, API_BASE_URL, DEFAULT_TIMEOUT. This signals a constant — a value defined once and never reassigned. In JavaScript, it is used for module-level constants (const MAX_RETRIES = 3). In Java, it marks static final fields. In C, it identifies preprocessor macros (#define BUFFER_SIZE 1024). The visual loudness is intentional: it shouts "do not change this."
snake_case has a readability advantage in one specific scenario: long identifiers with many words. Compare getHTTPResponseStatusCode (camelCase — where does "HTTP" end and "Response" begin?) vs get_http_response_status_code (snake_case — each word is clearly separated). The underscore acts as a visual space without actually being a space. This is one reason Python, which prioritizes readability, chose snake_case as its convention.
The tradeoff is character count. snake_case identifiers are longer than camelCase equivalents because each underscore adds a character (user_account_manager vs userAccountManager — 20 chars vs 18). In languages with a tradition of short lines (Python's 79/88-character limit), this matters. But modern editors with horizontal scrolling and wider monitors have made this concern less pressing than it was in the 80-column terminal era.
kebab-case and Its Uses
kebab-case uses lowercase letters with hyphens between words: user-profile, nav-bar, date-picker. It is called kebab-case because the words look like they are skewered on the hyphen. You will find it in CSS class names (.main-header), HTML attributes (data-user-id), URL segments (/api/user-profiles), npm package names (express-validator), and Git branch names (feature/add-user-auth).
The key limitation of kebab-case: it cannot be used as an identifier in most programming languages. In JavaScript, my-variable is interpreted as my minus variable. In Python, it is the same subtraction operation. This is why kebab-case lives in configuration-adjacent spaces (CSS, HTML, URLs, filenames, package names) rather than in code. When a kebab-case value enters code, it needs quoting (object["my-key"]) or conversion to camelCase.
CSS has universally adopted kebab-case, and fighting it is pointless. CSS custom properties (--main-bg-color), class names (.card-header), animation names (@keyframes slide-in), and utility frameworks like Tailwind (text-sm, bg-blue-500) all use kebab-case. BEM methodology extends this with double underscores and double hyphens for structure: block__element--modifier (card__title--highlighted).
URLs should use kebab-case for human-readable path segments. Search engines treat hyphens as word separators but treat underscores as joiners. The URL /random-number-generator is parsed as three separate words for search indexing, while /random_number_generator might be treated as one compound term. This is a minor SEO factor but has been confirmed by Google developers. For programmatic API paths, this matters less, but consistency still helps.
Language and Framework Conventions
JavaScript and TypeScript: camelCase for variables and functions, PascalCase for classes and components, SCREAMING_SNAKE for constants, kebab-case for file names in some projects (Angular uses kebab-case files: user-profile.component.ts). React components must be PascalCase for JSX to distinguish them from HTML elements (<MyButton> vs <button>). Node.js APIs use camelCase (fs.readFile, http.createServer).
Python: snake_case for almost everything — functions, variables, module names, method names (PEP 8). PascalCase for classes only. SCREAMING_SNAKE for module-level constants. Leading underscore (_private_method) signals "internal use" by convention. Double leading underscore (__mangled) triggers name mangling in classes. Dunder methods (__init__, __str__) are reserved for Python's data model.
Go: MixedCaps (PascalCase or camelCase) with no underscores. Exported identifiers start with uppercase (UserService is public, userService is package-private). This is enforced by the compiler, not just convention — visibility is determined by the first letter's case. Acronyms are all-caps: ServeHTTP, XMLParser, ID (not Id). Go's standard library is the style guide.
CSS uses kebab-case exclusively. Java uses camelCase for methods and variables, PascalCase for classes, SCREAMING_SNAKE for constants. Rust uses snake_case for functions and variables, PascalCase for types and traits, SCREAMING_SNAKE for constants. C# uses PascalCase for public members (including methods, unlike Java) and camelCase for private fields (often with leading underscore: _privateField). Each convention is documented in the language's official style guide.
Automated Conversion Pitfalls
The hardest problem in case conversion is acronyms. How do you convert XMLParser to snake_case? A naive algorithm that splits on uppercase transitions produces x_m_l_parser — wrong. A smarter algorithm that recognizes uppercase runs produces xml_parser — correct. But what about iPhone? Split on uppercase gives i_phone, which loses the brand-specific capitalization. There is no universal solution; every converter makes assumptions.
Going the other direction is equally ambiguous. Converting xml_parser to camelCase: is it xmlParser or XMLParser or XmlParser? The answer depends on whether you treat "xml" as an acronym (XML) or a regular word (Xml). Go says XMLParser, Java style guides say XmlParser, and JavaScript has no official stance (you see both). A good converter lets you configure acronym handling rather than assuming one style.
Multi-word acronyms create compound problems. Convert HTTPSConnection to snake_case: https_connection or h_t_t_p_s_connection? What about OAuth2Token: oauth2_token or o_auth2_token? Numbers adjacent to letters add another edge case: base64Encode becomes base64_encode (keeping the number attached) or base_64_encode (splitting the number)? Real-world converters handle these differently, and round-tripping (camel → snake → camel) often does not return the original.
Unicode and locale add more complexity. German has ß (eszett) which uppercases to SS. Turkish has dotted and dotless i (İ/i vs I/ı) — toLower("I") produces different results in Turkish locale vs English. If your converter calls toUpperCase() or toLowerCase() without specifying a locale, it may produce wrong results in non-English environments. Most case conversion libraries work on ASCII only and document this limitation explicitly.
// Converting between naming conventions
// camelCase → snake_case
function camelToSnake(str: string): string {
return str
// Insert underscore before uppercase letters that follow lowercase
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
// Insert underscore between consecutive uppercase and the rest
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
.toLowerCase();
}
camelToSnake('getUserName'); // 'get_user_name' ✓
camelToSnake('XMLParser'); // 'xml_parser' ✓
camelToSnake('iPhone'); // 'i_phone' — debatable
camelToSnake('getHTTPSUrl'); // 'get_https_url' ✓
camelToSnake('base64Encode'); // 'base64_encode' ✓
// snake_case → camelCase
function snakeToCamel(str: string): string {
return str.replace(/_([a-z0-9])/g, (_, char) => char.toUpperCase());
}
snakeToCamel('get_user_name'); // 'getUserName' ✓
snakeToCamel('xml_parser'); // 'xmlParser' — not XMLParser
snakeToCamel('base64_encode'); // 'base64Encode' ✓
// kebab-case → camelCase
function kebabToCamel(str: string): string {
return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
}
// PascalCase → kebab-case (useful for component → file name)
function pascalToKebab(str: string): string {
return str
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.toLowerCase();
}
pascalToKebab('UserProfile'); // 'user-profile' ✓
pascalToKebab('HTTPClient'); // 'http-client' ✓Best Practices for Teams
Pick one convention per context and enforce it with tooling, not willpower. ESLint's @typescript-eslint/naming-convention rule lets you specify patterns for variables, functions, classes, interfaces, type aliases, and more. Pylint checks PEP 8 naming by default. RuboCop enforces Ruby's conventions. Automated enforcement prevents style debates in code review — the linter is the arbiter, not individual taste.
Document your exceptions. Every project has a few — maybe you use SCREAMING_SNAKE for environment variable references even in JavaScript (const DATABASE_URL = process.env.DATABASE_URL), or you allow acronyms as all-caps in camelCase (parseXML not parseXml). Write these exceptions in a CONTRIBUTING.md or .editorconfig comment so new team members do not "fix" them in their first PR.
When converting between systems (API layer, database ORM, serialization), pick a single conversion boundary. A common pattern: the database uses snake_case (PostgreSQL convention), the API serializes as camelCase (JSON convention), and the frontend uses camelCase throughout. The conversion happens once in the API serialization layer (e.g., a middleware that transforms response keys). Do not scatter conversion logic across the codebase — that leads to inconsistent transforms and bugs.
For new projects, follow the dominant convention of your language ecosystem. Fighting the ecosystem creates friction with every library, tool, and tutorial you encounter. If you are writing Python, use snake_case even if your team comes from Java. If you are writing Go, accept MixedCaps even if you prefer underscores. The productivity gain from aligning with the ecosystem — consistent autocomplete, readable stack traces, compatible generated code — outweighs any personal preference.