Snake, Camel, Kebab, Pascal: A Working Guide to Naming Conventions
A Stupid Argument That Won't Die
Every developer who has worked on a team longer than six months has been in some version of this meeting. Someone opens a pull request. Someone else comments that the variable should be user_id, not userId, because the rest of the file uses snake_case. A third person points out that the API the file talks to returns userID, and that converting at the boundary is silly. A senior engineer mutes their mic to scream into a pillow. Forty minutes later the PR merges with a compromise nobody likes.
Naming-convention arguments are stupid in the way that arguments about which side of the road to drive on are stupid. The choice is mostly arbitrary, the consequences of getting it wrong are real, and the only durable solution is a written rule that everyone agrees to follow even when they personally prefer the other one. This post is not going to settle which convention is best. It is going to do something more useful: explain where each one came from, why every language and framework picked the one it did, and how to make a defensible choice for a new project without having the same forty-minute meeting every quarter.
A Short, Honest History
The conventions have older roots than most people assume, and the names for them are younger than the conventions themselves.
camelCase as a programming idea predates the term by decades. Early Smalltalk used it. Early Java used it. The C standard library mostly did not, but plenty of C codebases did. The word "camelCase" itself was popularized in 1995 by Newton Love, a programmer who wrote about the practice on Usenet and coined the term because the capital letters in the middle of a word look like the humps on a camel's back. That naming caught on so fast that by the late 1990s, "camelCase" was the standard term in style guides for languages that had been using it without a name for twenty years.
snake_case is the older convention by far in actual code — C, Lisp, and Unix system calls were full of underscored identifiers in the 1970s — but the name is much younger. The earliest widely-cited use of "snake_case" is from a 2004 post on the Hibernate forums by Gavin King, where he used the term as if it were already common. It probably was inside small communities before that, but it had not crossed over into general programmer vocabulary. The folk etymology is that the underscores between words look a bit like a snake slithering through the identifier; whether that is what King had in mind or whether he was reaching for a counterpart to camelCase is hard to say. Either way, the name stuck.
kebab-case has roots in Lisp, which used hyphens as the standard word separator in identifiers from the 1960s onward — Lisp can do this because hyphen is not a subtraction operator in prefix notation, so account-balance is a perfectly good identifier rather than a malformed expression. The name "kebab-case" appears to have emerged in the early 2010s in JavaScript and CSS communities, by analogy with snake_case: words skewered on hyphens, like meat on a kebab. Some style guides still call it "dash-case" or "lisp-case" or "spinal-case," but "kebab" has won.
PascalCase got its name from Pascal, the language Niklaus Wirth designed in 1970, whose style guides preferred capitalized identifiers for procedures and types. By the time Microsoft was writing the .NET Framework Design Guidelines in the early 2000s, "PascalCase" was the standard term for "every word capitalized, no separators," which is how the convention reached its current name. Confusingly, plenty of people still use "PascalCase" and "camelCase" interchangeably or call the capitalized version "UpperCamelCase," but in 2026 the dominant convention treats them as distinct.
The fifth convention worth naming is SCREAMING_SNAKE_CASE, used almost exclusively for constants. The name is self-deprecating in a way the others are not, which is part of why it caught on.
The Convention Each Language Actually Picked
If you look at where each convention is dominant, the pattern is less about language design and more about which earlier language each new one was trying to look like.
Python uses snake_case for variables and functions, PascalCase for classes, and SCREAMING_SNAKE_CASE for constants. This is codified in PEP 8, the style guide Guido van Rossum and Barry Warsaw wrote in 2001. The choice was not theoretical: it was a deliberate attempt to match the conventions of the C standard library and the Unix system-call interface that early Python was wrapping. Once PEP 8 existed and the standard library followed it, every serious Python project followed it too, and the convention is now so entrenched that breaking it in a code review is treated as a quality issue, not a style preference.
JavaScript uses camelCase for variables and functions, PascalCase for classes and React components, SCREAMING_SNAKE_CASE for constants, and kebab-case for file names in most ecosystems. The convention came from Java, which JavaScript was rushed into looking like for marketing reasons in 1995, even though the language semantics had almost nothing in common with Java. The kebab-case file convention is more recent and is mostly downstream of npm and Node.js, where package names are kebab-case by registry rule. CSS files and HTML attributes also use kebab-case, which keeps the file-system convention consistent with the markup convention.
Ruby uses snake_case for variables and methods and PascalCase for classes and modules, almost identical to Python. The Rails community has been so disciplined about this that snake_case has become a tribal marker.
Go rejects underscores entirely. The convention, baked into the standard library and enforced by gofmt, is camelCase for unexported identifiers and PascalCase for exported ones — the case of the first letter is semantically meaningful in Go because it controls visibility. "MixedCaps" is the term the Go authors used, but it is camelCase and PascalCase in everything but name.
Rust uses snake_case for functions, variables, and modules; PascalCase for types, traits, and enum variants; SCREAMING_SNAKE_CASE for constants. The compiler will emit a warning if you mix them up, which is one of the more aggressive style-enforcement decisions in any current language. The Rust core team made it intentionally hard to write Rust that does not look like Rust.
C# and the broader .NET world use PascalCase for nearly everything — public methods, properties, classes, namespaces — with camelCase only for local variables and private fields (sometimes with a leading underscore). Microsoft's .NET Framework Design Guidelines were the source, and they were unusually disciplined for an industry document; the conventions there now extend across most of the .NET ecosystem.
SQL traditionally uses SCREAMING_SNAKE_CASE for keywords and snake_case for identifiers, though the latter is more variable. PostgreSQL is case-insensitive for unquoted identifiers, which has produced a culture of strict snake_case to avoid ever having to use quotes; SQL Server is more permissive and the conventions are correspondingly messier.
JSON has no convention. The format is agnostic, but the de facto split is that JavaScript-facing APIs use camelCase keys, Python and Ruby-facing APIs use snake_case, and the JSON:API specification recommends kebab-case in URLs but camelCase or kebab in member names, which is exactly the kind of half-decision that produces the meeting at the top of this article.
HTTP headers use Train-Case-Like-This — capitalized words separated by hyphens — though the HTTP specification explicitly says headers are case-insensitive. Curl and most browser dev tools display them in Train-Case by convention.
CSS uses kebab-case for properties and class names. This is partly because CSS is from the same era and design tradition as HTML attributes (which use kebab-case for the same reason) and partly because the underscore was used in older CSS hacks for browser targeting.
The Tax You Actually Pay
The reason these arguments keep happening is that the cost of mixing conventions is not zero. It is just spread out across a lot of small frictions.
The biggest tax is at API boundaries. A Python backend that talks to a JavaScript frontend has to decide whether the JSON keys are snake_case (native to the backend) or camelCase (native to the frontend). Either choice means somebody is converting at runtime, and converting at runtime means a bug whenever a key gets renamed and the converter is not updated. A surprising amount of real-world API bug surface lives in this conversion layer, especially when the names round-trip through nested objects or arrays.
The second tax is at the file-system boundary. macOS is case-preserving but case-insensitive by default, while Linux is case-sensitive. A file checked in as UserProfile.tsx and imported as ./userProfile works on a developer's Mac and breaks in production CI on Linux. The fix is a strict file-naming convention enforced by a lint rule, which is more work than just picking one.
The third tax is human. A codebase that mixes conventions reads as low-quality even if every individual decision was defensible at the time. Code-review velocity drops because reviewers spend cycles on what should be a default. New hires onboard slowly because they cannot internalize a single rule.
None of these are showstoppers. They are the kind of friction that compounds over years and shows up as "this codebase feels heavy to work in" without anyone being able to point at a single cause.
Converting Without Religious Wars
Most working developers spend more time converting between conventions than choosing one. The cleanest pattern is to do conversion at exactly two layers: at the API boundary, where incoming JSON gets normalized to the codebase's native convention and outgoing JSON gets normalized back; and at the database layer, where ORM column names are mapped to in-memory field names. Inside the application, the convention should be single and enforced. ORMs like SQLAlchemy, ActiveRecord, and Prisma have built-in support for this kind of boundary mapping, and so do most modern HTTP client libraries.
For the smaller everyday job — pasting a variable name into a tool and getting it in the convention you need — you mostly want a quick converter rather than a regex you write fresh each time. The case converter on this site handles the seven common cases (upper, lower, title, sentence, camel, snake, kebab) in the browser, which is enough for the constant micro-task of taking userProfileId from a TypeScript file and dropping user_profile_id into a Python migration without thinking about it. It is intentionally not a code-mod or a refactoring tool; for whole-codebase rewrites you want your IDE's rename and your language's formatter (Prettier, Black, gofmt, rustfmt) doing the work, not a copy-paste loop.
How to Choose for a New Project
If you are starting a project today and have to pick, the right algorithm is short. Pick the convention your language's standard library and dominant style guide use. PEP 8 if you're writing Python. gofmt if you're writing Go. Standard JS / Prettier defaults if you're writing JavaScript or TypeScript. The .NET design guidelines if you're writing C#. There is no upside to being clever about this, and the downside is that every new contributor will have to learn your house style before they can be productive.
For the cross-cutting decisions where no language convention dominates — JSON key style across a polyglot system, URL slug style, environment variable names — pick once, write it down somewhere a new hire will actually read, and add a linter or schema check that fails the build if it gets violated. The convention is not the important part; the durability of the decision is. A merely-okay rule that everyone follows is worth more than a beautiful rule that gets debated every quarter.
The conventions themselves do not really matter. The argument about them does. Pick one, write it down, lint it, and move on to problems whose answers are not already written down for you.
Related Free Tools
Stay Informed
Get ecosystem updates
New tools, posts, and ecosystem news — no spam, unsubscribe anytime.