Snake Case Converter: snake_case, SCREAMING_SNAKE and kebab-case
snake_case joins words with underscores and keeps every letter lowercase: user_first_name, max_retry_count, created_at. Put the same words in capitals and you get SCREAMING_SNAKE_CASE, the convention for constants. Swap the underscore for a hyphen and you get kebab-case, the convention for URLs and CSS.
The converter below produces all four, and turns an existing identifier back into readable words. Below it: which language expects which style, why PostgreSQL quietly lowercases your column names, and why Google asks for hyphens rather than underscores in a URL.
Need the other styles too? The full upper lower case converter covers UPPERCASE, lowercase, Sentence case, Title Case, camelCase and more, with a live character count. Free, no sign-up.
snake_case and its relatives
Four separator styles, one underlying idea: replace the space with a character that identifiers are allowed to contain.
| Style | Same three words | Also called | Typical home |
|---|---|---|---|
| snake_case | user_first_name | underscore case, pothole case | Python, Ruby, SQL, Rust |
| SCREAMING_SNAKE_CASE | USER_FIRST_NAME | constant case, macro case | Constants, environment variables |
| kebab-case | user-first-name | dash case, spinal case | URLs, CSS, HTML attributes |
| dot.case | user.first.name | property case | Config files, message keys |
| camelCase | userFirstName | lower camel case | JavaScript, Java, Swift |
| PascalCase | UserFirstName | upper camel case | Class names everywhere |
The names are informal and inconsistent, which is why a tool that offers constant case and one that offers SCREAMING_SNAKE_CASE mean the same thing. The two camel styles have their own guide: camelCase and PascalCase explained.
Where snake_case is the standard
These are not preferences. Each is written down in the language’s official style guide and enforced by its linter.
| Context | Convention | Set by |
|---|---|---|
| Python variables and functions | snake_case | PEP 8 |
| Python constants | SCREAMING_SNAKE_CASE | PEP 8 |
| Ruby variables and methods | snake_case | Ruby style guide |
| Rust variables and functions | snake_case | Rust API guidelines |
| C and C++ (traditional) | snake_case | Long-standing practice |
| SQL tables and columns | snake_case | Portability across engines |
| Environment variables | SCREAMING_SNAKE_CASE | POSIX convention |
| YAML and TOML keys | snake_case or kebab-case | Project choice |
| URLs and slugs | kebab-case | Google search guidelines |
| CSS classes and custom properties | kebab-case | CSS convention |
Underscores and hyphens are not interchangeable in a URL. Google has said for years that it treats a hyphen as a word separator and an underscore as a joiner, so /red_shoes can be read as the single token redshoes while /red-shoes is read as two words. Use kebab-case in every public URL and keep underscores for code.
Why databases fold your column names
There is a concrete technical reason SQL settled on snake_case, and it catches people who arrive from a camelCase language.
In the SQL standard, an unquoted identifier is case insensitive. PostgreSQL implements that by folding unquoted identifiers to lowercase, so a column created as firstName is stored as firstname. Query it as firstName and it still works, because that folds too. Query it as "firstName", in double quotes, and it fails: the quoted form is case sensitive and there is no column by that exact name.
| Engine | Unquoted identifier | Consequence |
|---|---|---|
| PostgreSQL | Folded to lowercase | firstName becomes firstname |
| Oracle | Folded to UPPERCASE | firstName becomes FIRSTNAME |
| MySQL on Linux | Table names case sensitive | Users and users are two tables |
| MySQL on Windows | Table names case insensitive | The same schema behaves differently |
| SQL Server | Depends on collation | Usually case insensitive |
snake_case sidesteps all of it. A name that contains no capital letters cannot be folded into something different, so it behaves identically on every engine and on every operating system. That is the whole argument, and it is why an ORM that generates created_at rather than createdAt is doing you a favour.
How to convert to snake_case in code
| Language | Approach |
|---|---|
| Python | '_'.join(text.lower().split()) for words; a small loop for camelCase input |
| JavaScript | Insert an underscore before each capital, then lowercase the whole string |
| Lodash | _.snakeCase('userFirstName') returns user_first_name |
| Ruby | Rails adds 'UserFirstName'.underscore |
| SQL | LOWER(REPLACE(name, ' ', '_')) |
| Excel | =LOWER(SUBSTITUTE(TRIM(A1)," ","_")) |
| Shell | tr 'A-Z ' 'a-z_' |
The Excel formula is worth keeping: TRIM removes the double spaces that would otherwise become double underscores, SUBSTITUTE swaps space for underscore, and LOWER flattens the case. The other spreadsheet case recipes are collected in upper and lower case in Excel and converting text to uppercase in Excel.
Converting from camelCase is the harder direction, because the boundary is a capital letter rather than a space, and consecutive capitals such as HTTPResponse have no obvious split point. The converter above handles both: it detects a lowercase-to-uppercase transition and an acronym followed by a normal word.
SCREAMING_SNAKE_CASE: constants and environment variables
Capitals in code carry one meaning almost universally: this value does not change. MAX_RETRIES, DEFAULT_TIMEOUT, API_BASE_URL. The convention predates most modern languages and comes from C, where macros were written in capitals so a reader could see that the compiler would substitute text rather than evaluate a variable.
Environment variables follow the same rule for a different reason. The POSIX specification reserves lowercase names for the shell’s own use, so an exported variable in capitals cannot collide with one of the shell’s internals. DATABASE_URL is not shouting, it is staying out of the way.
| Name | Reads as | Correct? |
|---|---|---|
| MAX_RETRIES | A constant | Yes |
| maxRetries | A variable | Only if it can change |
| DATABASE_URL | An environment variable | Yes |
| database_url | A local variable | Wrong for an env var |
| Max_Retries | Nothing standard | No convention uses this |
The last row is the trap. Mixed capitals with underscores belongs to no published style guide, exactly as Capitalize Each Word belongs to no prose style guide — a point made in detail in should all letters in a title be capitalized. A run of capitals is also slower for a human to read, which is why the convention is reserved for short names; the reasoning is set out in uppercase vs lowercase.
kebab-case: URLs, CSS and file names
The hyphen is the separator you use when the underscore is unavailable or unhelpful.
- URLs. Hyphens are read as word boundaries by search engines; underscores are not. /upper-lower-case-converter is three words to Google, /upper_lower_case_converter is closer to one.
- CSS. Class names, custom properties and every property in the language itself use hyphens: background-color, --brand-yellow, .site-header.
- HTML attributes. data-user-id in the markup, read back as dataset.userId in JavaScript. The browser does the conversion because attribute names are case insensitive and identifiers are not.
- Public file names. A hyphen survives being pasted into an email or a chat window; an underscore is often swallowed by the underline of an automatic link.
Hyphens are illegal in identifiers. In every C-family language a hyphen is the minus operator, so user-name parses as user minus name. That is the entire reason snake_case exists: the underscore is the only separator that a compiler will accept inside a name.
snake_case or camelCase: does it matter?
For readability, marginally. Studies of code comprehension have generally found snake_case slightly faster to read for people unfamiliar with the code, with the advantage shrinking to almost nothing among developers used to the style in front of them. The underscore is an explicit boundary; a capital letter is an implicit one.
For everything else, the choice is made for you. Python code written in camelCase will fail review, JavaScript written in snake_case will fail review, and both linters will say so before a human does. The only real decision left is at the boundaries between systems, and there the rule is to convert once, in one named place.
| Question | Answer |
|---|---|
| New Python project | snake_case, PEP 8, no discussion needed |
| New JavaScript project | camelCase for code, kebab-case for CSS and URLs |
| Database schema | snake_case, whatever the application language is |
| Public JSON API | camelCase if the consumers are browsers; snake_case if they are Python or Ruby |
| Existing codebase | Whatever it already uses. Consistency beats correctness here. |
Four snake_case mistakes
- Double underscores from double spaces. Trim the text before converting, or first name becomes first__name, which in Python also has a special meaning.
- Leading underscores by accident. A name beginning with an underscore signals private in Python and reserved in C. Strip punctuation before converting.
- Underscores in a URL. Costs you the word separation that a hyphen would give, for no benefit.
- Converting acronyms letter by letter. A naive converter turns HTTPResponse into h_t_t_p_response. Test on an acronym first.
Every case style in one place. The upper lower case converter handles snake_case, kebab-case, camelCase, PascalCase, Title Case, Sentence case, UPPERCASE and lowercase, with a live word and character count. Free, in your browser.
Renaming a schema: a six-step checklist
Converting an existing database from camelCase to snake_case is the most common reason people arrive at a snake case converter with a hundred names in the clipboard. It is safe if you do it in this order.
- Export the current names. Query the information schema for every table and column name and paste the result into the converter above, one per line.
- Convert and read the output. The names that need a human are the ones containing acronyms, digits or existing underscores. Fix those by hand before you generate a single statement.
- Add the new columns, do not rename in place. A rename is instant but irreversible in the middle of a deployment. Adding a column, backfilling it and dropping the old one later can be stopped at any point.
- Update the application in the same release. An ORM that maps createdAt to created_at usually needs one configuration line rather than a hundred annotations.
- Search the string literals. Raw SQL in reports, dashboards, scheduled jobs and export scripts is not covered by any refactor tool.
- Drop the old columns in a later release, once nothing has referenced them for a full deployment cycle.
Case-only renames need two steps in Git. On macOS and Windows the file system is case insensitive, so renaming UserModel.py to user_model.py can be invisible to Git. Rename to a temporary name first, commit, then rename to the target.
Why the underscore, and not something else
The underscore is a historical accident that turned out well. When programming languages had to choose which punctuation could appear inside an identifier, the underscore was the one character with no arithmetic meaning. The hyphen was already minus, the dot was already member access, the slash was already division.
C adopted it in the early 1970s and every language that borrowed C syntax inherited the rule. Python later made it the official style, Ruby followed, and SQL had independently arrived at the same place because of identifier folding. snake_case is now the default in the two places most likely to outlive an application: the database schema and the configuration file.
Capital letters carry their own history and their own meaning, one that predates computing by centuries. That story is in uppercase unveiled, and the practical rules for capitals in ordinary writing are in the capital letters guide.
A conversion table for mixed codebases
| Concept | Python / SQL | JavaScript | CSS / URL | Environment |
|---|---|---|---|---|
| First name | first_name | firstName | first-name | FIRST_NAME |
| Created at | created_at | createdAt | created-at | CREATED_AT |
| Max retry count | max_retry_count | maxRetryCount | max-retry-count | MAX_RETRY_COUNT |
| HTTP response code | http_response_code | httpResponseCode | http-response-code | HTTP_RESPONSE_CODE |
| User ID | user_id | userId | user-id | USER_ID |
Paste any column of that table into the converter above and it will produce any other column. The camel columns are covered in the camelCase converter guide; for plain text rather than identifiers, use the lowercase converter or the title case converter.
Frequently asked questions
What is snake_case?
snake_case writes a multi-word name in lowercase with underscores between the words: user_first_name, max_retry_count, created_at. It is the standard for variables and functions in Python, Ruby and Rust, and for table and column names in SQL.
What is the difference between snake_case and kebab-case?
The separator. snake_case uses an underscore (user_first_name) and kebab-case uses a hyphen (user-first-name). Underscores are legal inside identifiers; hyphens are not, because a compiler reads them as minus. Use kebab-case for URLs and CSS.
What is SCREAMING_SNAKE_CASE for?
Constants and environment variables: MAX_RETRIES, DATABASE_URL. Capitals signal a value that does not change, a convention inherited from C macros. POSIX also reserves lowercase environment variable names for the shell itself.
How do I convert camelCase to snake_case?
Insert an underscore before every capital letter and lowercase the whole string, taking care with consecutive capitals such as HTTPResponse. The converter on this page does it for a whole list at once.
Should URLs use underscores or hyphens?
Hyphens. Google treats a hyphen as a word separator and an underscore as a joiner, so /red-shoes reads as two words while /red_shoes can read as one. Keep underscores for code and use kebab-case in every public URL.
Why do SQL columns use snake_case?
Because unquoted identifiers are folded by the database: PostgreSQL lowercases them, Oracle uppercases them. A name with no capitals survives that folding unchanged, so it behaves the same on every engine and operating system.
What is the Excel formula for snake_case?
=LOWER(SUBSTITUTE(TRIM(A1)," ","_")). TRIM removes double spaces that would become double underscores, SUBSTITUTE swaps each space for an underscore, and LOWER flattens the capitals.
Is snake_case easier to read than camelCase?
Slightly, for readers new to a codebase, because the underscore is an explicit word boundary while a capital letter is an implicit one. The difference is small and matters far less than being consistent with the language you are writing in.
The short version
snake_case is lowercase words joined by underscores, and it is the standard in Python, Ruby, Rust and SQL. Capitals turn it into SCREAMING_SNAKE_CASE for constants and environment variables. Hyphens turn it into kebab-case, which belongs in URLs, CSS and HTML attributes but never inside an identifier. Databases prefer snake_case because a name without capitals survives identifier folding on every engine, and search engines prefer hyphens because they read them as word boundaries. Trim your input first, watch out for acronyms, and convert between conventions in exactly one place.