I once reviewed a pull request where a single file used camelCase for JavaScript variables, snake_case for API response fields, PascalCase for one React component, and SCREAMING_SNAKE_CASE for a constant that wasn't actually constant. The developer's explanation was "I used whatever felt natural at the time."
Naming conventions exist precisely so you don't have to make that decision at the time. They're a solved problem. The only hard part is picking one per context and sticking to it.
The conventions and where they come from
camelCase: first word lowercase, subsequent words capitalized. getUserName, isActive, totalCount. The standard in JavaScript, Java, and Swift. The name comes from the humps in the middle of the word, like a camel's back.
PascalCase (also called UpperCamelCase): every word capitalized. UserProfile, HttpRequest, DatabaseConnection. Used for class names in most languages, React component names, and C# methods.
snake_case: words separated by underscores, all lowercase. user_name, is_active, total_count. The standard in Python, Ruby, Rust, and PostgreSQL. Arguably the most readable of the common conventions because the underscore provides a clear visual separator.
kebab-case: words separated by hyphens, all lowercase. user-name, is-active, total-count. Used in CSS class names, HTML attributes, URL slugs, and CLI flags. Named after a kebab skewer. Can't be used for variable names in most languages because the hyphen is the subtraction operator.
SCREAMING_SNAKE_CASE (or CONSTANT_CASE): words separated by underscores, all uppercase. MAX_RETRIES, API_BASE_URL, DEFAULT_TIMEOUT. Reserved for constants in most languages. The visual loudness is intentional -- it tells you this value shouldn't change.
dot.case: words separated by dots. user.name, app.config.timeout. Common in configuration files, Java package names, and object property paths.
The language-specific conventions matter
Each language community has converged on conventions for good reasons, and fighting them creates friction.
In JavaScript:
- Variables and functions: camelCase (
let userName) - Classes and constructors: PascalCase (
class UserProfile) - Constants: SCREAMING_SNAKE_CASE (
const MAX_RETRIES = 3) - File names: either camelCase or kebab-case (varies by team)
In Python:
- Variables and functions: snake_case (
user_name) - Classes: PascalCase (
UserProfile) - Constants: SCREAMING_SNAKE_CASE (
MAX_RETRIES) - Modules: snake_case (
user_utils.py)
In CSS:
- Class names: kebab-case (
.user-profile) - CSS custom properties: kebab-case (
--primary-color) - BEM methodology: block_element--modifier (`.card_title--active`)
When your JavaScript API client receives snake_case JSON from a Python backend, you have a boundary. The convention of the consuming language should win. Transform user_name to userName at the API boundary, not throughout your JavaScript code.
The conversion algorithms
Converting between cases programmatically requires two steps: tokenize the input into individual words, then rejoin them in the target format.
Tokenization needs to handle multiple input formats:
function tokenize(str) {
return str
// Insert separator before uppercase letters in camelCase/PascalCase
.replace(/([a-z])([A-Z])/g, '$1 $2')
// Insert separator before sequences of uppercase followed by lowercase
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
// Replace common separators with space
.replace(/[-_.\s]+/g, ' ')
.trim()
.toLowerCase()
.split(' ')
.filter(Boolean);
}
function toCamelCase(str) {
const words = tokenize(str);
return words[0] + words.slice(1).map(
w => w.charAt(0).toUpperCase() + w.slice(1)
).join('');
}
function toSnakeCase(str) {
return tokenize(str).join('_');
}
function toKebabCase(str) {
return tokenize(str).join('-');
}
function toPascalCase(str) {
return tokenize(str).map(
w => w.charAt(0).toUpperCase() + w.slice(1)
).join('');
}
The tricky part is handling acronyms. Is XMLParser two tokens (XML + Parser) or one (XMLParser)? The regex ([A-Z]+)([A-Z][a-z]) handles this by splitting before the last capital in a sequence of capitals: XML + Parser. Most conventions agree that acronyms in camelCase should be treated as words: xmlParser not xMLParser, and HTMLElement becomes htmlElement in camelCase.
The boundary problem in real applications
The most common case conversion headache in modern development is the API boundary. Your database columns are snake_case. Your backend serializes them as-is. Your frontend expects camelCase. You have three options:
Transform at the API layer. Your backend serializes to camelCase before sending responses. This means the frontend gets idiomatic JavaScript conventions, but the backend is now maintaining a mapping between database columns and API fields.
Transform on the client. The frontend receives snake_case and converts on arrival. Libraries like
camelcase-keyshandle nested object transformation. This keeps the backend simple but adds a processing step to every API call.Use one convention everywhere. Some teams use snake_case in their JavaScript because it matches the API and database. This is technically consistent but feels wrong in JavaScript and triggers linter warnings.
There's no universally right answer. Option 2 is the most common in practice. A utility function that recursively transforms object keys handles it cleanly:
function camelizeKeys(obj) {
if (Array.isArray(obj)) return obj.map(camelizeKeys);
if (obj !== null && typeof obj === 'object') {
return Object.fromEntries(
Object.entries(obj).map(([key, val]) => [
toCamelCase(key),
camelizeKeys(val)
])
);
}
return obj;
}
Four common mistakes
Inconsistency within a single file. Pick a convention and enforce it with a linter. ESLint's
camelcaserule, Pylint'snaming-style, and Rubocop's naming cops all catch inconsistencies at commit time. Automate this so it's not a code review argument.Using abbreviations inconsistently. If
idis lowercase inuserId, thenurlshould be lowercase inapiUrl, notapiURL. Pick a rule for abbreviations and document it.Naming booleans without a verb prefix.
activeis ambiguous.isActiveis clear.loadingis ambiguous.isLoadingis clear. Boolean variables and functions should read as yes/no questions.Converting case in places where the original casing is meaningful. HTTP headers are case-insensitive, but some APIs depend on exact casing. Database column names may be case-sensitive depending on the engine. Don't blindly transform everything -- know your boundaries.
For quick conversions between camelCase, snake_case, kebab-case, PascalCase, and other formats, I built a case converter at zovo.one/free-tools/case-converter. Paste in text in any format and get all the variations at once.
Naming conventions are boring. That's the point. They remove a category of decisions so you can focus on the decisions that actually matter.
I'm Michael Lip. I build free developer tools at zovo.one. 350+ tools, all private, all free.
Top comments (0)