Quanto costa la migrazione da ASP.NET WebForms a Core?
La scelta si fa su tre numeri: i minuti persi ogni giorno (4.459 euro l'anno ciascuno con 38 utenti), gli anni di vita richiesti e il rischio di un sistema che nessuno conosce.

Un distributore di materiale elettrico in Emilia, 52 dipendenti e 31 milioni di fatturato, ha un portale per ordini, listini e disponibilità scritto in ASP.NET WebForms nel 2011. Lo usano 38 persone: 14 in azienda e 24 agenti e clienti che entrano da fuori. Gira su .NET Framework 4.8, su un server Windows del 2016, con un database SQL Server. Il programmatore che lo ha scritto è passato a un altro lavoro tre anni fa e ogni modifica la fa un consulente che chiede sempre più tempo per capire dove mettere le mani. Il direttore dell'azienda ha una sola domanda: conviene fare la migrazione da ASP.NET WebForms a Core, oppure continuiamo a tenerlo in piedi?
Le risposte che girano sono due, entrambe scomode. La prima dice che WebForms è finito e che si riscrive tutto: la dice chi vende progetti di riscrittura. La seconda dice che funziona da quindici anni e quindi non si tocca: la dice chi non vuole spendere o chi non vuole decidere. La verità è meno comoda. WebForms non si "porta" su ASP.NET Core perché non esiste, in Core, un equivalente di Page, di ViewState e dei controlli server. Si può quindi parlare di sostituzione, non di conversione. Ma sostituire non vuol dire fare tutto in una volta, e soprattutto non vuol dire farlo subito.
La decisione si prende su tre numeri che nessun preventivo scrive: quanti minuti perde ogni utente ogni giorno a causa dei limiti del portale, quanti anni deve ancora servire e quanto vale il rischio di un sistema che nessuno conosce più. Con il caso dell'Emilia, che è composto e non di un cliente reale, vedrai come si misurano in una settimana. Il risultato: a cinque anni la strada più economica è mettere in sicurezza WebForms sul .NET Framework 4.8, dal sesto anno la migrazione a moduli vince, e il punto di pareggio sta a 5,2 anni.
Trovi i segnali che dicono che il portale è a rischio, l'inventario di che cosa contiene davvero, le quattro strade con i loro costi, il conto a uno, tre, cinque e otto anni, il modo di sostituire un'area per volta con un guscio ASP.NET Core affiancato a WebForms tramite YARP, il confronto fra una pagina .aspx e la sua sostituzione in Razor Pages, i problemi di sessione, di autenticazione e di ViewState che fanno saltare le stime, e gli errori che ho visto ripetersi in ventisei anni di progetti.
Una premessa onesta. Scrivo software dal 1999, ho costruito un gestionale legale multi-tenant e vendo anche questo tipo di lavoro, quindi so da che parte sta il mio interesse. Il caso è composto, le ipotesi sono scritte una per una e il risultato include il caso in cui non conviene migrare né da me né da nessun altro. Un consiglio che non ti dice quando non spendere vale poco.
Quando serve una migrazione da WebForms a ASP.NET Core?
Un portale WebForms in buono stato non è un problema da risolvere subito. Il problema è l'insieme di condizioni che lo circondano, e ci sono sette segnali. Con tre o più siamo nella zona in cui il rischio comincia a costare più del cambio.
1. Il portale gira solo su Windows e su IIS, e nessuno vuole toccare il server
.NET Framework 4.8 è supportato da Microsoft finché è supportata la versione di Windows su cui gira, ma non riceve più funzionalità nuove. Il rischio concreto non è che smetta, è che il server diventi un oggetto che nessuno osa aggiornare. Nel nostro caso il server è del 2016 e ha un solo amministratore esterno.
2. Lo sviluppo si fa solo con strumenti vecchi
I progetti WebForms si aprono con Visual Studio e si pubblicano con il vecchio formato di progetto. Le librerie moderne, dall'iniezione delle dipendenze integrata ai pacchetti che escono solo per .NET moderno, non arrivano. Ogni anno che passa, il numero di cose che il portale non può usare cresce.
3. Il portale lo conosce una persona sola, o nessuna
È il segnale più serio e il meno discusso. Duecentoquattordici pagine, scritte nell'arco di dodici anni da tre persone diverse, con la logica nel code-behind e nelle stored procedure, sono conoscenza che vive in una testa. Se manca, l'azienda non perde il software, perde la possibilità di cambiare il proprio modo di vendere.
4. Il ViewState e i postback rendono le pagine lente
Una griglia di listini con 2.000 righe che porta con sé un ViewState di 1,8 MB è un problema del modello e non di una pagina. Gli agenti in mobilità, con una connessione debole, aspettano nove secondi per vedere un prezzo. Questo si misura, ed è il primo numero da prendere.
5. Le librerie di terzi sono bloccate o a fine corsa
Controlli commerciali per griglie, grafici e report, versioni vecchie di componenti per i PDF e per l'invio di posta. Alcuni produttori continuano a supportare le versioni WebForms, altri no. Quello che conta è sapere quali sono i tuoi e a che versione stanno.
6. Le integrazioni sono tutte ripieghi
Il collegamento con il gestionale contabile, con il corriere, con il sistema di un cliente della grande distribuzione: ciascuno è un file CSV, un job notturno o un servizio web vecchio stile. Quando un cliente chiede i dati in tempo reale, il portale non sa darli. È qui che il costo del "non fare niente" si trasforma in un ordine perso.
7. Non si adatta al telefono e non si lascia automatizzare
Gli agenti usano il telefono. Una pagina WebForms disegnata per uno schermo da 1.280 pixel si usa pizzicando lo schermo. E non c'è un'interfaccia che un altro programma possa chiamare, quindi nessun collegamento si costruisce senza copiare logica.
Nel caso dell'Emilia i segnali veri sono cinque su sette: il server fermo, la persona che non c'è più, il ViewState pesante, le integrazioni a stampella e il telefono. Con un esito così la domanda non è se pensarci, ma che cosa fare e quando. Per l'orizzonte generale del costo di un programma vecchio, la guida al costo del software legacy mostra come si fa il conto sul caso generico.
Che cosa c'è davvero dentro il tuo portale WebForms?
Prima di scegliere una strada serve un inventario, perché il prezzo di qualunque soluzione dipende da quello che c'è dentro, e di solito chi decide descrive solo quello che vede. Un'applicazione WebForms contiene sei tipi di cose, e ciascuna si sostituisce in modo diverso.
- Pagine e controlli utente: i file
.aspxe.ascxcon il loro code-behind. Le maschere si ridisegnano, il code-behind si legge per capire che cosa decide. - ViewState e postback: il modo in cui la pagina ricorda il suo stato. Non si porta, si sostituisce con un modello esplicito.
- Accesso ai dati:
SqlDataSource, DataSet, query sparse nel code-behind, a volte logica dentro le stored procedure. - Moduli e handler:
Global.asax,HttpModule,HttpHandlerper i download, i PDF e le chiamate di integrazione. - Autenticazione e sessione: il vecchio provider di appartenenza, i cookie con il formato di .NET Framework, lo stato di sessione.
- Regole: sconti per cliente, blocchi di credito, arrotondamenti, scorte riservate. Sono il vero patrimonio e sono scritte solo nel codice.
Nel caso dell'Emilia l'inventario, fatto leggendo il progetto in tre giorni, dà questi numeri.
| Elemento | Quantità | Che cosa significa per il costo |
|---|---|---|
| Pagine .aspx | 214 | 61 usate ogni giorno, 88 una volta al mese, 65 mai: queste ultime non si migrano |
| Controlli utente .ascx | 63 | Quasi tutti si riducono a una dozzina di componenti riusabili |
| Griglie con ViewState oltre 500 KB | 17 | Sono le pagine lente: ordini, listini, disponibilità |
| Report e stampe | 41 | 12 sono documenti per i clienti e vanno rifatti con cura |
| HttpHandler e moduli | 9 | Download di PDF, esportazioni, due chiamate usate da altri sistemi |
| Procedure e viste nel database | 118 | In 31 c'è logica che il codice dà per scontata |
| Controlli commerciali di terzi | 5 | Due hanno una versione per Core, tre no |
| Integrazioni | 6 | Contabilità, corriere, EDI di un cliente, listini fornitori, newsletter, firma |
| Test automatici | 0 | Il comportamento va catturato prima di toccarlo |
La riga più importante è quella delle pagine mai usate: 65 su 214. Un terzo del portale non esiste nel conto, perché nessuno lo apre da mesi. Questo da solo abbassa il preventivo di una riscrittura più di qualsiasi negoziazione.
Si può convertire un'applicazione WebForms in ASP.NET Core?
No, e conviene dirlo subito perché è la fonte di quasi tutte le stime sbagliate. Non esiste uno strumento che prenda un progetto WebForms e produca un progetto Core che funziona. Esistono strumenti di analisi che indicano che cosa va cambiato, e librerie che permettono di far convivere i due mondi durante la sostituzione, ma la conversione delle pagine è un lavoro di scrittura.
La ragione è tecnica. In WebForms la pagina è un oggetto con un ciclo di vita (Init, Load, eventi dei controlli, PreRender) che tiene lo stato in un campo nascosto e lo rimanda al server a ogni clic. In ASP.NET Core non c'è niente di simile: la richiesta è un'azione che riceve dati e restituisce una risposta, e lo stato lo gestisce chi scrive il codice. Il passaggio non è di sintassi ma di modello.
Il modello che si avvicina di più per chi ama i componenti e gli eventi è Blazor, che mantiene l'idea di un controllo con il suo stato e i suoi eventi, ma lo risolve in modo diverso: il codice gira sul server collegato con una connessione continua, o nel browser. Per un portale con molti moduli di inserimento dati può essere una scelta sensata. Per un portale con pagine di consultazione e listini, in cui contano velocità e indicizzazione, le Razor Pages o MVC sono quasi sempre una scelta più semplice e più veloce.
| Se il tuo portale è soprattutto... | Il punto di arrivo naturale | Perché |
|---|---|---|
| Consultazione, listini, schede, ricerche | Razor Pages | Una pagina, un modello, poco stato. Veloce e semplice da testare |
| Inserimento di dati con maschere ricche | Blazor | Componenti con eventi, simile a come si ragiona in WebForms |
| Servizi per altri programmi e per il telefono | API minimali | Nessuna pagina, solo contratti chiari |
| Un po' di tutto | Razor Pages più API, Blazor dove serve | Un'area per volta, con la scelta migliore in ciascuna |
Per il caso dell'Emilia la scelta è Razor Pages per consultazione e listini (la maggior parte) e Blazor per l'inserimento degli ordini, dove le maschere sono ricche. Ma il punto non è la tecnologia, è l'ordine: la scelta si fa area per area, non una volta per tutte.
Quanto costa la migrazione da ASP.NET WebForms a Core rispetto a non farla?
Per rispondere servono quattro strade a confronto, con le stesse ipotesi. Qui il modello è in euro e si può rifare con i tuoi numeri. Le ipotesi del caso sono queste.
- Costo medio di una giornata di sviluppo: 420 euro. 220 giornate lavorative l'anno e 32 euro l'ora per il costo di un utente interno.
- Utenti: 38. Ogni minuto perso al giorno per ognuno vale 38 × 220 ÷ 60 × 32 = 4.459 euro l'anno.
- Un fermo del portale (ogni volta che si blocca una giornata di lavoro per quattro ore) vale 4 × 38 × 32 = 4.864 euro ciascuno.
- Rischio della conoscenza persa e dell'ambiente non ricostruibile: stimato a 6.000 euro l'anno se non si fa nulla, 1.500 se si mette in sicurezza, 600 dopo la migrazione.
Strada 0: non fare niente
Nessun costo di avvio. Ogni anno: 24 giornate di manutenzione (10.080 euro), 5.400 euro di server e licenze, 8 minuti persi al giorno per utente (35.672 euro), un fermo (4.864 euro) e 6.000 euro di rischio. Totale: 62.013 euro l'anno. Il punto è che questa strada non è gratis: è la più costosa fra quelle che durano.
Strada A: aggiornare e mettere in sicurezza WebForms
Si resta su .NET Framework 4.8: si aggiornano i pacchetti, si rimuovono le dipendenze morte, si toglie il ViewState dalle 17 griglie lente, si scrivono i test sulle 30 regole critiche, si mette il server in una macchina ricostruibile con uno script, si porta il database sotto controllo. 40 giornate, 16.800 euro. Costo annuo dopo l'intervento: 24 giornate (10.080), 4.200 di server, 6 minuti persi (26.754), mezzo fermo (2.432), rischio 1.500. Totale 44.964 euro l'anno.
Strada B: sostituire un'area per volta con ASP.NET Core
Un guscio Core si affianca al portale, il database resta condiviso e un'area alla volta passa dal vecchio al nuovo. 367 giornate, 154.140 euro, più 20 giornate di doppio mantenimento, 8.400 euro, perché per un periodo si tengono in vita due sistemi. Costo annuo a regime: 14 giornate (5.880), 1.800 di hosting, 2 minuti persi (8.918), nessun fermo, rischio 600. Totale 17.197 euro l'anno.
Strada C: riscrivere tutto e sostituire in un colpo
470 giornate, 197.400 euro, più 26,7 giornate di doppio mantenimento (11.200 euro), perché la riscrittura richiede più tempo prima di andare in produzione. Il costo annuo a regime è uguale alla strada B, 17.197 euro, ma il rischio di arrivare dopo il tempo previsto è maggiore, perché non c'è un prodotto in uso che prova il lavoro un pezzo alla volta.
| Strada | Costo di avvio | Doppio mantenimento | Costo annuo a regime |
|---|---|---|---|
| 0. Non fare niente | 0 | 0 | 62.013 |
| A. Aggiornare WebForms | 16.800 | 0 | 44.964 |
| B. Migrare a moduli con Core | 154.140 | 8.400 | 17.197 |
| C. Riscrivere tutto | 197.400 | 11.200 | 17.197 |
Se vuoi il quadro completo del percorso, dalla diagnosi al piano, la pagina sulla modernizzazione del software legacy lo descrive passo per passo.
Prima di leggere il conto per anni, una nota di metodo: i 367 giorni della strada B non sono un numero sparato. Sono la somma di sei pezzi: 25 per i dati e le regole, 30 per il guscio e l'autenticazione, 240 per le aree, 40 per le integrazioni, 20 per la messa in produzione e 12 per spegnere il vecchio. Se non sei in grado di scrivere i sei pezzi del tuo preventivo, non sei in grado di giudicarlo.
Dopo quanti anni conviene migrare WebForms a Core?
La domanda giusta non è "quanto costa", ma "quanto costa in tutto, per gli anni in cui il portale deve servire". Sommando costo di avvio, doppio mantenimento e costo annuo, il conto cumulato per le quattro strade è questo.
| Orizzonte | 0. Niente | A. Aggiornare | B. Moduli con Core | C. Riscrivere |
|---|---|---|---|---|
| 1 anno | 62.013 | 61.764 | 179.737 | 225.797 |
| 3 anni | 186.039 | 151.692 | 214.131 | 260.191 |
| 5 anni | 310.065 | 241.620 | 248.525 | 294.585 |
| 8 anni | 496.104 | 376.512 | 300.116 | 346.176 |
Leggi la tabella in tre passi. Primo: non fare niente non vince mai, nemmeno al primo anno, perché aggiornare costa 16.800 euro e fa risparmiare 17.049 euro in un anno. Secondo: a uno e tre anni la strada A è la più economica, a cinque anni A e B sono praticamente uguali (241.620 contro 248.525), e dal sesto anno la migrazione a moduli passa avanti: a otto anni costa 300.116 euro contro 376.512, quindi 76.396 euro in meno. Terzo: la strada C, la riscrittura, non vince mai, perché ha lo stesso costo annuo di B ma parte più alta e arriva dopo.
I punti di pareggio, calcolati sulle stesse ipotesi, sono tre.
- Aggiornare contro non fare niente: si ripaga in 0,99 anni, cioè dodici mesi. È l'unico intervento la cui convenienza non dipende da niente.
- Migrare contro non fare niente: si ripaga in 3,6 anni.
- Migrare contro aggiornare: si ripaga in 5,2 anni. Se il portale deve servire meno di cinque anni, si aggiorna e basta. Se deve servirne otto o dieci, si migra.
Il grafico qui sotto mostra le quattro linee: le curve di B e C partono molto più in alto ma crescono più piano.

Che cosa sposta il pareggio
Il numero 5,2 non è una legge, è il risultato di quelle ipotesi. Cambiandone una alla volta, ecco che cosa succede.
| Se cambia... | Il pareggio fra aggiornare e migrare diventa |
|---|---|
| Gli utenti scendono da 38 a 12 | 10,5 anni |
| Gli utenti salgono da 38 a 60 | 3,7 anni |
| Il rischio della conoscenza persa vale zero | 5,4 anni |
| La migrazione costa 480 giornate invece di 367 | 7,0 anni |
| Dopo la migrazione restano 4 minuti persi invece di 2 | 7,7 anni |
L'osservazione che conta: il numero di utenti pesa più del resto. Un portale usato da dodici persone non si migra per convenienza, si aggiorna. Un portale usato da sessanta persone, con agenti che lo aprono ogni giorno, si migra anche a un orizzonte breve. E la seconda: se i minuti persi dopo la migrazione non scendono davvero, tutto il conto cade, quindi il numero da difendere in progetto è questo, non le giornate.
Come si migra WebForms a Core senza fermare il portale?
Il metodo si chiama strangler fig: si costruisce il nuovo intorno al vecchio, un'area alla volta, finché il vecchio non serve più e si spegne. Nel mondo .NET ha due strumenti ufficiali. Il primo è YARP, un reverse proxy scritto da Microsoft per ASP.NET Core, che si mette davanti al portale e decide, per ogni indirizzo, se la richiesta va al nuovo o al vecchio. Il secondo è System.Web adapters, una libreria che permette al codice nuovo di leggere cose che prima vivevano in HttpContext, come la sessione, e di dialogare con l'applicazione vecchia. Per il principio generale, la guida allo Strangler Fig pattern spiega la logica; qui vediamo come si applica a WebForms.
L'architettura è questa.

1. Il guscio Core davanti al vecchio
Si crea un progetto ASP.NET Core che diventa il punto d'ingresso: gli utenti continuano ad aprire lo stesso indirizzo. Il guscio serve le aree già migrate e inoltra tutto il resto al portale WebForms, che continua a girare su IIS dove sta oggi. Il programma di avvio del guscio, nella sua forma più piccola, è questo.
using Yarp.ReverseProxy.Forwarder;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddHttpForwarder();
builder.Services.AddSystemWebAdapters();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSystemWebAdapters();
// Le aree già migrate rispondono dal nuovo.
app.MapRazorPages();
// Tutto il resto va al portale WebForms, a priorità più bassa.
var vecchio = builder.Configuration["PortaleVecchio:Url"]!;
app.MapForwarder("/{**catchall}", vecchio)
.Add(b => ((RouteEndpointBuilder)b).Order = int.MaxValue);
app.Run();Il dettaglio che conta è l'ordine: gli indirizzi del nuovo hanno la precedenza, e il vecchio riceve solo quello che il nuovo non conosce. Ogni area migrata è una pagina aggiunta al nuovo, senza altro da toccare: il vecchio smette di ricevere quelle richieste da solo. Se l'area nuova ha un difetto, togliere la pagina riporta le richieste al vecchio in pochi minuti.
2. Il database resta condiviso
Non si copia il database e non si fa una sincronizzazione. Vecchio e nuovo leggono e scrivono le stesse tabelle, che è la ragione per cui il passaggio si può fare un'area alla volta. Il prezzo è una regola di disciplina: finché un'area è nel vecchio, solo il vecchio la scrive. Quando passa nel nuovo, solo il nuovo. Scritture da due parti sulla stessa tabella sono la causa più frequente di dati guasti.
3. L'accesso utente deve essere uno solo
L'utente non deve fare il login due volte. Il guscio gestisce l'autenticazione e passa al vecchio l'identità in un modo che il vecchio capisce, di solito con un cookie condiviso protetto con una chiave comune. È la parte che richiede più attenzione, e le stime sbagliate nascono qui, con un titolo semplice: "unificare l'accesso".
4. La sessione si porta, ma con cautela
Se il vecchio tiene in sessione il carrello o i filtri di ricerca, il nuovo li deve poter leggere. La libreria dei System.Web adapters permette di condividere la sessione fra i due, ma ogni oggetto deve poter essere serializzato. In pratica i dati di sessione vanno ridotti a pochi valori semplici prima di partire: un identificativo di carrello, non l'oggetto carrello intero.
5. L'ordine delle aree
Si comincia dalle aree a basso rischio che mostrano un valore visibile, non da quelle più importanti. Un buon ordine, nel caso dell'Emilia, è questo: schede articolo e ricerca (consultazione, nessuna scrittura), listini e prezzi, disponibilità, ordini e carrello, area agenti, fatturazione e spegnimento. Le consultazioni vanno per prime perché sono la prova che il guscio regge, e perché sono anche le pagine più lente.
| Mese | Area | Che cosa si ottiene |
|---|---|---|
| 1-2 | Guscio, accesso unico, inventario e test sulle regole | Il portale è invariato, ma ora si può spostare un'area |
| 3-4 | Ricerca e schede articolo | Le pagine più lente scendono da 9 a meno di 1 secondo |
| 5-6 | Listini e prezzi | Gli agenti vedono il prezzo al primo clic, anche dal telefono |
| 7-8 | Disponibilità e scorte | I dati in tempo reale per il cliente della grande distribuzione |
| 9-12 | Ordini e carrello | L'area più complessa, in parallelo di una settimana con due utenti pilota |
| 13-14 | Area agenti, integrazioni residue | Si toglie l'ultimo motivo per tenere il vecchio |
| 15 | Spegnimento di WebForms | Si rimuove il server e il rischio che portava |
Per un portale di queste dimensioni servono quindici mesi, e ogni mese tre cose cambiano per gli utenti. La differenza con la riscrittura non è la durata, è il fatto che dal terzo mese ogni area che esce è già in uso, e ogni errore costa una pagina e non il portale.
Come si sostituisce una pagina WebForms con Razor Pages?
Un esempio concreto aiuta a vedere dove sta il lavoro. Questa è la pagina dei listini di un cliente, nella forma che si trova in un progetto del 2011.
// Listini.aspx.cs
public partial class Listini : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ddlFamiglia.DataSource = Famiglie.Elenco();
ddlFamiglia.DataBind();
Carica();
}
}
protected void ddlFamiglia_SelectedIndexChanged(object sender, EventArgs e)
{
Carica();
}
private void Carica()
{
var cliente = (int)Session["ClienteId"];
var righe = Listino.Righe(cliente, ddlFamiglia.SelectedValue);
foreach (var r in righe)
r.Prezzo = r.Prezzo * (1 - ScontoCliente(cliente, r.Famiglia));
gvListino.DataSource = righe;
gvListino.DataBind();
}
}Si vedono quattro cose che non si portano così come sono. La prima è IsPostBack: la pagina si ricarica da sola a ogni cambio del menu a tendina e si basa sul ViewState per ricordare le scelte. La seconda è Session["ClienteId"]: lo stato in sessione, che nel nuovo sarà un'identità autenticata. La terza è DataBind(): la griglia riceve i dati e ne tiene una copia nel ViewState, che è il motivo dei megabyte. La quarta è l'unica che non si deve perdere: la riga con lo sconto del cliente, cioè la regola.
La stessa pagina in Razor Pages è questa.
// Pages/Listini.cshtml.cs
public class ListiniModel : PageModel
{
private readonly IListinoService _listini;
public ListiniModel(IListinoService listini) => _listini = listini;
[BindProperty(SupportsGet = true)]
public string? Famiglia { get; set; }
public IReadOnlyList<string> Famiglie { get; private set; } = [];
public IReadOnlyList<RigaListino> Righe { get; private set; } = [];
public async Task OnGetAsync(CancellationToken ct)
{
var cliente = User.GetClienteId();
Famiglie = await _listini.FamiglieAsync(ct);
Righe = await _listini.RigheAsync(cliente, Famiglia, ct);
}
}Sono cambiate tre cose. Il menu a tendina diventa un parametro dell'indirizzo, quindi la pagina si può aggiungere ai preferiti e si può mandare a un collega. La sessione sparisce: il cliente arriva dall'identità, non da un valore nascosto. E lo sconto è uscito dalla pagina: vive in IListinoService, dove si può provare con un test. Quest'ultima mossa è il vero guadagno, perché la regola oggi si scopre solo aprendo la pagina.
La regola, nella sua forma di test, è questa.
[Fact]
public async Task Il_prezzo_applica_lo_sconto_del_cliente_sulla_famiglia()
{
var servizio = Fabbrica.ListinoConSconto(cliente: 1042, famiglia: "CAVI", sconto: 0.12m);
var righe = await servizio.RigheAsync(1042, "CAVI", CancellationToken.None);
Assert.All(righe, r => Assert.Equal(Math.Round(r.PrezzoListino * 0.88m, 2), r.Prezzo));
}Un test come questo costa mezz'ora e dura quanto il portale. Scriverlo prima di riscrivere la pagina è la differenza fra migrare e sperare di avere migrato. E il suo valore cresce con ogni area: dopo dieci aree, il portale ha dieci insiemi di regole provate che prima non esistevano.
Quali sono i problemi che fanno saltare le stime?
Le pagine non sono la parte difficile. Lo sono cinque cose attorno alle pagine, che nei preventivi si trovano di rado e che, ignorate, fanno raddoppiare il tempo.
L'autenticazione con il vecchio provider
I portali di quegli anni usano il provider di appartenenza di ASP.NET, con una tabella di utenti e una password con un formato di hash particolare. ASP.NET Core usa un formato diverso. La soluzione senza obbligare nessuno a reimpostare la password è riconoscere il vecchio formato al momento dell'accesso e riscrivere l'hash nel formato nuovo al primo login riuscito. Nel nostro caso è una settimana di lavoro, comprese le prove. Senza la procedura, il giorno del passaggio 38 persone chiedono una nuova password.
Il ViewState nascosto nelle regole
Capita di scoprire che una pagina funziona perché un valore è passato dal ViewState e non dal database. Il valore non c'è più nel nuovo, e la pagina si comporta in modo diverso senza dare errori. La difesa è una sola: i test sul comportamento si scrivono prima, sul vecchio, e si rieseguono sul nuovo.
Le stored procedure con logica dentro
In 31 procedure su 118 c'è una decisione che il codice dà per scontata: uno sconto che dipende dal giorno, un blocco se il cliente è scoperto. Non si riscrivono per prime. Si lasciano dove sono e si chiamano dal nuovo, e si estraggono dopo, quando c'è un test che le protegge. Cambiare insieme la pagina e la procedura è il modo più veloce per non sapere più quale delle due ha rotto cosa.
I controlli commerciali di terzi
Due dei cinque controlli del caso hanno una versione per Core, tre no. Per i tre servono sostituti: una libreria diversa per le griglie, una per i grafici, un generatore di PDF. Ogni sostituzione costa dai 5 ai 12 giorni e cambia l'aspetto delle pagine. Si decide quali sostituire all'inizio, non quando la pagina è già riscritta.
L'hosting e la pubblicazione
Il portale vecchio gira su IIS. Il nuovo può girare su IIS, su un contenitore o su un servizio gestito. La scelta cambia il costo annuo (1.800 euro nel caso) e va fatta presto, perché determina come si pubblica ogni area. Mettere il nuovo sullo stesso server del vecchio è la strada più rapida per cominciare, ma conviene decidere subito dove andrà a regime, per non pagare due volte. Per sapere che cosa si guadagna passando a ASP.NET Core vale la pena rileggere che cosa è cambiato con il rilascio di .NET Core 3.0, e per la pubblicazione delle interfacce che il guscio espone, la guida a pubblicare le API REST.
La somma di queste cinque cose, nel caso dell'Emilia, vale circa 70 giornate su 367. Un preventivo che non le elenca per nome è un preventivo che le sta nascondendo.
Come si decide in una settimana?
Il percorso che uso con un cliente è sempre lo stesso, e si fa in cinque giorni di lavoro.
Giorno 1: l'orizzonte in anni
Una domanda sola al direttore: per quanti anni pensi di usare questo portale? Non "fino a quando funziona", ma un numero. Dieci anni e tre anni sono due progetti diversi. Se la risposta è tre, tutto il resto si semplifica: si aggiorna e basta.
Giorno 2: il conteggio dei minuti
Si siede un'ora accanto a sei utenti, fra interni e agenti, e si annota ogni volta che aspettano, aggirano, ricopiano o telefonano per un dato che il portale non dà. Si cronometra. Il numero che esce, in minuti per utente al giorno, vale 4.459 euro l'anno per ogni minuto con 38 utenti. È il numero più sottovalutato del progetto, e di solito è il più alto.
Giorno 3: l'inventario
Lo scopo è la tabella vista sopra. Si contano le pagine, si guarda quali sono usate dai registri del server (non da quello che dice la gente), si misura il ViewState delle griglie e si elencano controlli, integrazioni e procedure con logica. Costa tre giorni se si parte da zero e uno se i registri ci sono già.
Giorno 4: le regole
Si estraggono le regole di business dal code-behind e dalle procedure, si scrivono in italiano con un esempio numerico per ciascuna, e le si fa leggere a chi lavora. Le regole che nessuno riconosce sono le più preziose o le più sbagliate, e in entrambi i casi vanno scoperte ora e non il giorno del collaudo.
Giorno 5: il conto e la decisione
Con orizzonte, minuti, inventario e regole si compila il confronto delle quattro strade. Se il pareggio fra aggiornare e migrare cade dopo l'orizzonte, si aggiorna. Se cade prima, si migra, e si sceglie il primo modulo.
Alla fine hai quattro cose che prima non avevi: un orizzonte, una cifra di minuti persi, un elenco di regole e un valore del rischio. Con queste la discussione non è più "WebForms sì o no", ma "quale strada riduce il costo e il rischio nei prossimi anni". Per chi ha valutato anche di partire da zero con un software su misura, il conto con le voci del preventivo è in quanto costa un software gestionale su misura, e per scegliere fra riscrivere e ripulire vedi riscrivere o rifattorizzare il software legacy.
Quali errori si ripetono nella migrazione da WebForms?
In ventisei anni di progetti gli stessi errori tornano. Ne elenco otto, con il modo di evitarli.
- Cominciare dalla pagina più importante. Si comincia da una consultazione, che prova il guscio con poco rischio.
- Scrivere il nuovo senza test sul vecchio. Se non sai che cosa fa il vecchio, non sai se il nuovo lo fa.
- Copiare il database "per sicurezza". Due copie dello stesso dato divergono nel giro di una settimana.
- Migrare le pagine che nessuno usa. Sono il 30% del conto e zero del valore.
- Sottovalutare l'accesso. Il giorno in cui 38 persone non riescono ad entrare, ogni altro successo non conta.
- Portare il ViewState. Ricostruirlo in un'altra forma vuol dire portarsi dietro il problema. Si disegna lo stato che serve davvero.
- Usare un solo grande rilascio. Ogni area esce da sola, in orario tranquillo, con la possibilità di tornare indietro in pochi minuti.
- Non misurare il guadagno. Se non misuri i minuti persi prima e dopo, non puoi dimostrare che il progetto ha funzionato, né a te stesso né a chi paga.
Un altro errore, meno tecnico: decidere dal titolo di un articolo. Anche questo, compreso, mostra un caso composto. Il tuo portale ha un'altra curva, e l'unico modo per saperlo è mettere i tuoi numeri nella tabella.
Che cosa farebbe oggi l'azienda dell'Emilia?
Torno al distributore. Il direttore aveva una domanda e un consulente che chiede sempre più tempo. Con il conto in mano avrebbe visto che il "non fare niente" costa 62.013 euro l'anno, che aggiornare WebForms si ripaga in dodici mesi, e che la migrazione a moduli si ripaga rispetto all'aggiornamento in 5,2 anni.
Avrebbe deciso in due tempi. Subito, 40 giornate: togliere il ViewState dalle 17 griglie lente, scrivere i test sulle 30 regole critiche, mettere il server in una macchina ricostruibile e sostituire i tre controlli senza futuro. Costa 16.800 euro, si ripaga in un anno e toglie il rischio più urgente. In parallelo, il guscio Core e la prima area, la ricerca e le schede articolo, perché sono le pagine più lente e quelle che gli agenti aprono di più. Con quella area in produzione, la decisione sul resto si prende con minuti recuperati misurati e non stimati.
La migrazione completa, 154.140 euro più il doppio mantenimento, diventa la scelta giusta se l'azienda pensa di tenere il portale per otto anni o più, se i clienti chiedono i dati in tempo reale, o se il consulente smette prima del previsto. Non è la scelta giusta se il motivo è "WebForms è vecchio": quel motivo, da solo, non è un numero.
In ogni caso avrebbe deciso con un numero e non con la simpatia di un venditore, e avrebbe ancora aperta la strada giusta fra due anni. È questa la differenza, e vale più dell'esito.
Se hai un portale WebForms e vuoi sapere in quale zona cade la tua azienda, il percorso comincia dalla pagina sulla modernizzazione del software legacy, che include anche i casi in cui la risposta è non toccare niente. Se il punto di partenza è un'applicazione .NET Framework di altro tipo, la guida alla migrazione da .NET Framework a .NET 10 fa lo stesso conto, e se il programma è scritto in Delphi c'è la migrazione Delphi a .NET. E se ti serve una persona che guardi i numeri con te, c'è la consulenza software.
In cinque righe. Conta quanti dei sette segnali si applicano al tuo portale e da quanti anni nessuno lo conosce. Misura i minuti persi ogni giorno per utente: valgono 4.459 euro l'anno ciascuno con trentotto utenti. Togli il ViewState dalle pagine lente prima di qualsiasi altra cosa. Estrai le regole dal code-behind e dalle procedure prima di parlare con qualsiasi fornitore. Aggiorna WebForms dove serve, e sostituisci un'area per volta con un guscio Core dove il numero di utenti e l'orizzonte lo giustificano.
Se il programma da modernizzare è un gestionale scritto in Visual Basic 6, il conto a uno, tre, cinque e otto anni, con il ponte COM per sostituire un modulo per volta, è in migrazione da VB6 a .NET.
Domande frequenti
Per un portale con 38 utenti e 214 pagine, sostituire un'area per volta con ASP.NET Core costa circa 154.140 euro (367 giornate), più 8.400 di doppio mantenimento. Riscrivere tutto costa 197.400 euro. Aggiornare e mettere in sicurezza WebForms costa 16.800 euro (40 giornate).
No: in Core non esistono Page, ViewState e controlli server, quindi non c'è una conversione automatica. Si sostituisce pagina per pagina con Razor Pages, MVC o Blazor, con un guscio ASP.NET Core e YARP che affianca il portale vecchio durante il passaggio.
A tre anni aggiornare costa meno (151.692 euro contro 214.131), a cinque anni sono pari (241.620 contro 248.525) e a otto anni migrare costa 300.116 euro contro 376.512. Il pareggio sta a 5,2 anni nel caso descritto e dipende soprattutto dal numero di utenti.
Con lo strangler fig: un guscio ASP.NET Core con YARP si mette davanti al portale, il database resta condiviso e un'area alla volta passa dal vecchio al nuovo. Nel caso descritto servono 15 mesi, e ogni area va in produzione da sola.
Non si porta: si disegna lo stato che serve davvero. Prima della migrazione conviene toglierlo dalle griglie lente, perché in un caso reale pesa anche 1,8 MB, e scrivere test sul comportamento del vecchio da rieseguire sul nuovo.
Cinque: l'autenticazione con il vecchio provider di appartenenza, il ViewState nascosto nelle regole, le stored procedure con logica dentro, i controlli commerciali senza versione per Core e le scelte di hosting. Nel caso descritto valgono circa 70 giornate su 367.
