How-To & Life · Guide · Text & Writing Utilities
How to Convert to snake_case
snake_case vs SCREAMING_SNAKE, PascalCase-to-snake rules, acronym handling, and language conventions (Python, Ruby, DB columns).
snake_case is the naming convention that uses lowercase words joined by underscores. It’s the default for Python functions and variables, Ruby methods, database column names, Rust local variables, and most config files. Converting from other case styles — PascalCase, camelCase, kebab-case, Title Case with Spaces — looks like a one-line regex until you hit acronyms (HTMLParser → html_parser? or h_t_m_l_parser?), numbers embedded in identifiers, and the SCREAMING_SNAKE variant used for constants. This guide covers the canonical conversion rules, how each language’s community treats edge cases, and the regex patterns that produce consistent results.
Advertisement
The four snake variants
- snake_case — standard lowercase, for names
- SCREAMING_SNAKE_CASE — uppercase, for constants
- Title_Snake_Case — rare, sometimes HTTP header-style
- camel_Snake_Case — very rare, hybrid
From camelCase and PascalCase
The canonical rule: insert an underscore before any uppercase letter preceded by a lowercase letter, then lowercase the whole thing.
str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase() "camelCase" -> "camel_case" "PascalCase" -> "pascal_case" "getXMLParser" -> "get_xml_parser" ?? (see next section)
The acronym problem
“HTMLParser” is a single concept but three capitals in a row. The naive regex turns it into “htmlparser” (missing the word break) or “h_t_m_l_parser” (too aggressive). The canonical fix inserts underscores at two boundaries:
- Lowercase → uppercase: “a” → “A”
- Uppercase + uppercase-then-lowercase: “AB” + “c” at the split
function toSnake(s) {
return s
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2")
.toLowerCase();
}
toSnake("HTMLParser") // "html_parser"
toSnake("getHTTPResponse") // "get_http_response"
toSnake("APIKey") // "api_key"
toSnake("iOSVersion") // "i_os_version" (edge case!)The “iOSVersion” case shows the limit: there’s no dictionary-free way to know “iOS” is one acronym. Most codebases work around it by writing IOSVersion oriOsVersion in source.
From kebab-case
Trivial substitution:
str.replace(/-/g, "_").toLowerCase() "my-variable-name" -> "my_variable_name"
From Title Case or space-separated
str.replace(/\s+/g, "_").toLowerCase() "Get User Name" -> "get_user_name"
Strip punctuation first for robustness:
str
.replace(/[^\p{L}\p{N}\s]/gu, "")
.replace(/\s+/g, "_")
.toLowerCase()Numbers in identifiers
Language communities disagree on how to handle digits next to letters:
user2FA→user_2_f_a?user_2fa?user2_fa?version2→version_2orversion2?
Python’s inflection library usesuser_2fa and version_2. Rails uses the same rule. Go prefers user2fa with no underscore before digits. Pick one and stay consistent.
SCREAMING_SNAKE_CASE for constants
Convert snake_case to screaming by uppercasing:
"max_retry_count".toUpperCase() // -> "MAX_RETRY_COUNT"
Python, Ruby, JavaScript, Rust, and most C-family languages use SCREAMING_SNAKE for compile-time constants and environment variables. API_KEY, MAX_CONNECTIONS,DEFAULT_TIMEOUT_MS.
Language conventions
- Python (PEP 8) — snake_case for functions and variables, PascalCase for classes, SCREAMING_SNAKE for module-level constants.
- Ruby — snake_case for methods and variables, PascalCase for classes and modules, SCREAMING_SNAKE for constants.
- Rust — snake_case for variables, functions, modules; PascalCase for types; SCREAMING_SNAKE for constants and statics.
- PHP — snake_case was the historic convention; modern PHP (PSR-1) prefers camelCase for methods.
- SQL — column names almost universally snake_case. PascalCase and camelCase in column names cause quoting headaches in PostgreSQL especially.
Database column naming
Almost all ORMs expect snake_case column names and auto-map them to camelCase or PascalCase fields in application code. Railsfirst_name column <-> Ruby first_nameattribute. ActiveRecord and SQLAlchemy both do this conversion automatically; mess up the DB casing and you’ll fight the ORM all day.
Environment variables
Conventionally SCREAMING_SNAKE: DATABASE_URL,NODE_ENV, API_KEY. POSIX shells treat variable names as identifiers, so underscores and digits are fine; hyphens are not.
Round-trip considerations
snake_case → camelCase → snake_case usually round-trips cleanly if you avoid acronyms and digit edge cases. Breaking examples:
"html_parser" -> "htmlParser" -> "html_parser" OK "user_2fa" -> "user2Fa" -> "user_2_fa" NOT OK "io_s_version" -> "ioSVersion" -> "io_s_version" OK "api_key" -> "apiKey" -> "api_key" OK
Leading-underscore conventions
Python uses _private for module-internal names and__dunder__ for special protocol methods. JavaScript conventionally prefixes private fields with underscore even though the language has real private syntax now. Preserve leading underscores in conversion:
function toSnakePreservePrefix(s) {
const leading = s.match(/^_+/)?.[0] ?? "";
const rest = s.slice(leading.length);
return leading + toSnake(rest);
}Common mistakes
Using one regex pass and getting “HTMLParser” wrong. Mixing snake and camel in the same codebase because you copy-pasted from sources with different conventions. Using SCREAMING_SNAKE for long identifiers and making them unreadable (a constant named MAX_ALLOWED_RETRY_COUNT_BEFORE_FAILis a code smell, not a convention win). Converting database columns to camelCase and creating quoting headaches in Postgres. And forgetting that acronyms are the forever edge case — pick your rule and document it.
Run the numbers
Advertisement