Evitare le insidie ​​del codice integrato automaticamente

Pubblicato: 2022-03-10
Riepilogo rapido ↬ L'uso eccessivo del codice CSS o JS in linea, invece di fornire codice tramite risorse statiche, può danneggiare le prestazioni del sito. In questo articolo impareremo invece come caricare codice dinamico tramite file statici, evitando gli inconvenienti di troppo codice inline.

L'inlineing è il processo di inclusione del contenuto dei file direttamente nel documento HTML: i file CSS possono essere inseriti all'interno di un elemento di style e i file JavaScript possono essere inseriti all'interno di un elemento di script :

 <style> /* CSS contents here */ </style> <script> /* JS contents here */ </script>

Stampando il codice già nell'output HTML, l'inlining evita le richieste di blocco del rendering ed esegue il codice prima del rendering della pagina. In quanto tale, è utile per migliorare le prestazioni percepite del sito (ovvero il tempo necessario affinché una pagina diventi utilizzabile). Ad esempio, possiamo utilizzare il buffer dei dati forniti immediatamente durante il caricamento del sito (circa 14kb) su inline gli stili critici, inclusi gli stili di contenuto above-the-fold (come era stato fatto sul precedente sito di Smashing Magazine) e le dimensioni dei caratteri e le larghezze e le altezze del layout per evitare un rendering irregolare del layout quando il resto dei dati viene consegnato .

Tuttavia, se esagerato, l'inserimento del codice può anche avere effetti negativi sulle prestazioni del sito: poiché il codice non è memorizzabile nella cache, lo stesso contenuto viene inviato ripetutamente al client e non può essere memorizzato nella cache tramite Service Workers o memorizzato nella cache e accessibile da una rete di distribuzione dei contenuti. Inoltre, gli script inline sono considerati non sicuri quando si implementa una politica di sicurezza dei contenuti (CSP). Quindi, rende una strategia sensata incorporare quelle parti critiche di CSS e JS che rendono il caricamento del sito più veloce ma evitano il più possibile altrimenti.

Con l'obiettivo di evitare l'inlining, in questo articolo esploreremo come convertire il codice inline in asset statici: invece di stampare il codice nell'output HTML, lo salviamo su disco (creando effettivamente un file statico) e aggiungiamo il corrispondente <script> o <link> per caricare il file.

Iniziamo!

Lettura consigliata : La sicurezza di WordPress come processo

Altro dopo il salto! Continua a leggere sotto ↓

Quando evitare l'inline

Non esiste una ricetta magica per stabilire se un codice deve essere inline o meno, tuttavia, può essere abbastanza evidente quando un codice non deve essere inline: quando coinvolge una grossa porzione di codice e quando non è necessario immediatamente.

Ad esempio, i siti WordPress integrano i modelli JavaScript per eseguire il rendering di Media Manager (accessibile nella pagina Libreria multimediale in /wp-admin/upload.php ), stampando una notevole quantità di codice:

Uno screenshot del codice sorgente per la pagina Libreria multimediale
Modelli JavaScript integrati da WordPress Media Manager.

Occupando ben 43kb, la dimensione di questo pezzo di codice non è trascurabile e poiché si trova in fondo alla pagina non è necessario immediatamente. Quindi, avrebbe molto senso servire questo codice invece attraverso risorse statiche o stamparlo all'interno dell'output HTML.

Vediamo di seguito come trasformare il codice inline in asset statici.

Attivazione della creazione di file statici

Se i contenuti (quelli da inserire) provengono da un file statico, non c'è molto da fare se non semplicemente richiedere quel file statico invece di inserire il codice.

Per il codice dinamico, però, dobbiamo pianificare come/quando generare il file statico con il suo contenuto. Ad esempio, se il sito offre opzioni di configurazione (come la modifica della combinazione di colori o dell'immagine di sfondo), quando deve essere generato il file contenente i nuovi valori? Abbiamo le seguenti opportunità per creare i file statici dal codice dinamico:

  1. Su richiesta
    Quando un utente accede al contenuto per la prima volta.
  2. Al cambiamento
    Quando l'origine del codice dinamico (ad es. un valore di configurazione) è cambiata.

Consideriamo prima su richiesta. La prima volta che un utente accede al sito, diciamo tramite /index.html , il file statico (es. header-colors.css ) non esiste ancora, quindi deve essere generato. La sequenza degli eventi è la seguente:

  1. L'utente richiede /index.html ;
  2. Durante l'elaborazione della richiesta, il server verifica se esiste il file header-colors.css . Poiché non lo fa, ottiene il codice sorgente e genera il file su disco;
  3. Restituisce una risposta al client, incluso il tag <link rel="stylesheet" type="text/css" href="/staticfiles/header-colors.css">
  4. Il browser recupera tutte le risorse incluse nella pagina, incluso header-colors.css ;
  5. A quel punto questo file esiste, quindi è servito.

Tuttavia, la sequenza degli eventi potrebbe anche essere diversa, portando a un esito insoddisfacente. Per esempio:

  1. L'utente richiede /index.html ;
  2. Questo file è già memorizzato nella cache dal browser (o da qualche altro proxy, o tramite Service Workers), quindi la richiesta non viene mai inviata al server;
  3. Il browser recupera tutte le risorse incluse nella pagina, incluso header-colors.css . Questa immagine, tuttavia, non è memorizzata nella cache del browser, quindi la richiesta viene inviata al server;
  4. Il server non ha ancora generato header-colors.css (es. è stato appena riavviato);
  5. Restituirà un 404.

In alternativa, potremmo generare header-colors.css non quando si richiede /index.html , ma quando si richiede lo stesso /header-colors.css . Tuttavia, poiché questo file inizialmente non esiste, la richiesta è già trattata come un 404. Anche se potremmo aggirarlo, alterando le intestazioni per cambiare il codice di stato in un 200 e restituendo il contenuto dell'immagine, questo è un modo terribile di fare le cose, quindi non prenderemo in considerazione questa possibilità (siamo molto meglio di così!)

Ciò lascia solo un'opzione: generare il file statico dopo che la sua origine è cambiata.

Creazione del file statico quando cambia l'origine

Tieni presente che possiamo creare codice dinamico da fonti sia dipendenti dall'utente che dal sito. Ad esempio, se il tema consente di modificare l'immagine di sfondo del sito e tale opzione è configurata dall'amministratore del sito, il file statico può essere generato come parte del processo di distribuzione. Se invece il sito consente ai propri utenti di modificare l'immagine di sfondo dei propri profili, il file statico deve essere generato in runtime.

In poche parole, abbiamo questi due casi:

  1. Configurazione utente
    Il processo deve essere attivato quando l'utente aggiorna una configurazione.
  2. Configurazione del sito
    Il processo deve essere attivato quando l'amministratore aggiorna una configurazione per il sito o prima di distribuire il sito.

Se considerassimo i due casi in modo indipendente, per il n. 2 potremmo progettare il processo su qualsiasi stack tecnologico volessimo. Tuttavia, non vogliamo implementare due soluzioni diverse, ma un'unica soluzione in grado di affrontare entrambi i casi. E poiché dal numero 1 il processo per generare il file statico deve essere attivato sul sito in esecuzione, è importante progettare questo processo attorno allo stesso stack tecnologico su cui viene eseguito il sito.

Durante la progettazione del processo, il nostro codice dovrà gestire le circostanze specifiche di n. 1 e n. 2:

  • Versione
    È necessario accedere al file statico con un parametro “versione”, in modo da invalidare il file precedente alla creazione di un nuovo file statico. Mentre il numero 2 potrebbe semplicemente avere la stessa versione del sito, il numero 1 deve utilizzare una versione dinamica per ogni utente, possibilmente salvata nel database.
  • Posizione del file generato
    #2 genera un file statico unico per l'intero sito (es. /staticfiles/header-colors.css ), mentre #1 crea un file statico per ogni utente (es. /staticfiles/users/leo/header-colors.css ).
  • Evento scatenante
    Mentre per #1 il file statico deve essere eseguito in runtime, per #2 può anche essere eseguito come parte di un processo di compilazione nel nostro ambiente di staging.
  • Distribuzione e distribuzione
    I file statici in n. 2 possono essere integrati senza problemi all'interno del pacchetto di distribuzione del sito, senza problemi; i file statici in n. 1, tuttavia, non possono, quindi il processo deve gestire problemi aggiuntivi, come più server dietro un sistema di bilanciamento del carico (i file statici verranno creati solo in 1 server o in tutti e come?).

Progettiamo e implementiamo il processo dopo. Affinché ogni file statico venga generato, dobbiamo creare un oggetto contenente i metadati del file, calcolarne il contenuto dalle sorgenti dinamiche e infine salvare il file statico su disco. Come caso d'uso per guidare le spiegazioni di seguito, genereremo i seguenti file statici:

  1. header-colors.css , con uno stile dai valori salvati nel database
  2. welcomeuser-data.js , contenente un oggetto JSON con dati utente sotto alcune variabili: window.welcomeUserData = {name: "Leo"}; .

Di seguito, descriverò il processo per generare i file statici per WordPress, per i quali dobbiamo basare lo stack su funzioni PHP e WordPress. La funzione per generare i file statici prima della distribuzione può essere attivata caricando una pagina speciale che esegue shortcode [create_static_files] come ho descritto in un articolo precedente.

Ulteriore lettura consigliata : Making A Service Worker: A Case Study

Rappresentare il file come un oggetto

Dobbiamo modellare un file come un oggetto PHP con tutte le proprietà corrispondenti, quindi possiamo sia salvare il file su disco in una posizione specifica (ad esempio sotto /staticfiles/ o /staticfiles/users/leo/ ), sia sapere come richiedere il file di conseguenza. Per questo, creiamo un'interfaccia Resource che restituisce sia i metadati del file (nome file, dir, tipo: "css" o "js", versione e dipendenze da altre risorse) sia il suo contenuto.

 interface Resource { function get_filename(); function get_dir(); function get_type(); function get_version(); function get_dependencies(); function get_content(); }

Per rendere il codice manutenibile e riutilizzabile seguiamo i principi SOLID, per i quali impostiamo uno schema di ereditarietà degli oggetti per le risorse per aggiungere gradualmente proprietà, partendo dalla classe astratta ResourceBase da cui erediteranno tutte le nostre implementazioni di Resource:

 abstract class ResourceBase implements Resource { function get_dependencies() { // By default, a file has no dependencies return array(); } }

Seguendo SOLID, creiamo sottoclassi ogni volta che le proprietà differiscono. Come affermato in precedenza, la posizione del file statico generato e il controllo delle versioni per richiederlo saranno diversi a seconda del file relativo alla configurazione dell'utente o del sito:

 abstract class UserResourceBase extends ResourceBase { function get_dir() { // A different file and folder for each user $user = wp_get_current_user(); return "/staticfiles/users/{$user->user_login}/"; } function get_version() { // Save the resource version for the user under her meta data. // When the file is regenerated, must execute `update_user_meta` to increase the version number $user_id = get_current_user_id(); $meta_key = "resource_version_".$this->get_filename(); return get_user_meta($user_id, $meta_key, true); } } abstract class SiteResourceBase extends ResourceBase { function get_dir() { // All files are placed in the same folder return "/staticfiles/"; } function get_version() { // Same versioning as the site, assumed defined under a constant return SITE_VERSION; } }

Infine, all'ultimo livello, implementiamo gli oggetti per i file che vogliamo generare, aggiungendo il nome del file, il tipo di file e il codice dinamico tramite la funzione get_content :

 class HeaderColorsSiteResource extends SiteResourceBase { function get_filename() { return "header-colors"; } function get_type() { return "css"; } function get_content() { return sprintf( " .site-title a { color: #%s; } ", esc_attr(get_header_textcolor()) ); } } class WelcomeUserDataUserResource extends UserResourceBase { function get_filename() { return "welcomeuser-data"; } function get_type() { return "js"; } function get_content() { $user = wp_get_current_user(); return sprintf( "window.welcomeUserData = %s;", json_encode( array( "name" => $user->display_name ) ) ); } }

Con questo, abbiamo modellato il file come un oggetto PHP. Successivamente, dobbiamo salvarlo su disco.

Salvataggio del file statico su disco

Il salvataggio di un file su disco può essere facilmente eseguito tramite le funzioni native fornite dal linguaggio. Nel caso di PHP, ciò si ottiene tramite la funzione fwrite . Inoltre, creiamo una classe di utilità ResourceUtils con funzioni che forniscono il percorso assoluto del file su disco e anche il suo percorso relativo alla radice del sito:

 class ResourceUtils { protected static function get_file_relative_path($fileObject) { return $fileObject->get_dir().$fileObject->get_filename().".".$fileObject->get_type(); } static function get_file_path($fileObject) { // Notice that we must add constant WP_CONTENT_DIR to make the path absolute when saving the file return WP_CONTENT_DIR.self::get_file_relative_path($fileObject); } } class ResourceGenerator { static function save($fileObject) { $file_path = ResourceUtils::get_file_path($fileObject); $handle = fopen($file_path, "wb"); $numbytes = fwrite($handle, $fileObject->get_content()); fclose($handle); } }

Quindi, ogni volta che il sorgente cambia e il file statico deve essere rigenerato, eseguiamo ResourceGenerator::save passando l'oggetto che rappresenta il file come parametro. Il codice seguente rigenera e salva su disco i file "header-colors.css" e "welcomeuser-data.js":

 // When need to regenerate header-colors.css, execute: ResourceGenerator::save(new HeaderColorsSiteResource()); // When need to regenerate welcomeuser-data.js, execute: ResourceGenerator::save(new WelcomeUserDataUserResource());

Una volta che esistono, possiamo accodare i file da caricare tramite i <script> e <link> .

Accodamento dei file statici

Accodare i file statici non è diverso dall'accodare qualsiasi risorsa in WordPress: attraverso le funzioni wp_enqueue_script e wp_enqueue_style . Quindi, ripetiamo semplicemente tutte le istanze dell'oggetto e utilizziamo un hook o l'altro a seconda che il loro valore get_type() sia "js" o "css" .

Per prima cosa aggiungiamo funzioni di utilità per fornire l'URL del file e per indicare che il tipo è JS o CSS:

 class ResourceUtils { // Continued from above... static function get_file_url($fileObject) { // Add the site URL before the file path return get_site_url().self::get_file_relative_path($fileObject); } static function is_css($fileObject) { return $fileObject->get_type() == "css"; } static function is_js($fileObject) { return $fileObject->get_type() == "js"; } }

Un'istanza della classe ResourceEnqueuer conterrà tutti i file che devono essere caricati; quando richiamate, le sue funzioni enqueue_scripts e enqueue_styles eseguiranno l'accodamento, eseguendo le corrispondenti funzioni di WordPress ( rispettivamente wp_enqueue_script e wp_enqueue_style ):

 class ResourceEnqueuer { protected $fileObjects; function __construct($fileObjects) { $this->fileObjects = $fileObjects; } protected function get_file_properties($fileObject) { $handle = $fileObject->get_filename(); $url = ResourceUtils::get_file_url($fileObject); $dependencies = $fileObject->get_dependencies(); $version = $fileObject->get_version(); return array($handle, $url, $dependencies, $version); } function enqueue_scripts() { $jsFileObjects = array_map(array(ResourceUtils::class, 'is_js'), $this->fileObjects); foreach ($jsFileObjects as $fileObject) { list($handle, $url, $dependencies, $version) = $this->get_file_properties($fileObject); wp_register_script($handle, $url, $dependencies, $version); wp_enqueue_script($handle); } } function enqueue_styles() { $cssFileObjects = array_map(array(ResourceUtils::class, 'is_css'), $this->fileObjects); foreach ($cssFileObjects as $fileObject) { list($handle, $url, $dependencies, $version) = $this->get_file_properties($fileObject); wp_register_style($handle, $url, $dependencies, $version); wp_enqueue_style($handle); } } }

Infine, istanziamo un oggetto della classe ResourceEnqueuer con un elenco degli oggetti PHP che rappresentano ciascun file e aggiungiamo un hook di WordPress per eseguire l'accodamento:

 // Initialize with the corresponding object instances for each file to enqueue $fileEnqueuer = new ResourceEnqueuer( array( new HeaderColorsSiteResource(), new WelcomeUserDataUserResource() ) ); // Add the WordPress hooks to enqueue the resources add_action('wp_enqueue_scripts', array($fileEnqueuer, 'enqueue_scripts')); add_action('wp_print_styles', array($fileEnqueuer, 'enqueue_styles'));

Ecco fatto: essendo accodati, i file statici verranno richiesti durante il caricamento del sito nel client. Siamo riusciti a evitare di stampare codice inline e caricare invece risorse statiche.

Successivamente, possiamo applicare diversi miglioramenti per ulteriori miglioramenti delle prestazioni.

Letture consigliate : Introduzione al test automatizzato dei plugin di WordPress con PHPUnit

Raggruppare i file insieme

Anche se HTTP/2 ha ridotto la necessità di raggruppare i file, rende comunque il sito più veloce, perché la compressione dei file (ad esempio tramite GZip) sarà più efficace e poiché i browser (come Chrome) hanno un sovraccarico maggiore nell'elaborazione di molte risorse .

A questo punto, abbiamo modellato un file come un oggetto PHP, che ci consente di trattare questo oggetto come un input per altri processi. In particolare, possiamo ripetere la stessa procedura sopra per raggruppare tutti i file dello stesso tipo e servire la versione in bundle invece di tutti i file indipendenti. Per questo, creiamo una funzione get_content che estrae semplicemente il contenuto da ogni risorsa in $fileObjects e lo stampa di nuovo, producendo l'aggregazione di tutto il contenuto da tutte le risorse:

 abstract class SiteBundleBase extends SiteResourceBase { protected $fileObjects; function __construct($fileObjects) { $this->fileObjects = $fileObjects; } function get_content() { $content = ""; foreach ($this->fileObjects as $fileObject) { $content .= $fileObject->get_content().PHP_EOL; } return $content; } }

Possiamo raggruppare tutti i file insieme nel file bundled-styles.css creando una classe per questo file:

 class StylesSiteBundle extends SiteBundleBase { function get_filename() { return "bundled-styles"; } function get_type() { return "css"; } }

Infine, accodiamo semplicemente questi file raggruppati, come prima, invece di tutte le risorse indipendenti. Per CSS, creiamo un bundle contenente i file header-colors.css , background-image.css e font-sizes.css , per i quali istanziamo semplicemente StylesSiteBundle con l'oggetto PHP per ciascuno di questi file (e allo stesso modo possiamo creare il JS file bundle):

 $fileObjects = array( // CSS new HeaderColorsSiteResource(), new BackgroundImageSiteResource(), new FontSizesSiteResource(), // JS new WelcomeUserDataUserResource(), new UserShoppingItemsUserResource() ); $cssFileObjects = array_map(array(ResourceUtils::class, 'is_css'), $fileObjects); $jsFileObjects = array_map(array(ResourceUtils::class, 'is_js'), $fileObjects); // Use this definition of $fileEnqueuer instead of the previous one $fileEnqueuer = new ResourceEnqueuer( array( new StylesSiteBundle($cssFileObjects), new ScriptsSiteBundle($jsFileObjects) ) );

Questo è tutto. Ora richiederemo solo un file JS e un file CSS invece di molti.

Un ultimo miglioramento per le prestazioni percepite consiste nell'assegnare priorità alle risorse, ritardando il caricamento di quelle risorse che non sono necessarie immediatamente. Affrontiamo questo dopo.

async / defer Attributi per le risorse JS

Possiamo aggiungere attributi async e defer al tag <script> , per modificare quando il file JavaScript viene scaricato, analizzato ed eseguito, in modo da dare la priorità a JavaScript critico e spingere tutto ciò che non è critico il più tardi possibile, diminuendo così il caricamento apparente del sito volta.

Per implementare questa funzionalità, seguendo i principi SOLID, dovremmo creare una nuova interfaccia JSResource (che eredita da Resource ) contenente le funzioni is_async e is_defer . Tuttavia, questo chiuderebbe la porta ai tag <style> che alla fine supportano anche questi attributi. Quindi, tenendo presente l'adattabilità, adottiamo un approccio più aperto: aggiungiamo semplicemente un metodo generico get_attributes all'interfaccia Resource in modo da mantenerla flessibile da aggiungere a qualsiasi attributo (sia già esistente o ancora da inventare) per entrambi <script> e tag <link> :

 interface Resource { // Continued from above... function get_attributes(); } abstract class ResourceBase implements Resource { // Continued from above... function get_attributes() { // By default, no extra attributes return ''; } }

WordPress non offre un modo semplice per aggiungere attributi extra alle risorse accodate, quindi lo facciamo in un modo piuttosto complicato, aggiungendo un hook che sostituisce una stringa all'interno del tag tramite la funzione add_script_tag_attributes :

 class ResourceEnqueuerUtils { protected static tag_attributes = array(); static function add_tag_attributes($handle, $attributes) { self::tag_attributes[$handle] = $attributes; } static function add_script_tag_attributes($tag, $handle, $src) { if ($attributes = self::tag_attributes[$handle]) { $tag = str_replace( " src='${src}'>", " src='${src}' ".$attributes.">", $tag ); } return $tag; } } // Initize by connecting to the WordPress hook add_filter( 'script_loader_tag', array(ResourceEnqueuerUtils::class, 'add_script_tag_attributes'), PHP_INT_MAX, 3 );

Aggiungiamo gli attributi per una risorsa durante la creazione dell'istanza dell'oggetto corrispondente:

 abstract class ResourceBase implements Resource { // Continued from above... function __construct() { ResourceEnqueuerUtils::add_tag_attributes($this->get_filename(), $this->get_attributes()); } }

Infine, se la risorsa welcomeuser-data.js non deve essere eseguita immediatamente, possiamo impostarla come defer :

 class WelcomeUserDataUserResource extends UserResourceBase { // Continued from above... function get_attributes() { return "defer='defer'"; } }

Poiché viene caricato come differito, uno script verrà caricato in un secondo momento, anticipando il momento in cui l'utente può interagire con il sito. Per quanto riguarda i guadagni in termini di prestazioni, ora siamo pronti!

C'è ancora un problema da risolvere prima di poterci rilassare: cosa succede quando il sito è ospitato su più server?

Gestire più server dietro un sistema di bilanciamento del carico

Se il nostro sito è ospitato su più siti dietro un sistema di bilanciamento del carico e viene rigenerato un file dipendente dalla configurazione dell'utente, il server che gestisce la richiesta deve, in qualche modo, caricare il file statico rigenerato su tutti gli altri server; in caso contrario, gli altri server serviranno una versione non aggiornata di quel file da quel momento in poi. Come facciamo questo? Fare in modo che i server comunichino tra loro non è solo complesso, ma alla fine potrebbe rivelarsi impraticabile: cosa succede se il sito funziona su centinaia di server, da regioni diverse? Chiaramente, questa non è un'opzione.

La soluzione che ho trovato è aggiungere un livello di indirizzamento: invece di richiedere i file statici dall'URL del sito, vengono richiesti da una posizione nel cloud, ad esempio da un bucket AWS S3. Quindi, dopo aver rigenerato il file, il server caricherà immediatamente il nuovo file su S3 e lo servirà da lì. L'implementazione di questa soluzione è spiegata nel mio precedente articolo Condivisione di dati tra più server tramite AWS S3.

Conclusione

In questo articolo, abbiamo considerato che l'integrazione di codice JS e CSS non è sempre l'ideale, perché il codice deve essere inviato ripetutamente al client, il che può influire sulle prestazioni se la quantità di codice è significativa. Abbiamo visto, ad esempio, come WordPress carica 43kb di script per stampare il Media Manager, che sono modelli JavaScript puri e potrebbero essere caricati perfettamente come risorse statiche.

Quindi, abbiamo escogitato un modo per rendere il sito più veloce trasformando il codice inline JS e CSS dinamico in risorse statiche, che possono migliorare la memorizzazione nella cache a più livelli (nel client, Service Workers, CDN), permette di raggruppare ulteriormente tutti i file insieme in una sola risorsa JS/CSS per migliorare il rapporto durante la compressione dell'output (come tramite GZip) ed evitare un sovraccarico nei browser dall'elaborazione di più risorse contemporaneamente (come in Chrome), e inoltre consente di aggiungere attributi async o defer al tag <script> per velocizzare l'interattività dell'utente, migliorando così il tempo di caricamento apparente del sito.

Come effetto collaterale benefico, la suddivisione del codice in risorse statiche consente anche al codice di essere più leggibile, trattando unità di codice anziché grandi blob di HTML, il che può portare a una migliore manutenzione del progetto.

La soluzione che abbiamo sviluppato è stata realizzata in PHP e include alcuni bit specifici di codice per WordPress, tuttavia, il codice stesso è estremamente semplice, a malapena poche interfacce che definiscono proprietà e oggetti che implementano tali proprietà seguendo i principi SOLID e una funzione per salvare un file su disco. Questo è praticamente tutto. Il risultato finale è pulito e compatto, semplice da ricreare per qualsiasi altro linguaggio e piattaforma e non difficile da introdurre in un progetto esistente, fornendo facili guadagni in termini di prestazioni.