Da Java a .Net: la mia esperienza, due anni dopo – Parte II

A che cosa non rinuncerei e che cosa mi manca

Seconda parte di un post sulla mia esperienza di passaggio da piattaforma Java (e JEE) a piattaforma .Net: una settimana fa ho parlato (qui) delle principali differenze tra i due linguaggi principe dei due ecosistemi (Java e C#, rispettivamente), chiarendo quali caratteristiche dell’uno e dell’altro preferisco, e del fatto che esistono nelle due community convenzioni di codifica diverse; oggi proseguirò parlando della differenza tra alcune caratteristiche peculiari delle due piattaforme e della vitalità dei due ecosistemi.

Due piattaforme

Nel mare di differenze, più o meno rilevanti, ne scelgo due, una per la quale non esisterei a scegliere .Net ed una per la quale non esiterei a scegliere JVM:

  • generics “veri”: la disponibilità a runtime dei parametri di tipo dei generics rappresenta la caratteristica della piattaforma .Net che preferisco, se non altro per quanto riguarda le differenze rispetto alla piattaforma Java. Ne ho già parlato per quanto riguarda la differenza tra i linguaggi, ma si tratta di un aspetto legato all’implementazione della feature in questione a livello di runtime e che ha impatto su come la stessa feature può essere implementata anche in altri linguaggi (Scala o Groovy o Kotlin soffrono delle stesse limitazioni di cui soffre Java, limitazioni legate al fatto che i tipi generici si perdono con la compilazione (type erasure))
  • exe / dll vs jar: la piattaforma .Net richiede che, al momento della compilazione, sia noto se un particolare assembly sarà utilizzato come libreria (diventando una dll) o come entry point dell’esecuzione di un programma (diventando un exe); al compilatore Java, al contrario, non interessa sapere come verranno utilizzate le classi che compila: a valle della compilazione, esse potranno essere distribuite singolarmente od impacchettate in un file jar, ma solo al momento dell’avvio della JVM sarà necessario indicare l’entry point dell’esecuzione. Ciò ha due conseguenze, che personalmente trovo molto sgradevoli quando lavoro su piattaforma .Net: la necessità di cambiare esplicitamente la configurazione di un assembly per passare da dll ad exe (o viceversa) e l’impossibilità di avere più classi con metodo Main nello stesso assembly (cosa a mio parere intollerabilmente scomoda nel corso di esperimenti o per sviluppare utility di test)

Due ecosistemi

Le differenze tra i due ecosistemi sono molte, alcune legate ad impostazioni filosofiche (si pensi ad esempio al fatto che solo di recente Microsoft ha optato per una svolta in ottica open source), altre a motivi storici (le due piattaforme sono nate a distanza di parecchi anni l’una dall’altra, con quella più giovane sempre attardata rispetto a quella più vecchia dal punto di vista dell’ampiezza e della vitalità delle community di riferimento). Qualche (personalissima) osservazione su alcuni aspetti a mio avviso interessanti:

Ambienti di sviluppo

Se tradizionalmente il mondo Java è sempre stato caratterizzato dalla possibilità di scelta tra più IDE (per citare solo quelli più diffusi, Eclipse, NetBeans, IntelliJIdea), il mondo .Net ha visto solo nell’ultimo anno lo sviluppo di una (validissima, per la mia limitata esperienza) alternativa al monopolio di Visual Studio (Rider di JetBrains). La cosa ha vantaggi e svantaggi, su entrambi i fronti: se infatti è innegabile che sia comodo che qualunque developer con esperienza di sviluppo .Net condivida con qualunque collega una conoscenza approfondita di uno stesso strumento, è innegabile anche che IDE diversi, sviluppati autonomamente, in concorrenza e talvolta secondo logiche open, abbiano la possibilità di raggiungere l’eccellenza su specifici strumenti ed aspetti, al di fuori di un’ottica strettamente legata a questioni di opportunità aziendale. Personalmente, e superato – penso – il tempo in cui la cosa poteva essere motivata da un qualche pregiudizio o dall’inesperienza, trovo che Visual Stupido Studio (diciamo nella versione 2015, sulla quale ho la maggior parte dell’esperienza) sia un ambiente di sviluppo piuttosto arretrato rispetto ad Eclipse o ad IntelliJIdea, soprattutto quando VS viene utilizzato senza il plugin Resharper di JetBrains (ma, a quel punto: perché non utilizzare Rider?).

Strumenti

Una cosa che mi ha colpito (negativamente) nei primi mesi di passaggio dal mondo Java al mondo .Net è stata quella che percepivo (e continuo a percepire…) come povertà e scarsa varietà di strumenti di sviluppo: al di là degli strumenti utilizzati dietro le quinte dall’onnipresente Visual Studio, di cui si è detto ed il cui ruolo di strumento monopolista del mondo .Net spiega probabilmente l’estrema desolazione del panorama circostante, non trovavo nulla (o quasi) del forse fin troppo vitale ecosistema di strumenti di supporto alla gestione dei progetti e del build cui ero abituato: Ant, Maven, Gradle, Sbt (solo per fare qualche nome), Hudson, con la pletora di relativi plugin che fanno tutto tranne il caffé… Non che nel mondo .Net non vi siano strumenti analoghi (MS Build, il porting NAnt, Team City) – il loro utilizzo però è generalmente nascosto da quello di VS, con un approccio molto più IDE-centrico di quanto mi sento di considerare preferibile in un mondo di DevOps e la varietà di scelta è molto meno ampia. Questa situazione è chiaramente correlata a quella del punto precedente: essendoci nella pratica un solo IDE diffusamente utilizzato, non è irragionevole che la prassi che si è evoluta sia quella che ne utilizza le configurazioni ed il “motore” come source of truth e come entry-point per tutto ciò che riguarda la gestione del build. Nel mondo Java, al contrario, la disponibilità di più ambienti di sviluppo differenti, ugualmente validi, che si dividono le preferenze degli utenti, ha probabilmente spinto verso l’abitudine di utilizzare configurazioni indipendenti dall’IDE come source of truth, ad esempio, per quanto riguarda le dipendenze esterne e gli step di build e pacchettizzazione, ed ha favorito la realizzazione di strumenti specifici per la gestione di tali aspetti del ciclo di sviluppo di un progetto.

Altri linguaggi

Le due piattaforme in esame sono molto simili anche per quanto riguarda l’idea che sia possibile farvi girare software scritto in linguaggi diversi: ancora una volta, la varietà di opzioni disponibili per JVM (Java, Groovy, Scala, Kotlin, Clojure, Jython, JRuby, solo per citare i più mainstream) mi pare notevolmente più ampia di quella che si trova su .Net (C# ed F#, più qualche porting morto di Python e Ruby). La cosa, di per sé non fondamentale, può essere importante in contesti nei quali siano presenti richieste funzionali particolari, per i quali siano indicati linguaggi diversi (penso ad esempio a ciò che può fornire un linguaggio a tipizzazione dinamica come Groovy in termini di facilità di sviluppo di DSL, all’utilizzo di un linguaggio funzionale come Scala o Clojure per lo sviluppo di algoritmi, …).

Librerie

Il mondo Java è senza dubbio noto per la quantità sterminata di librerie disponibili liberamente, più o meno per qualunque ragionevole task possa essere necessario implementare – tale varietà a volte è quasi di disturbo, nel senso che non esiste quasi mai un solo modo per fare una cosa e che quasi sempre si tratta di scegliere tra più opzioni. La mia impressione è che il mondo .Net si muova nella stessa direzione, ma con molto ritardo: se in alcuni casi non esistono molte alternative (o non ne esistono di molto utilizzate) al modo Microsoft di fare le cose (penso a WCF e WPF: nel mondo Java in mezz’ora si possono probabilmente elencare dieci framework/librerie per ciascuno dei due domini), in altri c’è invece molta varietà (penso al caso degli IoC container); in altri ancora (e sono probabilmente i casi più interessanti per farsi un’idea del rapporto e delle dinamiche che intercorrono tra le due tecnologie) le librerie mainstream utilizzate su piattaforma .Net sono il porting di equivalenti librerie nate e sviluppatesi su piattaforma Java (iTextSharp, Poi.Net, NHibernate, Akka.Net, …).

Nettamente a favore del mondo .Net, per chiudere il paragrafo sulla disponibilità di librerie, è la mia sensazione circa la facilità con cui è possibile condividere una propria libreria in modo che sia utilizzabile da altri: il repository NuGet ufficiale, infatti, permette a chiunque di registrarsi e pubblicare i propri artefatti, un po’ come avviene nella community Node.js con la pubblicazione di pacchetti NPM; nel mondo Java, al contrario, il repository di pacchetti equivalente (Maven Central), non supporta una modalità così immediata e diretta di pubblicazione ma richiede un passaggio di validazione umana (apertura ticket su un issue-tracker).

Portabilità

La portabilità cross-platform è sempre stata una – forse la – caratteristica peculiare della piattaforma Java (secondo il mantra abusato write once, run everywhere) ma, con l’introduzione di .Net Core e della specifica .Net Standard, è diventata parte anche dell’offerta tecnologica della piattaforma .Net. Da questo punto di vista, ma la cosa è ovvia se si pensa alla storia delle due piattaforme, l’offerta di Microsoft è ancora molto più acerba di quella di Oracle: benché .Net Core sia molto interessante ed inizi ad avere un discreto supporto per sistemi diversi da quelli della famiglia Windows, al momento non regge il confronto della piattaforma Java come scelta production ready per progetti sui quali la portabilità cross-platform sia un requisito essenziale.

Ricchezza e varietà

Dal punto di vista della varietà di ciò che i due ecosistemi mettono a disposizione, in termini di strumenti, ambienti di sviluppo, linguaggi supportati, librerie disponibili, non ho dunque molti dubbi: il mondo Java, anche solo per questioni di diffusione (qui e qui) e di età, risulta decisamente più ricco e vivo del mondo .Net. Questo non sempre è elemento decisionale centrale quando si tratta di partire con un nuovo progetto, ma in generale è un aspetto che può influenzare la scelta dello stack tecnologico (si pensi ad esempio a casi in cui sono forti esigenze di integrazione con altri sistemi – e sia dunque importante la disponibilità di librerie esterne già pronte – o nei quali siano presenti esigenze funzionali stringenti che rendono interessante disporre di un set di linguaggi vario, ciascuno well suited per uno specifico scopo ma con garanzia di interoperabilità seamless con gli altri).

Il mio punto di vista, sin qui, è stato quello di chi è passato dall’ecosistema più ricco e vario a quello maggiormente monoteista; mi rendo conto però che chi si trovasse a fare il passaggio inverso troverebbe forse maggiori difficoltà di quelle incontrate da me, proprio per la serie di motivi per cui io mantengo una preferenza per l’ecosistema Java: da un grande potere derivano grandi responsabilità, per citare lo zio di Spider Man, e l’improvvisa disponibilità di molte alternative, con le scelte che essa richiede di fare, può rappresentare un gradino iniziale da non sottovalutare.

Conclusione

Posto che le scelte effettive dipendono da fattori come vincoli tecnologici imposti dai clienti, specificità di progetto, skill del team, e che pertanto le preferenze personali contano niente, le mie sono decisamente a favore di C# (su Java) come linguaggio ma a favore di Java (su .Net) come piattaforma ed ecosistema. Non mi resta dunque che studiare Kotlin!!