Ir para o conteúdo
OneKitly

camelCase, snake_case, kebab-case: qual usar, e porque raramente és tu a escolher

Publicado a 03/07/2026 · 13 min de leitura · Ferramentas de texto e idioma

Daniel Okonkwo

Daniel OkonkwoProgramador front-end e redator de Tecnologia na OneKitly

Desempenho web · Formatos de ficheiro

Verificado a partir de 7 fontes

Ver perfil
Em resumo

Cada convenção é imposta pelo que a sintaxe à volta permite, não escolhida por gosto. Em quase toda a linguagem infixa o hífen é o operador de subtração, pelo que user-name é analisado como user menos name e não pode ser um identificador: o JavaScript lança um SyntaxError com var user-name. Esse único facto divide o mundo. Os nomes de propriedades CSS, os atributos HTML e os caminhos de URL vivem em gramáticas onde os identificadores nunca são expressões, pelo que aí o hífen é inequívoco e kebab-case é o estilo nativo. As linguagens da família Lisp aceitam identificadores kebab pela mesma razão: são prefixas, não infixas. Todo o resto assenta em snake_case ou camelCase, e a escolha é por ecossistema: a PEP 8 impõe snake_case para funções e variáveis Python; o Rust faz o mesmo e o seu compilador avisa por omissão; o Go impõe MixedCaps e torna semântica a maiúscula inicial, porque controla a exportação; Java e JavaScript usam lowerCamelCase com tipos em PascalCase. A armadilha é a ida e volta. Converter camelCase em snake_case e regressar não é sem perdas quando há siglas: parseHTMLDocument passa a parse_htmldocument e depois a parseHtmldocument com um conversor ingénuo, e a fronteira de palavra desapareceu de vez. Corrija na origem tratando as siglas como palavras normais, como exige o guia de estilo Java da Google. E um slug de URL é uma quarta coisa: minúsculas, sem acentos e com comprimento limitado.

As convenções não são gosto. O hífen é o operador menos, pelo que kebab-case não pode ser um identificador na maioria das linguagens - e é exatamente por isso que o CSS e os URL o usam. Mais a ida e volta com siglas que corrompe nomes em silêncio, e a regra que a corrige.

O hífen é o operador menos, e é essa toda a explicação

Escreva var user-name = 1 no Node e obtém SyntaxError: Unexpected token '-'. O analisador não está a ser chato. Numa linguagem infixa, a-b em posição de expressão significa subtrair b de a, e o tokenizador não tem como saber que queria um identificador em vez de dois operandos. Python, Java, C, C#, Go, Rust, PHP, Ruby e SQL partilham todos esta restrição. O hífen já está tomado.

Isso deixa exatamente duas formas de juntar palavras dentro de um identificador: o sublinhado, que nenhuma linguagem usa como operador, ou a maiúscula, que não é um caráter separador no sentido do tokenizador. snake_case e camelCase não são duas escolas estéticas. São as duas únicas soluções para uma restrição imposta pela aritmética.

A exceção confirma a regra. Lisp, Scheme, Clojure e Common Lisp permitem todos identificadores em kebab-case - make-hash-table, my-function-name - porque são linguagens prefixas: a subtração escreve-se (- a b), pelo que um hífen entre letras nunca pode ser um operador. Mude a gramática e a convenção de nomes muda com ela, que é exatamente a demonstração.

Onde kebab-case é nativo: CSS, atributos HTML, URL

Em CSS, background-color é um nome de propriedade numa posição onde nenhuma expressão é admitida, pelo que o hífen não tem com que ser confundido. A prova de que o CSS tem consciência da tensão é calc(): a especificação exige espaços à volta dos sinais de mais e de menos lá dentro, precisamente porque é o único sítio do CSS onde um hífen podia ser uma subtração ou parte de um identificador. Os atributos HTML seguem a mesma lógica: data-user-id é um nome em posição de atributo, nunca uma expressão.

É no ponto de cruzamento que fica interessante. O DOM tem de expor data-user-id ao JavaScript, onde o hífen é ilegal, pelo que o renomeia: element.dataset.userId. O modelo de objetos CSS faz o mesmo às propriedades, transformando background-color em style.backgroundColor. Essas duas conversões automáticas são a demonstração mais clara possível de que a convenção é uma função da gramática anfitriã e de mais nada: o mesmo nome, escrito de duas formas, porque duas gramáticas exigem duas grafias.

Os URL admitem tanto o hífen como o sublinhado - ambos são carateres não reservados -, pelo que aqui a razão é diferente e muito mais branda. As recomendações de URL da Google preferem o hífen porque se lê como separador de palavras tanto para rastreadores como para pessoas, e porque um sublinhado pode desaparecer sob o sublinhado de uma ligação. É um argumento de legibilidade, não gramatical, mas endureceu numa convenção tão forte que hoje um URL com sublinhados parece um erro.

A ida e volta que perde informação

Converter camelCase em snake_case e voltar parece uma bijeção. Não é, e são as siglas que a partem. Um conversor ingénuo insere um sublinhado antes de cada maiúscula que segue uma minúscula e depois passa tudo a minúsculas. Passe parseHTMLDocument por ele e obtém parse_htmldocument, porque não há nenhuma minúscula antes do H, do T, do M nem do L. Converta de volta e obtém parseHtmldocument. A fronteira de palavra entre HTML e Document desapareceu, e nenhuma esperteza a jusante a consegue recuperar.

getIDFromURL é pior, porque o dano não se limita à caixa. A regra ingénua produz get_idfrom_url - dispara entre o t e o I, e de novo entre o m e o U, mas não dentro de IDFrom - e o regresso dá getIdfromUrl. Um nome que eram três palavras claras tornou-se duas palavras estropiadas, e se essa cadeia for uma coluna de base de dados, uma chave JSON ou um campo de API, a corrupção ficou persistida.

A regra que corrige, e o caso que continua a falhar

A correção cabe numa segunda regra de fronteira. Ao lado da divisão habitual minúscula-depois-maiúscula, acrescente uma divisão entre uma sequência de maiúsculas e uma maiúscula seguida de minúscula. Em expressões regulares são duas passagens: inserir um sublinhado entre ([a-z0-9]) e ([A-Z]), depois entre ([A-Z]+) e ([A-Z][a-z]), e por fim passar a minúsculas. Com essas duas regras, parseHTMLDocument passa a parse_html_document e volta como parseHtmlDocument; getIDFromURL passa a get_id_from_url e volta como getIdFromUrl; exportToPDFFile passa a export_to_pdf_file. As formas kebab são parse-html-document, get-id-from-url e export-to-pdf-file. As fronteiras de palavra sobrevivem.

Repare que a ida e volta continua a não ser a identidade: parseHTMLDocument volta como parseHtmlDocument, com a sigla em caixa de título. Esse é o resultado correto, não um erro residual, e aponta para a correção verdadeira. A secção 5.3 do guia de estilo Java da Google exige exatamente isto no momento da escrita: escreva as siglas como palavras normais, ou seja XmlHttpRequest em vez de XMLHTTPRequest, e o nome torna-se um ponto fixo da conversão. Um nome que sobrevive à sua própria ida e volta é um nome que pode passar sem risco por um gerador de código, um ORM, um serializador e de volta.

Resta um caso que a regra das duas expressões continua a falhar, e vale a pena conhecê-lo porque parece a regra a falhar. As siglas de caixa mista derrotam-na: supportsIPv6 passa a supports_i_pv6, e a forma kebab é supports-i-pv6. A segunda expressão vê o P maiúsculo seguido do v minúsculo e divide ali, que é exatamente o que deve fazer em todo o resto. Nenhuma regra de fronteira que olhe apenas para a caixa das letras pode saber que IPv6 é um único token. Este é o argumento mais forte a favor da regra da Google: escreva supportsIpv6 desde o início e o conversor nunca terá de adivinhar.

Um slug de URL é uma quarta coisa, não kebab-case com passos extra

Um slug parece kebab-case mas tem três obrigações extra que um identificador nunca tem. Tem de sobreviver à passagem a minúsculas, porque os caminhos de URL são comparados pelos servidores distinguindo maiúsculas, mas escritos com descuido pelas pessoas. Tem de sobreviver à remoção de acentos, porque um caminho com carateres acentuados é codificado em percentagens e torna-se ilegível. E tem de caber num orçamento de comprimento, porque os slugs acabam em e-mails, material impresso e barras de endereços onde um caminho de 200 carateres é inutilizável.

O passo de remoção de acentos é onde as implementações ingénuas perdem dados em silêncio. A receita habitual é normalizar para a forma decomposta, apagar as marcas combinantes e depois manter apenas letras, dígitos e hífenes. Aplicada a um título francês funciona: Crème Brûlée & Co. — 2026 Edition fica creme-brulee-co-2026-edition, 28 carateres. Aplicada ao alemão destrói o texto. O título Größe & Maße: der Überblick sai como gro-e-ma-e-der-uberblick, porque o esse alemão não tem decomposição canónica: não é convertido em nada, é simplesmente apagado, tal como qualquer outro caráter que não seja letra latina.

A correção é transliterar antes de normalizar, com um mapa por língua: o esse alemão para ss, as vogais com trema para oe, ae e ue em alemão, o o cortado e o a com anel escandinavos para os equivalentes de duas letras. Com esse passo à frente, o mesmo título alemão dá groesse-masse-der-ueberblick, 28 carateres e de facto legível. Torne o slug resultante imutável assim que publicado, limite-o a uns 60 a 80 carateres cortando numa fronteira de palavra, e nunca o regenere a partir de um título editado sem emitir um redirecionamento a partir do antigo.

Escolher, na prática

Siga o anfitrião, não a sua preferência. Dentro de um ficheiro Python, snake_case, mesmo que o JSON que está a analisar seja camelCase. Dentro de um ficheiro CSS, kebab-case, mesmo que os tokens de design tenham sido escritos em camelCase. Dentro de um esquema PostgreSQL, snake_case, porque o analisador vai passar o seu camelCase a minúsculas de qualquer modo e passará o resto do projeto a escrever aspas duplas.

Converta apenas nas fronteiras, e num único sítio. Se a sua API fala camelCase e a sua base de dados snake_case, ponha uma só camada de correspondência entre ambas em vez de converter avulso em cada chamada, e faça dessa camada o único código que conhece a regra das duas expressões. Escreva as siglas como palavras em todo o lado, para que a conversão seja um ponto fixo e ninguém tenha de voltar a pensar nisso. E quando gerar um slug, trate-o como um identificador publicado a partir do momento em que sai: é a única destas quatro formas que um desconhecido vai colar numa mensagem.

O que exige o guia de estilo de cada ecossistema, e o que realmente o impõe: uma convenção, um linter ou o próprio analisador.
EcossistemaVariáveis e funçõesTipos e classesConstantesO que o impõe
Python (PEP 8)snake_caseCapWordsUPPER_SNAKE_CASESó convenção; os linters avisam, o interpretador aceita tudo
Rustsnake_caseUpperCamelCaseSCREAMING_SNAKE_CASEO compilador: non_snake_case e non_camel_case_types avisam por omissão
GomixedCapsMixedCapsMixedCaps, nunca sublinhadosO compilador: a maiúscula inicial é o que exporta o identificador, logo a caixa é semântica, não estilo
Java (Google Java Style)lowerCamelCaseUpperCamelCaseUPPER_SNAKE_CASEConvenção, mais a regra explícita de que as siglas se escrevem como palavras: XmlHttpRequest, não XMLHTTPRequest
JavaScript e TypeScriptcamelCasePascalCaseUPPER_SNAKE_CASENão há guia oficial; a gramática só exclui o hífen, porque é o operador menos
CSS e HTMLkebab-case para propriedades, classes e propriedades personalizadasO CSS não tem tipos definidos pelo utilizador; os nomes de elementos HTML são minúsculospropriedades personalizadas em kebab-case, prefixadas com dois hífenesA gramática: um nome de propriedade nunca é uma expressão, logo um hífen no meio não pode ser um menos - e por isso calc() exige espaços à volta dos sinais de menos
PostgreSQLsnake_case para tabelas e colunassnake_case para tipos e domíniosUPPER_SNAKE_CASE apenas por convençãoO analisador: os identificadores sem aspas são convertidos para minúsculas, pelo que um nome de tabela em camelCase fica minúsculo em silêncio a não ser que o coloque entre aspas para sempre
Conversor para kebab-caseConverta qualquer texto ou identificador camelCase em kebab-case minúsculo (com hífens).Experimentar a ferramenta

Perguntas frequentes

Porque posso escrever background-color em CSS mas não backgroundColor numa folha de estilos?
Porque os nomes de propriedades CSS são um vocabulário fixo definido pela especificação, e esta escreve-os em kebab-case. Não é que camelCase seja ilegal na gramática: é que backgroundColor não é o nome de propriedade nenhuma, pelo que a declaração é descartada como desconhecida. As grafias em camelCase existem apenas no modelo de objetos CSS, a vista JavaScript de um estilo, onde o hífen seria um sinal de menos. Duas grafias, duas gramáticas, uma propriedade.
Lê-se melhor camelCase ou snake_case?
Os trabalhos publicados de rastreio ocular sobre isto são poucos, antigos e contestados, e não sustentam nenhuma afirmação forte em qualquer sentido: alguns estudos acham snake_case marginalmente mais rápido de ler, outros acham que os leitores treinados são mais rápidos no estilo que praticam diariamente. O que não é contestado é o custo da incoerência dentro da mesma base de código. Adote o que o ecossistema exigir, imponha-o com um formatador e gaste o orçamento de discussão em algo que mude comportamentos.
Como converto em segurança o JSON camelCase de uma API para uma base de dados em snake_case?
Use a regra de fronteira em duas passagens, aplique-a num único módulo e fixe a correspondência para qualquer nome que contenha uma sigla. A versão pragmática é manter uma tabela de exceções explícita: um dicionário curto com os quinze ou vinte nomes de campo do seu esquema cuja conversão não quer que uma expressão regular decida. Essa tabela custa uma hora a escrever e elimina toda a classe de erro, ao passo que uma correspondência puramente algorítmica acabará por encontrar um nome como supportsIPv6 e produzir algo que nenhum revisor nota até uma consulta não devolver nada.
Posso usar camelCase para nomes de tabelas e colunas no PostgreSQL?
Pode, mas apenas colocando o identificador entre aspas em todas as ocorrências, para sempre, em cada consulta, migração, vista e script. O PostgreSQL converte para minúsculas os identificadores sem aspas, pelo que uma tabela criada como userAccounts passa a useraccounts, e uma consulta posterior por userAccounts só a encontra porque também é convertida para useraccounts - até ao dia em que alguém coloca uma delas entre aspas e as duas deixam de corresponder. Aqui snake_case não é preferência de estilo: é a forma que atravessa o analisador intacta.
Um slug de URL deve ser apenas o título em kebab-case?
Quase, mas com três acrescentos que o kebab-case sozinho não dá. Passe tudo a minúsculas, porque um caminho que difere apenas na caixa é outro recurso para um servidor mas a mesma coisa para uma pessoa. Translitere antes de remover os acentos, ou carateres como o esse alemão desaparecem por completo em vez de se tornarem ss. E limite o comprimento numa fronteira de palavra, à volta de 60 a 80 carateres, já que o slug será colado em sítios sem espaço. Mais uma regra, alheia à caixa: uma vez publicado, nunca o altere sem um redirecionamento permanente a partir do caminho antigo.

Artigos que podem interessar-lhe

Todos os guias
ExplicaçãocamelCase vs snake_case: guia das convenções de nomenclatura no códigocamelCase, snake_case, PascalCase e kebab-case explicados: como cada um se apresenta, onde é a convenção e como escolher um de forma coerente.GuiaCodificação de URL explicada: o sinal de percentagem e onde mordeA codificação por percentagem decide-se componente a componente, e é daí que vem toda a confusão. Uma barra é legal num caminho e tem de ser escapada num valor de consulta; um espaço é %20 num caminho e pode ser + num corpo de formulário. Eis os conjuntos exatos da RFC 3986, as três funções de JavaScript que discordam e as armadilhas.ExplicaçãoA cifra de César explicada: como funcionam o deslocamento e o ROT13A cifra de César desloca cada letra por uma quantidade fixa. Saiba como funciona o deslocamento, porque o ROT13 é um caso especial, como codificar e descodificar à mão, e porque a cifra não oferece segurança real hoje.TutorialComo converter JSON para CSV: achatar arrays de objetos em linhas e colunasUm guia prático para transformar um array JSON de objetos num ficheiro CSV limpo, incluindo o achatamento de campos aninhados e os casos-limite.ExplicaçãoRetirar os acentos estraga a pesquisa — até o fazer dos dois ladosRebater os diacríticos é um passo de normalização, e a normalização só funciona quando a mesma função corre sobre o índice e sobre a consulta. NFC contra NFD com os pontos de código à vista, e as letras — ø, ł, ß, œ, ı — que saem do rebatimento intactas.ExplicaçãoAs hashtags são um índice de pesquisa, não um megafoneUma hashtag torna uma publicação localizável numa consulta, o que é uma função diferente de a fazer espalhar-se. Modele a visibilidade que uma etiqueta compra realmente e a resposta cai sozinha: o valor de uma etiqueta é o seu número de espectadores por publicação publicada, não o seu volume — por isso uma etiqueta muito popular não devolve quase nada e uma específica devolve vinte e cinco vezes mais.

Ferramentas relacionadas

Fontes

Detetaste um erro neste artigo?