Vai al contenuto
Allin

camelCase, snake_case, kebab-case: quale usare, e perché raramente scegli tu

Pubblicato il 03/07/2026 · 12 min di lettura · Strumenti testo e lingua

Daniel Okonkwo

Daniel OkonkwoSviluppatore front-end e redattore Tech presso Allin

Performance web · Formati di file

Verificato su 7 fonti

Vedi il profilo
In breve

Ogni convenzione è imposta da ciò che la sintassi circostante permette, non scelta per gusto. In quasi ogni linguaggio infisso il trattino è l'operatore di sottrazione, quindi user-name si analizza come user meno name e non può essere un identificatore: JavaScript solleva un SyntaxError su var user-name. Questo solo fatto divide il mondo. I nomi di proprietà CSS, gli attributi HTML e i percorsi URL vivono in grammatiche dove gli identificatori non sono mai espressioni, quindi lì il trattino non è ambiguo e kebab-case è lo stile nativo. I linguaggi della famiglia Lisp ammettono identificatori kebab per lo stesso motivo: sono prefissi, non infissi. Tutto il resto si assesta su snake_case o camelCase, e la scelta è per ecosistema: la PEP 8 impone snake_case per funzioni e variabili Python; Rust fa lo stesso e il suo compilatore avverte per impostazione predefinita; Go impone MixedCaps e rende semantica la maiuscola iniziale, perché governa l'esportazione; Java e JavaScript usano lowerCamelCase con tipi in PascalCase. La trappola è il viaggio di andata e ritorno. Convertire camelCase in snake_case e tornare indietro non è senza perdite quando ci sono acronimi: parseHTMLDocument diventa parse_htmldocument e poi parseHtmldocument con un convertitore ingenuo, e il confine di parola è perso per sempre. Correggilo alla fonte trattando gli acronimi come parole comuni, come impone la guida di stile Java di Google. E uno slug di URL è una quarta cosa: minuscolo, privo di accenti e con lunghezza limitata.

Le convenzioni non sono gusto. Il trattino è l'operatore meno, quindi kebab-case non può essere un identificatore nella maggior parte dei linguaggi: ecco perché lo usano CSS e URL. In più, il viaggio di andata e ritorno sugli acronimi che corrompe i nomi in silenzio, e la regola che lo risolve.

Il trattino è l'operatore meno, ed è tutta qui la spiegazione

Digita var user-name = 1 in Node e ottieni SyntaxError: Unexpected token '-'. Il parser non sta facendo il difficile. In un linguaggio infisso, a-b in posizione di espressione significa sottrai b da a, e il tokenizer non ha modo di sapere che intendevi un identificatore anziché due operandi. Python, Java, C, C#, Go, Rust, PHP, Ruby e SQL condividono tutti questo vincolo. Il trattino è già occupato.

Restano quindi esattamente due modi per unire parole dentro un identificatore: il trattino basso, che nessun linguaggio usa come operatore, o una maiuscola, che nel senso del tokenizer non è affatto un separatore. snake_case e camelCase non sono due scuole estetiche. Sono le uniche due soluzioni a un vincolo imposto dall'aritmetica.

L'eccezione conferma la regola. Lisp, Scheme, Clojure e Common Lisp ammettono tutti identificatori in kebab-case - make-hash-table, my-function-name - perché sono linguaggi prefissi: la sottrazione si scrive (- a b), quindi un trattino fra lettere non può mai essere un operatore. Cambia la grammatica e cambia con essa la convenzione di denominazione: è esattamente il punto.

Dove kebab-case è nativo: CSS, attributi HTML, URL

In CSS, background-color è un nome di proprietà in una posizione dove nessuna espressione è ammessa, quindi il trattino non ha con che cosa confondersi. La prova che il CSS è consapevole della tensione è calc(): la specifica richiede spazi attorno ai segni più e meno al suo interno, proprio perché è l'unico punto del CSS in cui un trattino potrebbe essere una sottrazione o parte di un identificatore. Gli attributi HTML seguono la stessa logica: data-user-id è un nome in posizione di attributo, mai un'espressione.

È al punto di passaggio che diventa interessante. Il DOM deve esporre data-user-id a JavaScript, dove il trattino è illegale, quindi lo rinomina: element.dataset.userId. Il CSS Object Model fa lo stesso con le proprietà, trasformando background-color in style.backgroundColor. Quelle due conversioni automatiche sono la dimostrazione più chiara possibile che la convenzione è una funzione della grammatica ospite e di nient'altro: lo stesso nome, scritto in due modi, perché due grammatiche esigono due grafie.

Gli URL ammettono sia il trattino sia il trattino basso - entrambi sono caratteri non riservati - quindi qui la ragione è diversa e molto più morbida. Le indicazioni di Google sugli URL preferiscono il trattino perché si legge come separatore di parole sia per i crawler sia per le persone, e perché un trattino basso può sparire sotto la sottolineatura di un link. È un argomento di leggibilità, non di grammatica, ma si è indurito in una convenzione tanto forte che oggi un URL con trattini bassi sembra un errore.

Il viaggio di andata e ritorno che perde informazione

Convertire camelCase in snake_case e tornare indietro sembra una biiezione. Non lo è, e gli acronimi sono il punto di rottura. Un convertitore ingenuo inserisce un trattino basso prima di ogni maiuscola che segue una minuscola, poi porta tutto in minuscolo. Passaci parseHTMLDocument e ottieni parse_htmldocument, perché prima della H, della T, della M e della L non c'è nessuna minuscola. Riconverti e ottieni parseHtmldocument. Il confine di parola fra HTML e Document è sparito, e nessuna astuzia a valle può recuperarlo.

getIDFromURL è peggio, perché il danno non si ferma al case. La regola ingenua produce get_idfrom_url - scatta fra la t e la I, e di nuovo fra la m e la U, ma non dentro IDFrom - e il ritorno dà getIdfromUrl. Un nome che era tre parole chiare è diventato due parole storpiate, e se quella stringa è una colonna di database, una chiave JSON o un campo di API, la corruzione è ormai persistita.

La regola che risolve, e il caso che ancora sfugge

La correzione sta in una seconda regola di confine. Accanto alla consueta divisione minuscola-poi-maiuscola, aggiungi una divisione fra una sequenza di maiuscole e una maiuscola seguita da minuscola. In termini di espressioni regolari sono due passaggi: inserire un trattino basso fra ([a-z0-9]) e ([A-Z]), poi fra ([A-Z]+) e ([A-Z][a-z]), quindi passare in minuscolo. Con queste due regole, parseHTMLDocument diventa parse_html_document e torna come parseHtmlDocument; getIDFromURL diventa get_id_from_url e torna come getIdFromUrl; exportToPDFFile diventa export_to_pdf_file. Le forme kebab sono parse-html-document, get-id-from-url e export-to-pdf-file. I confini di parola sopravvivono.

Nota che il viaggio di andata e ritorno non è ancora l'identità: parseHTMLDocument torna come parseHtmlDocument, con l'acronimo in maiuscolo iniziale. È il risultato corretto, non un bug residuo, e indica la vera soluzione. La sezione 5.3 della guida di stile Java di Google impone esattamente questo già in fase di scrittura: scrivi gli acronimi come parole comuni, quindi XmlHttpRequest anziché XMLHTTPRequest, e il nome diventa un punto fisso della conversione. Un nome che sopravvive al proprio giro è un nome che puoi far passare senza rischi per un generatore di codice, un ORM, un serializzatore e ritorno.

C'è un caso che la regola a due espressioni continua a sbagliare, e vale la pena conoscerlo perché sembra un fallimento della regola. Gli acronimi a case misto la battono: supportsIPv6 diventa supports_i_pv6, e la forma kebab è supports-i-pv6. La seconda espressione vede la P maiuscola seguita dalla v minuscola e taglia lì, che è esattamente ciò che deve fare ovunque altrove. Nessuna regola di confine che guardi solo il case delle lettere può sapere che IPv6 è un unico token. È l'argomento più forte a favore della regola di Google: scrivi supportsIpv6 fin dall'inizio e il convertitore non dovrà mai indovinare.

Uno slug di URL è una quarta cosa, non kebab-case con qualche passaggio in più

Uno slug sembra kebab-case ma ha tre obblighi in più che un identificatore non ha mai. Deve sopravvivere al passaggio in minuscolo, perché i server confrontano i percorsi URL distinguendo le maiuscole mentre le persone li digitano con leggerezza. Deve sopravvivere alla rimozione degli accenti, perché un percorso con caratteri accentati viene codificato in percentuali e diventa illeggibile. E deve stare in un budget di lunghezza, perché gli slug finiscono in e-mail, materiale stampato e barre degli indirizzi dove un percorso di 200 caratteri è inutilizzabile.

Il passo di rimozione degli accenti è dove le implementazioni ingenue perdono dati in silenzio. La ricetta abituale è normalizzare nella forma decomposta, cancellare i segni combinanti e poi tenere solo lettere, cifre e trattini. Applicata a un titolo francese funziona: Crème Brûlée & Co. — 2026 Edition diventa creme-brulee-co-2026-edition, 28 caratteri. Applicata al tedesco distrugge il testo. Il titolo Größe & Maße: der Überblick esce come gro-e-ma-e-der-uberblick, perché la esse tedesca non ha decomposizione canonica: non viene ripiegata su nulla, viene semplicemente cancellata come ogni altro carattere che non sia una lettera latina.

La correzione è translitterare prima di normalizzare, con una mappa per lingua: la esse tedesca in ss, le vocali con dieresi in oe, ae e ue in tedesco, la o barrata e la a con anello scandinave nei loro equivalenti a due lettere. Con quel passo davanti, lo stesso titolo tedesco dà groesse-masse-der-ueberblick, 28 caratteri e davvero leggibile. Rendi immutabile lo slug risultante una volta pubblicato, limitalo attorno ai 60-80 caratteri tagliando su un confine di parola, e non rigenerarlo mai da un titolo modificato senza emettere un reindirizzamento dal vecchio.

Scegliere, in pratica

Segui l'ospite, non la tua preferenza. Dentro un file Python, snake_case, anche se il JSON che stai analizzando è in camelCase. Dentro un file CSS, kebab-case, anche se i design token sono stati scritti in camelCase. Dentro uno schema PostgreSQL, snake_case, perché il parser ridurrà comunque a minuscolo il tuo camelCase e passerai il resto del progetto a scrivere virgolette doppie.

Converti solo ai confini, e in un unico posto. Se la tua API parla camelCase e il tuo database snake_case, metti un solo livello di mappatura fra i due invece di convertire alla spicciolata a ogni chiamata, e fai di quel livello l'unico codice che conosce la regola a due espressioni. Scrivi gli acronimi come parole ovunque, così la conversione è un punto fisso e nessuno deve più pensarci. E quando generi uno slug, trattalo come un identificatore pubblicato dal momento in cui esce: è l'unica di queste quattro forme che uno sconosciuto incollerà in un messaggio.

Che cosa impone la guida di stile di ciascun ecosistema, e che cosa lo impone davvero: una convenzione, un linter o il parser stesso.
EcosistemaVariabili e funzioniTipi e classiCostantiChe cosa lo impone
Python (PEP 8)snake_caseCapWordsUPPER_SNAKE_CASESolo convenzione; i linter avvisano, l'interprete accetta tutto
Rustsnake_caseUpperCamelCaseSCREAMING_SNAKE_CASEIl compilatore: non_snake_case e non_camel_case_types avvisano di default
GomixedCapsMixedCapsMixedCaps, mai trattini bassiIl compilatore: la maiuscola iniziale rende esportato l'identificatore, quindi il case è semantica, non stile
Java (Google Java Style)lowerCamelCaseUpperCamelCaseUPPER_SNAKE_CASEConvenzione, più la regola esplicita che gli acronimi si scrivono come parole: XmlHttpRequest, non XMLHTTPRequest
JavaScript e TypeScriptcamelCasePascalCaseUPPER_SNAKE_CASENessuna guida ufficiale; la grammatica esclude solo il trattino, perché è l'operatore meno
CSS e HTMLkebab-case per proprietà, classi e proprietà personalizzateIl CSS non ha tipi definiti dall'utente; i nomi degli elementi HTML sono minuscoliproprietà personalizzate in kebab-case, precedute da due trattiniLa grammatica: un nome di proprietà non è mai un'espressione, quindi un trattino al suo interno non può essere un meno - ecco perché calc() richiede spazi attorno ai segni meno
PostgreSQLsnake_case per tabelle e colonnesnake_case per tipi e dominiUPPER_SNAKE_CASE solo per convenzioneIl parser: gli identificatori non quotati vengono ridotti a minuscolo, quindi un nome di tabella in camelCase diventa minuscolo in silenzio a meno di virgolettarlo per sempre
Convertitore kebab-caseConverti qualsiasi testo o identificatore camelCase in kebab-case minuscolo (con trattini).Prova lo strumento

Domande frequenti

Perché posso scrivere background-color in CSS ma non backgroundColor in un foglio di stile?
Perché i nomi delle proprietà CSS sono un vocabolario fisso definito dalla specifica, e la specifica li scrive in kebab-case. Non è che camelCase sia illegale nella grammatica: è che backgroundColor non è il nome di alcuna proprietà, quindi la dichiarazione viene scartata come sconosciuta. Le grafie in camelCase esistono solo nel CSS Object Model, la vista JavaScript di uno stile, dove il trattino sarebbe un segno meno. Due grafie, due grammatiche, una proprietà.
Si legge meglio camelCase o snake_case?
I lavori pubblicati di eye-tracking su questo sono pochi, datati e contestati, e non sostengono alcuna affermazione forte in nessuna direzione: alcuni studi trovano snake_case marginalmente più veloce da leggere, altri trovano che i lettori allenati siano più veloci nello stile che praticano ogni giorno. Ciò che non è contestato è il costo dell'incoerenza dentro una stessa base di codice. Prendi ciò che impone l'ecosistema, fallo rispettare da un formattatore e spendi il budget di discussione su qualcosa che cambi i comportamenti.
Come converto in sicurezza il JSON camelCase di un'API verso un database in snake_case?
Usa la regola di confine a due passaggi, applicala in un solo modulo e fissa la mappatura per ogni nome che contenga un acronimo. La versione pragmatica è tenere una tabella di eccezioni esplicita: un breve dizionario dei quindici o venti nomi di campo del tuo schema sulla cui conversione non vuoi che decida una regex. Quella tabella costa un'ora di scrittura ed elimina l'intera classe di bug, mentre una mappatura puramente algoritmica prima o poi incontrerà un nome come supportsIPv6 e produrrà qualcosa che nessun revisore nota finché una query non restituisce niente.
Posso usare camelCase per i nomi di tabelle e colonne in PostgreSQL?
Puoi, ma solo virgolettando l'identificatore ogni singola volta che compare, per sempre, in ogni query, migrazione, vista e script. PostgreSQL riduce a minuscolo gli identificatori non quotati, quindi una tabella creata come userAccounts diventa useraccounts, e una query successiva su userAccounts la trova solo perché anch'essa si riduce a useraccounts, fino al giorno in cui qualcuno ne virgoletta una e le due smettono di corrispondere. Qui snake_case non è una preferenza di stile: è la forma che attraversa il parser intatta.
Uno slug di URL deve essere solo il titolo in kebab-case?
Quasi, ma con tre aggiunte che il solo kebab-case non dà. Porta tutto in minuscolo, perché un percorso che differisce solo per il case è una risorsa diversa per un server ma la stessa cosa per una persona. Translittera prima di togliere gli accenti, altrimenti caratteri come la esse tedesca svaniscono del tutto invece di diventare ss. E limita la lunghezza su un confine di parola, attorno ai 60-80 caratteri, dato che lo slug verrà incollato in posti senza spazio. Un'ultima regola, estranea al case: una volta pubblicato, non cambiarlo mai senza un reindirizzamento permanente dal vecchio percorso.

Articoli che potrebbero interessarti

Tutte le guide
SpiegazionecamelCase vs snake_case: guida alle convenzioni di denominazione nel codicecamelCase, snake_case, PascalCase e kebab-case spiegati: come appare ciascuno, dove è la convenzione e come sceglierne uno con coerenza.GuidaLa codifica URL spiegata: percent-encoding e dove mordeIl percent-encoding si decide componente per componente, ed è da lì che nasce tutta la confusione. Una barra è lecita in un percorso e va escapata in un valore di query; uno spazio è %20 in un percorso e può essere + in un corpo di form. Ecco gli insiemi esatti della RFC 3986, le tre funzioni JavaScript che non concordano e le trappole.SpiegazioneIl cifrario di Cesare spiegato: come funzionano lo scorrimento e ROT13Il cifrario di Cesare sposta ogni lettera di una quantità fissa. Scopri come funziona lo scorrimento, perché ROT13 è un caso speciale, come cifrare e decifrare a mano e perché il cifrario oggi non offre alcuna sicurezza reale.TutorialCome convertire JSON in CSV: appiattire array di oggetti in righe e colonneUna guida pratica per trasformare un array JSON di oggetti in un file CSV pulito, incluso l'appiattimento dei campi annidati e i casi limite.SpiegazioneTogliere gli accenti rompe la ricerca — finché non lo fai da entrambi i latiRipiegare i segni diacritici è un passaggio di normalizzazione, e la normalizzazione funziona solo se la stessa funzione gira sull'indice e sulla query. NFC contro NFD con i punti di codice in chiaro, e le lettere — ø, ł, ß, œ, ı — che escono dal ripiegamento intatte.SpiegazioneGli hashtag sono un indice di ricerca, non un megafonoUn hashtag rende un post trovabile in una ricerca, che è un compito diverso dal farlo circolare. Modella la visibilità che un tag compra davvero e la risposta esce da sé: il valore di un tag è il suo numero di spettatori per post pubblicato, non il suo volume — quindi un tag molto popolare rende quasi nulla e uno specifico venticinque volte tanto.

Strumenti correlati

Fonti

Hai notato un errore in questo articolo?