Ultimamente ho il feed invaso di una sola roba: WordPress fa schifo.
Non sono mai stato un detrattore a spada tratta, ha i suoi contro e i suoi pro. Però quello che ho fatto in questi ultimi mesi è un bel benchmark sul "fa schifo o no".
TL;DR di quello che penso oggi, dopo il lavoro fatto fino a qui: WordPress è sicuramente uno strumento potente, vecchio, e forse un po' fuori tempo. Se avete un sito in WordPress dovete cambiarlo? Nell'80% dei casi no. Forse dovete solo cambiare webagency.
Ma avrò modo di entrare nel dettaglio. Lesssssgo to the main topic.
Come sono finito a fare il webmaster di WordPress
Era questo inverno ed ero sul lago di Como con la mia ragazza, la quale ha un'attività: un bellissimo residence a Marciana Marina, all'Isola d'Elba. Chiama suo zio, parlano, e a un certo punto si gira verso di me con la frase fatidica.
"Ma tu hai voglia di vedere come mai non funziona il nostro sito?"
Scrivo software complessi dalla mattina alla sera, gestisco infrastrutture enormi ogni giorno. Che vuoi che sia controllare un sito. Accetto.
Ed ecco ragazzi come ho conosciuto vostra ma… ah no, aspetta, siamo su un blog tech che pubblico su LinkedIn, non è la frase giusta. Ma comunque è così che sono finito a fare il webmaster di WordPress.
Intendiamoci: con WordPress chiunque ci ha battuto la testa qualche volta, e chi dice di non averlo mai fatto mente. Io personalmente ci ho avuto assai a che fare: risoluzione di piccoli problemi, migrazioni, insomma le robe che chi usa WordPress di solito non fa. Quindi non sono di certo un toro su WordPress, ma nemmeno uno scappato di casa.
Il sito è questo: intur.it.
Quello che trovi sempre
Inizio a metterci mano e lo scenario è il classico: plugin da aggiornare, major di WP indietro, una fraccata di plugin che fanno mezza cosa ciascuno.
Ma non ero lì per quello. Respiro, spallucce, proseguo.
Il problema da risolvere era un altro, e aveva un nome: Caldera Forms.
Il problema vero: un URL che era cambiato sotto i piedi
L'Intur, come ogni struttura ricettiva, ha un booking engine su cui si appoggia. In questo caso SimpleBooking. E come nel più classico dei casi, i nostri bro di SB hanno deciso di cambiare i query param della ricerca. Una semplice modifica all'API di ricerca delle disponibilità.
Semplice per loro. Dall'altra parte c'è un form che, da un giorno all'altro, non porta più da nessuna parte: l'utente compila, clicca, e arriva su una pagina di ricerca che non ha capito niente di quello che gli è stato chiesto.
Il pezzo più importante del funnel di un residence, quello dove il visitatore dice "ok, vediamo quanto costa", era diventato un vicolo cieco.
Il tour della UI di Caldera
Ok, andiamo a vedere la UI di Caldera, eeeeeeeeeeeeee beh, che dire.
Vuoi un datepicker? Non c'è.
Vuoi aggiungere un campo che faccia qualcosa che non era già stato deciso in partenza? Eh no, non c'è.
Ah, vuoi una UI in linea con gli standard, tipo Booking o Airbnb? AHAHAHAHHAHAHAHAH. Sciocco. Scordatela: dieci tap per una prenotazione.
Mi ricompongo, scusate. Insomma: avevo già il vomito dopo dieci minuti, allego screen.

Non era colpa di Caldera
E qui però va detta la cosa onesta, quella che ho capito solo dopo aver aperto il cofano per bene.
Il form non poteva funzionare. Non perché Caldera fosse fatto male, ma perché quel lavoro non si poteva fare con un form. L'URL a cui bisogna arrivare è questo:
https://www.simplebooking.it/ibe/search
?hid=4859&lang=IT&cur=EUR
#/q&guests=2,12&in=2026-10-03&out=2026-10-10&coupon=
Guardate dov'è il cancelletto. hid, lang e cur stanno nella query string, come Dio comanda. Ma date e ospiti stanno dopo il fragment, dentro un secondo giro di parametri che vive nell'hash.
Un <form method="get"> sa fare una cosa sola: prende i suoi campi e li impacchetta nella query string, prima del cancelletto. Quello che c'è dopo l'hash non lo tocca — non è mai stato progettato per toccarlo, e infatti il fragment non viene nemmeno spedito al server.
Quindi non è che Caldera fosse pigro, o che mi mancasse il campo giusto. Nessun form builder al mondo può costruire quell'URL, perché non è un form submit: è una stringa da comporre in JavaScript e su cui fare una redirect.
Questa è la parte che mi ha convinto a smettere di cercare plugin.
Le alternative erano tutte a pagamento
Provo a vedere le alternative, per scrupolo. Tutte a pagamento. (Che SimpleBooking avesse un widget suo l'ho scoperto mesi dopo, e non da loro. Ci arrivo in fondo.)
Non avrei cacciato un centesimo. Quindi cosa si fa quando non si vuole pagare ma si vuole qualcosa? DIY.
E siamo nell'era del vibecoding, no? Un paio di prompt vista lago e pum pem pam, datepicker scintillante e funzionante.
No. Chiaramente non funziona così. Vorrei farvi il mio solito pippone sul vibecoding, che spoiler non spoiler non esiste, ma ve lo risparmio: merita un pezzo a parte.
In locale funzionava
Però la strada era quella. Un po' di tutorial su come funzionano i plugin di WordPress, roger, compreso il funzionamento, inizio a scrivere. Prime prove in locale con Docker e un WP di test: ottimo, funziona tutto.
Prendo il plugin, lo carico in produzione. E bam.

BELLO VIBECODARE WOOOOOH!
Poco male: avevo fatto un backup dall'hosting. Mentre schifo WordPress vado a ripristinarlo. Downtime totale: 2 minuti.
Per questo dico che il vibecoding non esiste: la differenza tra un pomeriggio andato e due minuti di disagio sta in una cosa che ho fatto prima, che nessun llm ti avrebbe scritto di fare durante la tua sessione di prompting.
WordPress non sandboxa i plugin
Sul perché si sia rotto: JavaScript e PHP sono amici il giusto, e WordPress non sandboxa proprio bene i plugin.
È il dettaglio che spiega perché un errore in un file da trecento righe si porta giù tutto il sito e non solo se stesso. Un plugin non gira in un contenitore suo: viene incluso nello stesso processo che sta costruendo la pagina. Un errore fatale lì non è "il mio widget non compare", è il sito non risponde. Compresa l'area di amministrazione, che è esattamente il posto da cui vorresti disattivare il plugin che ha causato il problema.
Ed è per questo che il backup non è prudenza, è parte del ciclo di sviluppo.
E niente, lezione appresa. Dopo qualche iterazione tutto funziona.
Cosa c'è dentro, dopo qualche iterazione
Il risultato è un plugin da un file solo, arrivato alla versione 1.4, che si mette in pagina con uno shortcode:
[booking_widget app="home"]
Zero dipendenze aggiunte al progetto: jQuery c'era già perché se lo porta dietro il tema, daterangepicker e moment.js arrivano da CDN. Tre cose dentro meritano di essere raccontate.
Il range picker, e i dieci tap che diventano due
Il punto non era "mettere un calendario". Era smettere di trattare arrivo e partenza come due campi indipendenti, perché non lo sono: sono un intervallo. Un range picker al posto di due date picker, e già metà del problema dei dieci tap sparisce.

Il resto è nelle opzioni, e sono cinque righe che valgono più di tutto il resto del codice:
autoUpdateInput : false,
autoApply : true,
minDate : moment(),
drops : isMobile ? "up" : "down",
linkedCalendars : false,
autoApply toglie di mezzo il bottone "Conferma": scegli la seconda data e il calendario si chiude. Un tap in meno, e soprattutto una cosa in meno da capire.
minDate: moment() rende impossibile prenotare nel passato, che sembra ovvio e nel form di prima non lo era.
drops: "up" su mobile fa aprire il calendario verso l'alto. Sembra una pignoleria estetica: è la differenza tra vedere il calendario e vedere metà calendario tagliato dal fondo dello schermo, con la gente che non capisce perché non riesce a scegliere la data.
linkedCalendars: false slega i due mesi affiancati. Chi arriva il 30 settembre e parte il 2 ottobre non deve rincorrere niente.
E autoUpdateInput: false dice al picker di non scrivere lui nel campo, così il formato della data lo decido io: corto su mobile, lungo su desktop, con un em dash in mezzo invece di un trattino.
Nessuna di queste è una funzione che ho scritto io. Sono decisioni. È il tipo di lavoro che un plugin generico non può fare per te, perché richiede di sapere come prenota la gente in un residence a ottobre.
I bambini hanno tutti dodici anni
SimpleBooking vuole gli ospiti come lista di età: guests=2,12,12 vuol dire due adulti e due bambini di dodici anni. Ora, all'Intur i bambini contano come ospiti e l'età non incide sul prezzo. A me l'età non serve, e non voglio chiederla a un utente che sta solo cercando di capire quanto costa una settimana.
Quindi:
var childrenStr = "";
for (var i = 0; i < numChildren; i++) { childrenStr += ",12"; }
var guests = adults + childrenStr;
Per ogni bambino, un dodici. Fisso.
Sembra un workaround ma è aver capito che quel dato, in questa struttura, non serve a nessuno. È il dominio che decide il codice. Se avessi seguito l'API mi sarei ritrovato un selettore di età per bambino, tre tap in più a testa, e un campo che non cambia niente per nessuno.
Una direttiva aziendale mi ha fatto scrivere tre righe invece di trenta.
Due dettagli
Il widget può comparire più volte sulla stessa pagina: header, corpo, fondo. Quindi c'è un contatore statico che dà a ogni istanza il suo ID e la sua funzione di init:
static $instance = 0;
$instance++;
$widget_id = 'ibw-widget-' . $instance;
Senza quello il secondo widget ruba i click al primo, ed è un bug che vedi solo in produzione, su una pagina che non stavi guardando.
E poi il pezzo di WordPress che mi ha fatto perdere mezz'ora, che lascio qui perché magari la risparmio a qualcuno: wp_add_inline_style non funziona da uno shortcode. Il motivo è banale e cattivo: wp_head() gira prima di the_content(), quindi quando il tuo shortcode viene eseguito la testa della pagina è già stata stampata e il tuo CSS accodato non ha più un posto dove andare. La soluzione è stampare il <style> a mano dentro l'output, una volta sola, con un flag statico.
Non è elegante. È che la piattaforma è vecchia e le cose vecchie hanno un ordine di esecuzione che non puoi discutere.
Cose da aggiustare, e una che ho scoperto tardi
È in produzione da aprile e funziona. That's it.
La cosa interessante è arrivata dopo. A una certa scopro che SimpleBooking ha un widget suo: si chiama Syncro Box. Non l'ho scoperto da loro: l'ho scoperto andando a guardare i siti di altri alberghi che usano lo stesso motore, per capire come avevano risolto.
Quindi me lo sono studiato, con l'idea di buttare il mio e usare il loro. E su tre cose il loro vince, senza discussione:
- Una dipendenza sola. Uno script. Io ne carico tre: jQuery, moment, daterangepicker.
- Diciassette lingue, gratis. Le mie tre stanno in un array scritto a mano.
- Le regole dell'albergo le legge dal backoffice. Soggiorno minimo, preavviso minimo: le sa. Il mio no, e ci arrivo.
E però l'ho scartato, per un motivo solo: la sezione Styles espone i colori e i font, e basta. Nessun parametro di layout. Il loro widget è impilato in verticale e la barra orizzontale che ho in home non è una cosa che si ottiene configurando, semplicemente non esiste l'opzione di averla.
Ho valutato anche le due vie di mezzo, e sono peggio di entrambe le strade pulite. Riscrivere il loro markup col mio CSS vuol dire firmare per un sito che si rompe a ogni loro deploy, senza preavviso e senza colpa. Pilotare il loro widget dalla mia interfaccia richiederebbe un'API che non c'è: mi accoppierei al loro DOM, cioè la stessa scommessa con più passaggi.
Quindi resta il mio, con due limiti veri.
Il calendario non sa le regole dell'albergo. C'è minDate: moment() e nient'altro, quindi si possono scegliere date che poi l'IBE rifiuta. È una scelta: soggiorno minimo e preavviso cambiano per stagione e per camera, stanno nel backoffice, e replicarli nello shortcode significa bloccare date prenotabili il giorno che l'albergo li cambia. Meglio un errore dentro l'IBE, dove quella regola vive per davvero, che una data buona negata in home. Tanto poi ho visto che gli errori dentro l'IBE, sono ben gestiti quindi non ci si accorge della differenza al netto di fare un confronto mirato.
L'URL della ricerca è ricostruito a mano, guardando come lo fa SimpleBooking. È l'unico punto dove il plugin può rompersi davvero, ed è esattamente come si era rotto Caldera. Mi consola solo che non sarebbe un guasto silenzioso: quel datepicker viene usato costantemente anche internamente per capire disponibilità nell'immediato ed avere un range di prezzo subito. Quindi, nel giro di poco qualcuno si accorgerebbe che é rotto e saprei come aggiustarlo.
In ogni caso ve lo lascio su github, é una roba semplice, non aspettatevi chi sa che, ma se vi é utile son contento.
Il conto
Ci ho lavorato una manciata di ore. Le ho lavorate bene, perché è quello che faccio di mestiere e non le ho fatturate perché è l'attività della famiglia della mia ragazza.
Quindi mettiamolo in chiaro prima che me lo scriva qualcuno nei commenti: questo è un unfair advantage. Un residence a caso non ha in casa uno che gli scrive un plugin WordPress nel tempo libero.
Ed è qui che la storia diventa interessante, perché la domanda giusta non è "quanto ha risparmiato l'Intur". La domanda vera é: e chi non ce l'ha, a chi si affida?
WordPress no, di certo. WordPress alla domanda "mi serve un campo data che funzioni" risponde con un carrello, sempre, per qualunque cosa.
Un'agenzia che te lo fa su misura è la risposta ovvia, ma nella maggior parte dei casi è anche quella dove ti fregano: perché quel lavoro lì, visto da fuori, sono due input e un pulsante, e non c'è modo che tu possa distinguere il preventivo onesto da quello gonfiato di dieci volte. Io il preventivo lo saprei leggere. Ma se lo sapessi leggere non starei chiedendo un preventivo.
La strada più sensata è quella che ho trovato per ultima: chiedere al proprio booking engine. Un widget ce l'hanno. Magari non ti piace: a me non piaceva, ma esiste, lo mantengono loro, parla diciassette lingue e sa le regole del tuo albergo. E molto probabilmente è già incluso nel contratto che stai pagando: l'IBE è attivo da anni e non mi risulta una voce a parte nel listino.
Il resto è la parte che mi gira in testa da mesi. Quelle ore le paghi una volta e ti resta una cosa tua: un file, che leggi, che modifichi, che sposti su un altro sito. L'alternativa la paghi ogni anno e non ti resta niente, il plugin per il form, quello per le traduzioni, la versione Pro del builder, il widget del fornitore, e il giorno che smetti di pagare torni al punto di partenza.
Il fai-da-te non è gratis: costa competenza, che è la valuta più cara che c'è. Ma non è un canone.
E allora la domanda con cui vi lascio è questa: non è che ci stiamo abituando a non avere più niente di nostro, solo perché non vogliamo pagare subito il prezzo pieno, che sia in competenza o in pecunia?