Sviluppo 17 min di lettura

Astro e Cloudflare: un sito veloce con poca zavorra

Il sito di un tecnico racconta come lavori prima che qualcuno apra ispeziona elemento del browser. Qui racconto lo stack e il ragionamento dietro ogni scelta.

Indice degli argomenti

Un sito personale può essere due cose: po’ esse fero, o po’ esse piuma. 😅

Scherzi a parte, può essere un biglietto da visita elegante, caricato online e lasciato lì.

Oppure un piccolo sistema che dice qualcosa su come lavori quando le cose devono durare. E io ho scelto la seconda.

Non perché un sito personale debba essere per forza complicato. Anzi: questo sito proprio non lo è. Niente database, niente pannello di amministrazione, niente runtime JavaScript grande quanto una casa, niente plugin messi lì… “ché non si sa mai”.

Sotto il cofano qui ci sono Astro, Cloudflare, Tailwind CSS v4, GitHub Actions, Markdown, qualche script Node e qualche scelta ragionata.

Non voglio scrivere la lista della spesa della tecnologica utilizzata. Voglio concentrarmi sul criterio: scegliere meno pezzi, ma quelli giusti. Spostare la complessità solo dove serve. Levare il resto.

È lo stesso modo in cui ragioniamo in Encodia quando un progetto web deve essere veloce, mantenibile e governabile anche dopo il lancio.

Questo post racconta come ho costruito davideprevosto.it, ma si rivolge a chi deve prendere una decisione simile: agenzie, PMI, team marketing, founder e aziende che hanno bisogno di un asset web solido, non dell’ennesimo sito che sembra moderno per due mesi ma che poi a metterci mano è un dramma.

Non si tratta di un tutorial passo passo. È una visita guidata, con qualche scelta che rifarei identica a come l’ho fatta e qualche compromesso che terrei d’occhio se il progetto crescesse.

L’obiettivo non era usare Astro

Usare Astro non è il punto. Il punto era costruire un sito che facesse bene poche cose:

  • caricare in fretta;
  • essere leggibile da browser, motori di ricerca e strumenti AI;
  • pubblicare contenuti senza passare da un database;
  • avere una pipeline di deploy robusta;
  • restare semplice da modificare, anche tra sei mesi.

Astro e Cloudflare sono arrivati dopo, come conseguenza.

In Encodia non partiamo quasi mai da “sviluppiamolo con X”. Partiamo da vincoli, obiettivi, vita prevista del progetto, persone che lo useranno, budget di manutenzione, rischio tecnico.

A volte la risposta è WordPress, magari con Roots. A volte, spesso, è Laravel con Livewire o Filament. A volte è Statamic. A volte è un’app web scritta con Angular. In questo caso la risposta era: HTML statico generato bene, distribuito in CDN, con pochissimo JavaScript e una pipeline controllata.

Tradotto: Astro + Cloudflare.

Perché non WordPress, Next.js o un SaaS

La domanda non è “quale tecnologia è più bella?”. Gli sviluppatori ci cascano volentieri qui e poi iniziano a discutere di cose che al cliente interessano… il giusto.

La domanda è: qual è la tecnologia meno ingombrante che risolve bene questo problema? Per questo sito, WordPress sarebbe stato decisamente troppo.

WordPress lo uso e lo conosco bene. Quando serve un ecosistema di plugin, integrazioni già pronte o particolari esigenze editoriali, può ancora essere la scelta corretta. Ma qui avrebbe significato mettere in piedi database, PHP, aggiornamenti continui, plugin, hardening e manutenzione per risolvere un problema che non li richiedeva.

Next.js è un ottimo framework, ma è nato per interfacce React, rendering ibrido e applicazioni più dinamiche. Per un sito di contenuti con blog in Markdown, aggiungere React nel client solo per renderizzare testo non aveva senso.

I SaaS come Webflow, Framer o Wix sono comodi, ma se un sito parla di qualità tecnica, performance, SEO e manutenzione, pubblicarlo su una piattaforma dove non controllo le scelte che contano sarebbe stato incoerente.

Astro ♥️ risolve il problema in modo diretto: genera HTML statico a build-time, zero JavaScript lato client per default, integra Markdown nativo e si appoggia bene alla CDN e al runtime Cloudflare.

Non è la scelta giusta per tutto. Ma qui era quella giusta, per me.

Cosa si ottiene da uno stack così

Vista da fuori, questa può sembrare una scelta da sviluppatore: Astro, Cloudflare Workers, Content Collections, middleware, immagini Open Graph generate via Satori.

Il tema invece è uno solo: controllo.

Controllo sulla performance, perché il browser riceve HTML già pronto e non deve aspettare “un’app” qualsiasi per capire cosa mostrare.

Controllo sulla manutenzione, perché i contenuti sono file versionati in Git e la build fallisce se un frontmatter è scritto male.

Controllo sulla pubblicazione, perché un post programmato non dipende da un cron nascosto dentro un server, ma da un processo esplicito.

Controllo sulla SEO tecnica, perché title, description, canonical, sitemap, RSS, dati strutturati, FAQ e immagini social sono parte del codice, non una speranza affidata a un plugin.

Controllo sul costo operativo, perché Cloudflare distribuisce asset statici e Worker senza dover mantenere una macchina, un database e una serie di servizi sempre accesi.

Ah sì, è anche una roba figa. Ma è figa perché fa il suo, senza chiedere attenzione ogni settimana.

Astro 6: la configurazione

Il cuore di tutto è astro.config.mjs. Vale la pena leggerlo perché ogni sezione racconta una scelta.

astro.config.mjs
import { execSync } from 'node:child_process';
import { URL } from 'node:url';
import { defineConfig, envField } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import cloudflare from '@astrojs/cloudflare';
import expressiveCode from 'astro-expressive-code';
import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers';
export default defineConfig({
site: 'https://davideprevosto.it',
build: {
inlineStylesheets: 'always',
},
markdown: {
rehypePlugins: [rehypeExternalLinks],
},
env: {
schema: {
BLOG_ENABLED: envField.boolean({
context: 'server',
access: 'public',
optional: true,
default: false,
}),
},
},
integrations: [
expressiveCode({
/* ... */
}),
],
vite: {
plugins: [tailwindcss()],
define: {
__APP_VERSION__: JSON.stringify(getLatestGitTag()),
},
},
adapter: cloudflare(),
});

Tre scelte meritano un approfondimento.

inlineStylesheets: 'always'

Astro di default genera file CSS separati e li collega con <link>. Con inlineStylesheets: 'always', il CSS viene iniettato direttamente nell’<head> di ogni pagina.

Sul primo caricamento, un file CSS esterno costa una richiesta HTTP aggiuntiva con latenza. Inline non costa niente perché arriva con l’HTML.

Lo svantaggio è che il CSS non beneficia di cache separata. Su un sito statico, però, con CSS piccolo e distribuzione Cloudflare di mezzo, il compromesso è sensato: meno richieste e rendering più rapido.

Tutti i link verso domini esterni nei file Markdown vengono processati a build-time per aggiungere automaticamente target="_blank" e rel="noopener noreferrer".

Niente pacchetto esterno. Bastano poche righe:

function rehypeExternalLinks() {
return (tree) => {
visitNodes(tree, (node) => {
if (node.type !== 'element' || node.tagName !== 'a') return;
const href = node.properties?.href;
if (typeof href !== 'string' || !isExternalHref(href)) return;
node.properties.target = '_blank';
node.properties.rel = mergeRel(node.properties.rel, ['noopener', 'noreferrer']);
});
};
}

Scrivo [Astro](https://astro.build) in Markdown e la build produce l’HTML corretto.

Non è questione di risparmiare tre attributi. È spostare una regola ripetitiva nel posto giusto, così non ci penso più.

__APP_VERSION__ e git tag

function getLatestGitTag() {
try {
return execSync('git describe --tags --abbrev=0', { stdio: ['ignore', 'pipe', 'ignore'] })
.toString()
.trim();
} catch {
return '0.0.0-unknown';
}
}

A build-time, Astro legge l’ultimo tag Git del repository e lo inietta nel bundle come costante __APP_VERSION__.

Il footer mostra la versione corrente del sito senza che io debba aggiornare manualmente una stringa.

Sembra una finezza, ma si tratta di tracciabilità: quando qualcosa cambia online, posso collegarlo a una release. Nei progetti veri, quelli dei clienti, questa differenza conta molto più di quanto sembri durante lo sviluppo.

Tailwind CSS v4: il tema nel CSS

Il sito usa Tailwind CSS v4. Rispetto alla v3 cambia il modo di configurare il tema: non c’è più tailwind.config.js, perché colori, tipografia, spaziatura e animazioni vivono in src/style.css come variabili CSS native.

src/style.css (estratto)
@import 'tailwindcss';
@theme {
--color-accent: #6f3a1f;
--color-bg: #fafaf7;
--color-text: #111110;
/* ... */
}
:root[data-theme='dark'] {
--color-accent: #f59e0b;
--color-bg: #0a0a0a;
--color-text: #fafafa;
}

Il tema chiaro è il default. Il tema scuro sovrascrive le stesse variabili quando data-theme="dark" è presente sull’elemento root.

Niente duplicazioni, niente classi dark: sparse ovunque, niente configurazione separata che devi ricordarti di aprire quando tocchi il design system.

L’integrazione con Vite (versione 7 perché la 8 non è ancora supportata da Astro mentre scrivo) è ridotta all’osso:

astro.config.mjs
vite: {
plugins: [tailwindcss()],
},

Content Collections: il blog in Markdown, ma con guardrail

Il blog è costruito sulle Astro Content Collections v2. Ogni post è un file Markdown che risiede dentro src/content/blog/<categoria>/.

Lo schema è validato con Zod a build-time:

src/content.config.mjs
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({
title: z.string().min(1).max(90),
description: z.string().min(1).max(180),
publishDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
category: z.enum(BLOG_CATEGORY_SLUGS),
tags: z.array(z.string().min(1)).default([]),
excerpt: z.string().optional(),
draft: z.boolean().default(false),
canonical: z.string().url().optional(),
faq: z
.object({
/* ... */
})
.optional(),
cta: z
.object({
/* ... */
})
.optional(),
}),
});

Se un post ha un campo mancante o un valore fuori schema, la build fallisce prima di andare in produzione.

È il tipo di controllo che in un CMS spesso deleghi a plugin, validazioni editoriali o semplice disciplina. Qui vive nel codice.

Qualche numero che non è casuale:

  • title max 90 caratteri: limite pensato per title tag leggibili e SEO friendly;
  • description max 180 caratteri: una meta description non deve diventare un paragrafo;
  • faq e cta come oggetti annidati: il renderer sa trasformarli in markup semantico senza reinventare HTML dentro il Markdown;
  • draft: true: le bozze restano fuori dalla build pubblica.
  • Eccetera…

Non ho un CMS, ma ho regole chiare. Per un sito come il mio, spesso è meglio così che avere un pannello pieno di opzioni che non riesci a gestire.

Cos’è il frontmatter?

Il blocco di metadati in cima a ogni file Markdown, racchiuso tra ---. Contiene titolo, data, categoria e tutti gli altri campi del post. Se manca un campo obbligatorio o il valore è nel formato sbagliato, la build si blocca!

Pubblicazione programmata

I post con publishDate futura vengono filtrati a build-time:

const posts = (await getCollection('blog')).filter(
(post) => !post.data.draft && post.data.publishDate <= new Date(),
);

Nessun database, nessun webhook per ogni articolo, nessuna API privata per chiedere “è ora di pubblicare?”.

Il processo di build decide cosa è visibile. L’unica cosa che serve è ricostruire il sito nel momento giusto.

E qui entrano in gioco le GitHub Actions.

Expressive Code: i blocchi di codice devono essere leggibili

Per i blocchi di codice ho scelto Expressive Code, un’integrazione Astro che va oltre il syntax highlighting base.

astro.config.mjs
expressiveCode({
plugins: [pluginLineNumbers()],
themes: ['min-light', 'github-dark-dimmed'],
themeCssSelector: (theme) =>
theme.name === 'min-light' ? "[data-theme='light']" : "[data-theme='dark']",
useDarkModeMediaQuery: false,
defaultProps: {
showLineNumbers: true,
wrap: false,
},
});

Il tema del blocco di codice segue il tema del sito tramite il selettore CSS data-theme, senza media query e senza JavaScript aggiuntivo.

Funzionalità che uso nei post:

  • titolo del file (title="file.ts") nel tab superiore;
  • numeri di riga attivati di default;
  • marker su righe specifiche per evidenziare parti rilevanti;
  • diff syntax (ins/del) per mostrare cosa cambia.

Perché perderci tempo?

Perché in un post tecnico il codice non è decorazione: è il contenuto. Deve essere leggibile.

Deploy su Cloudflare

Il sito gira sul runtime Cloudflare tramite l’adapter ufficiale @astrojs/cloudflare.

package.json (script npm)
{
"scripts": {
"build": "(git fetch --unshallow --tags origin || git fetch --tags origin) 2>/dev/null; ASTRO_TELEMETRY_DISABLED=1 astro build",
"deploy": "npm run build && wrangler deploy",
"preview": "npm run build && wrangler dev"
}
}

Lo script build fa una cosa poco scenografica ma utile: prima di lanciare Astro prova a recuperare i tag Git dal remote.

In CI il repository può essere clonato in modalità shallow, quindi git describe --tags potrebbe non trovare nulla. Il fetch rende disponibile la storia necessaria per calcolare __APP_VERSION__. Se non riesce, la build continua comunque e usa il fallback.

Così la build si comporta allo stesso modo in locale e in produzione.

Caching su Cloudflare

Il file public/_headers configura le policy di cache direttamente in CDN:

public/_headers
/fonts/*
Cache-Control: public, max-age=31536000, immutable
/img/*
Cache-Control: public, max-age=31536000, immutable
/_astro/*
Cache-Control: public, max-age=31536000, immutable

Font, immagini e asset di build hanno cache di un anno con flag immutable. Cloudflare li serve dalla CDN senza coinvolgere il Worker.

L’HTML non ha la stessa cache aggressiva, perché il contenuto deve riflettere pubblicazioni e aggiornamenti. Ma è già statico, quindi resta veloce.

X-Content-Type-Options: nosniff è presente su tutti i file: impedisce al browser di indovinare il content-type di un asset e di eseguire qualcosa che non dovrebbe.

Content negotiation via middleware

Una cosa che non si vede: alcune pagine possono essere richieste in Markdown puro tramite header Accept o query string.

src/middleware.ts (logica essenziale)
if (ctx.url.searchParams.get('format') === 'md') {
ctx.locals.prefersMarkdown = true;
}
const chosen = preferredType(ctx.request.headers.get('accept'));
ctx.locals.prefersMarkdown = chosen === 'text/markdown';

Una richiesta a davideprevosto.it/sviluppo/astro-cloudflare-sito-veloce-senza-zavorra/?format=md restituisce il sorgente Markdown della pagina.

È utile per LLM, tool di scraping, archiviazione, lettura automatizzata o per chi vuole analizzare il contenuto senza attraversare l’HTML renderizzato.

Il middleware aggiunge Vary: Accept, così le cache intermedie mantengono versioni separate.

Il sito ha anche un file llms.txt e una versione Markdown della bio su /davide-prevosto.md.

Non è una garanzia di indicizzazione perfetta. Però è un segnale: chi vuole leggere il contenuto senza attraversare l’HTML, può farlo.

GitHub Actions: lo faccio lavorare di notte

Il blog supporta post con data futura. Il meccanismo è semplice: il processo di build li esclude finché la data non è raggiunta.

Per farli apparire al momento giusto serve un rebuild periodico.

.github/workflows/scheduled-publish.yml
name: Scheduled publish
on:
schedule:
- cron: '0 4 * * *' # 04:00 UTC = 06:00 ora italiana
workflow_dispatch: # trigger manuale dalla UI GitHub
jobs:
trigger-deploy:
runs-on: ubuntu-latest
steps:
- name: Trigger Cloudflare Pages rebuild
run: curl -X POST "${{ secrets.CF_DEPLOY_HOOK_URL }}"

L’Action manda un POST a un Deploy Hook di Cloudflare Pages. Cloudflare avvia il processo di build nel suo ambiente, con le variabili già configurate, e pubblica il risultato.

workflow_dispatch permette di forzare il rebuild dalla UI GitHub.

Niente server sempre acceso. Niente cron job dimenticato su una macchina. Niente “aspettavo che qualcuno cliccasse pubblica”.

Solo una pipeline esplicita.

Font: non li carico dalla CDN di Google

I font sono serviti dal sito stesso, non da Google Fonts.

src/style.css
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url('/fonts/inter-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, /* ... */;
}

Due font: Inter (spoiler, sì il font lo ha scelto l’AI, ma non è detto che non lo sostituirò) per il testo e JetBrains Mono per il codice.

Entrambi WOFF2, caricati con font-display: swap e con unicode-range limitato al subset latin, così il browser scarica meno roba.

Il motivo per non usare Google Fonts non è solo performance. Ogni richiesta a fonts.googleapis.com è una dipendenza esterna e un dato che lascia il sito.

Qui non aveva senso fare diversamente.

I font sono in public/fonts/, cache di un anno su Cloudflare, nessuna chiamata a terzi per disegnare una lettera.

Facile? Sì.

Trascurabile? Forse, ma mi piaceva fare così.

Immagini OG: generate via Satori

Ogni post del blog può avere un’immagine di anteprima Open Graph generata automaticamente con Satori.

Satori converte HTML/JSX in SVG. Poi Sharp converte lo SVG in PNG.

scripts/generate-og-images.js (schema del layout)
const svg = await satori(
{
type: 'div',
props: {
style: { background: '#0a0a0a', width: '2400px', height: '1260px' /* ... */ },
children: [
// logo in alto
// titolo e description al centro
// categoria + autore in basso
],
},
},
{
width: 2400,
height: 1260,
fonts: [
/* Inter 400, 500, 800 */
],
},
);

L’output è un PNG a 2400x1260 px, risoluzione 2x per display Retina.

Lo script accetta uno slug opzionale per rigenerare un solo post:

Terminal window
node scripts/generate-og-images.js # tutti i post
node scripts/generate-og-images.js astro-cloudflare-sito-veloce-senza-zavorra # un solo post

Le immagini finiscono in public/img/og/<categoria>/<slug>.png e vengono servite come asset statici.

Il frontmatter del post può specificare un excerpt dedicato per l’immagine OG: testo diverso dalla description SEO, scritto per funzionare meglio quando il link viene condiviso.

Questa è una di quelle parti che molti rimandano perché “tanto è solo social”.

Poi scopri che molti ti vedono per la prima volta da quella card su LinkedIn o WhatsApp.

JavaScript: quasi niente

Il sito non usa nessun framework JavaScript lato client.

Il file src/main.js, meno di 10 KB prima della minificazione, gestisce in vanilla JS quello che serve:

  • scroll reveal con IntersectionObserver;
  • parallax leggero, disabilitato se l’utente preferisce meno movimento;
  • toggle tema chiaro/scuro, salvato in localStorage;
  • form contatti, con POST all’endpoint /api/contact;
  • navigazione mobile, con aria-expanded e chiusura con Escape;
  • anni di esperienza, calcolati da un attributo data-years-from invece di scolpire il numero nel codice.

Astro garantisce che questo script sia il solo JavaScript globale nell’HTML finale.

Il risultato è semplice: il browser riceve HTML, CSS inline e un file JS piccolo.

Meno JavaScript da caricare, meno cose da rompere.

Struttura del repository

src/
├── components/ # Componenti Astro riutilizzabili
├── content/
│ └── blog/ # Post Markdown organizzati per categoria
├── layouts/ # Layout Astro (BlogLayout.astro, ecc.)
├── lib/ # Utility TypeScript: SEO, blog, tassonomia, contenuti
├── pages/ # Routing basato su file
│ ├── index.astro # Homepage
│ ├── blog/ # Listing, [slug].astro, categorie, tag
│ ├── api/ # Endpoint SSR (contact.js)
│ └── *.xml.js # Sitemap, RSS
├── main.js # JavaScript client-side
├── style.css # Design system + Tailwind v4
└── middleware.ts # Content negotiation

Le pagine del blog sono pre-renderizzate.

L’unica parte dinamica è l’endpoint /api/contact, che deve ricevere POST dal form. Astro permette questa convivenza senza trasformare tutto il sito in un’app server-side.

Anche qui il criterio è lo stesso: statico dove si può, dinamico dove serve.

Cosa rifarei uguale, cosa terrei d’occhio

Rifarei uguale:

  • Astro come framework, perché fa esattamente quello che serve, senza aggiunte inutili;
  • Cloudflare come runtime e distribuzione, perché latenza bassa e infrastruttura globale senza gestire server sono un vantaggio reale;
  • Tailwind CSS v4, perché colori e spaziatura vivono nel CSS, non in un file di configurazione separato;
  • font auto-ospitati, perché si guadagna su tutti e tre i fronti: performance, privacy e controllo;
  • niente database, perché non c’era nulla che richiedesse un database;
  • Markdown validato, perché scrivere Markdown non vuol dire lavorare senza regole.

Non lo so Rick…

  • il form contatti che invia una notifica al mio canale Discord è funzionale, ma se il volume crescesse servirebbe un flusso più strutturato;
  • la generazione delle immagini OG è uno script separato, quindi è facile dimenticarsi di rigenerare le immagini;
  • un sito statico funziona benissimo finché il contenuto e le interazioni lo permettono. Se domani servissero area riservata, workflow editoriali complessi o logiche applicative, l’architettura andrebbe ripensata.

Dove si vede la differenza

Un sito tecnico dice qualcosa su come lavori prima ancora di aprire “ispeziona elemento” del browser.

Dice se sai scegliere. Dice se sai togliere. Dice se hai pensato al deploy, alla cache, alla SEO, ai contenuti, alla manutenzione, all’accessibilità, alla leggibilità del codice.

L’ho costruito come vorrei fosse costruito un progetto che mi viene affidato: con meno rumore possibile, con scelte tracciabili, con una pipeline chiara e senza lasciare grane per dopo.

In Encodia lavoriamo spesso su WordPress, Laravel, Statamic, SEO tecnica, integrazioni e manutenzione evolutiva.

Questo sito mostra un’altra parte dello stesso mestiere: capire quando non serve una piattaforma grande, quando non serve un CMS, quando non serve portarsi dietro un’intera applicazione per pubblicare contenuto e generare contatti.

Astro e Cloudflare qui non sono un capriccio da sviluppatore: sono il modo più leggero che ho trovato per ottenere velocità, controllo e qualità editoriale senza troppa zavorra.

FAQ

Domande su Astro e Cloudflare

Perché usare Astro per un sito aziendale o personale?

Astro ha senso quando il contenuto è centrale e la UI non richiede un runtime JavaScript pesante. Genera HTML statico, integra Markdown e lascia aggiungere interattività solo dove serve. Per landing, blog, siti editoriali leggeri e pagine corporate tecniche è spesso una scelta molto pulita.

Cloudflare Workers o Cloudflare Pages?

Il progetto usa l'adapter @astrojs/cloudflare, quindi compila per il runtime Cloudflare. Pages può gestire hosting e pipeline, Workers è il modello di esecuzione. La distinzione è tecnica, ma il valore pratico è semplice: deploy vicino agli utenti, niente server da mantenere e cache controllabile.

Astro sostituisce WordPress o Laravel?

No. Astro, WordPress, Laravel e Statamic risolvono problemi diversi. In Encodia scegliamo lo stack in base al progetto: se servono CMS redazionale, logiche applicative o back office, Astro da solo non basta. Se serve un sito veloce, editoriale e governabile senza database, può essere la scelta giusta.

Il blog è statico o dinamico?

Statico al 100%. I post sono file Markdown processati a build-time con Astro Content Collections. Non c'è database, non c'è query SQL e non c'è CMS headless. Cloudflare serve pagine già renderizzate, con una pipeline di pubblicazione controllata.

Se vuoi parlarne sul serio

Se stai valutando Astro, Cloudflare o una riprogettazione del tuo sito attuale, la domanda giusta non è “qual è lo stack migliore?”.

La domanda giusta è: quanto controllo ti serve davvero, e su quali parti?

Posso aiutarti a capirlo prima di scrivere una riga di codice: cosa tenere, cosa rifare, cosa lasciare statico, cosa rendere dinamico, cosa non ha senso costruire.

Se il progetto richiede WordPress, Laravel, Statamic o un’altra strada, va benissimo. Il punto non è sponsorizzare Astro.

Il punto è scegliere lo stack che ti lascia più margine nei prossimi anni, non quello che sembra più brillante nel primo incontro.