Pensando en estrategias de migración de VB6 a VB.Net

Casi a hilo de lo que comentaba en el anterior post, hace poco Jorge Serrano publico Cada vez que migras una línea de código de VB6 a .NET se muere un gatito en el mundo, interesantisimo post que vale la pena leer pues resume muy bien los dilemas, actitudes,creencias erróneas y decisiones que se acometen cuando se tienen aplicaciones en Visual Basic 6 y se piensa el migrar a .Net como una opción a considerar.

Me detendré poco en el tema de la discusión de si migrar si o migrar no, pero en mi modesta opinión creo que los criterios de discusión deberían girar alrededor de (entre otras) estas preguntas:
– ¿Qué tan diferente son los procesos de negocio de la organización de como eran cuando la aplicación fue construida?
– ¿Son los algoritmos técnicos de las aplicaciones aun validos para los procesos de datos?
– ¿El esfuerzo de “entender” la aplicación actual y documentar la especificación de la nueva es mayor que el de la “simple” migración? (Cuando no queda nadie del equipo original)

Llegados a nuestro escenario “pues si, necesitamos migrar esa aplicación pues ya cada vez es mas difícil mantenerla y vamos a migrar de plataforma….. ” hay que plantearse las cosas de como acometerla, no tanto desde el punto de vista técnico, que para eso hay suficiente información en Internet, sino mas bien de cuales serian los pasos razonables para lograr el objetivo de una migración exitosa.

Con esas miras puestas, la experiencia que he tenido recientemente me llevan a plantear dos pequeñas metas antes de la migración en si:
– Reducir todo lo que se pueda la cantidad de lineas a migrar.
– Reducir y aislar los comportamientos críticos no migrables.

Cuando hablo de reducir la cantidad de lineas a migrar, parto del hecho de que es mas que probable que la aplicación a migrar hay sufrido modificaciones durante su tiempo de vida, lo cual haya ocasionado que métodos o clases que antes eran invocados se hayan convertido en código muerto, y reducir ese código muerto antes de la migración deberá ser uno de los principales objetivos que debamos de plantearnos, puesto que es preferible saber de antemano que código ya no es invocado (y eliminarlo/comentarlo) que matarnos en hacer que funcione para luego saber que … sorpresa.. sorpresa… era código muerto.

El tema de los comportamientos de lenguaje no migrables (“As any”, redimensionamiento de arrays …) es complicado, por lo que creo que es conveniente, en la medida de lo posible tomar las acciones para reducirlos antes de la migración y si eso no es posible marcar dicho código como problemático.

Ahora bien, estos son objetivos loables, pero lamentablemente difíciles de conseguir con las herramientas de Microsoft “out of the box”, que si que el Code Analysis de VS .Net nos facilitara el encontrar las referencias “muertas”, pero lamentablemente el tener una información completa de este tipo solo sera posible cuando el código migrado compile sin errores, pero claro… volvemos a lo mismo podemos pasarnoslas divertidos corrigiendo código para luego descubrir que ese código no era invocado en ningún lugar.

Entonces queda claro que el análisis de dependencias es critico, y lamentablemente no queda sino recurrir a herramientas de terceros, concretamente he estado probando Project Analyzer con resultados muy interesantes pues ademas de encontrarme con la opción de encontrar todo el código muerto, me detecto todos las porciones de código que requerían migración manual.

Así pues, creo que el contar con una herramienta de ese estilo (he mencionado Project Analyzer, pero igual podría ser alguna otra) cobra un importancia muy especial en esta clase de proyectos, ya que permite cubrir los dos primeros objetivos señalados y llevando de rebote una ventaja adicional: darnos cuenta, mediante el análisis de dependencias de que porciones de código no compatible como llamadas a la API de Windows, podrían ser reemplazadas por código .Net.

Ese análisis e identificación de codigo critico seria un primer paso, de ahí vendrían los siguientes:
– Refactorización, si, se puede, y nos ayudara a tener nuestro código heredado mas legible.
– Preconversión de código no migrable a código migrable, donde sea posible y se sepa que se seguirá usando luego de la migración.
– Migración en si, ya sea usando Visual Studio o una herramienta de terceros, y de ahí las sucesivas iteraciones para estabilizar el producto hasta el nivel deseado (reemplazo de ADO por ADO.Net, eliminación de los Interop….)

Es obvio que este proceso iterativo por lo que no conviene dar un paso nuevo hasta que no se sepa que la nueva versión sigue haciendo lo que hacia antes, de ahí la importancia de tratar de acometer la mayoría de los cambios posibles antes de la migración en si.

¿Qué le hubieramos pedido a un hipotetico VB 6.5?

Pues si, ya han pasado poco mas de 10 años en que .NET fue anunciado (recordemos que antes era llamado NGWS), por lo que ya tendría poco sentido hablar de su predecesor, pero la experiencia de volver a tener que lidiar con VB6 me ha hecho volver a reflexionar algunas cosas…

Primero situémonos en el contexto, VB6 fue lanzado durante 1998 he incluía mejoras como: ADO 2.0 (un enésimo santo grial en la unificación de los modelos de acceso a datos), WebClasses, mejoras en el soporte a COM e integración con MTS; con todo no fue un modelo rupturista y las empresas entusiastamente dieron el salto desde VB5 (versión que por primera vez libero a VB del p-code permitiendo compilar en modo nativo).

Todo bien excepto algunos detalles…. como la herencia, lo cual le hacia ser denostado como un lenguaje no totalmente orientado a objetos, palideciendo en ese sentido ante sus rivales: Delphi y Java. Admitamoslo, durante buen tiempo los fans de VB actuaron con la política de la zorra y las uvas, diciendo que la herencia no era totalmente necesaria, y pregonando la potencia de las interfaces para lograr el polimorfismo. Aun así Microsoft decidio explorar por el lado de una OOP sin las complicaciones del C++, y el resultado fue Visual J++ 6.0 el cual tuvo problemas con Sun, por lo que el producto no tuvo tanto éxito, aunque si que me he topado con un cliente en el cual había herencia de su uso.

Visto ese escenario, para principios del 2000 se rumoreaba sobre lo que seria el inminente ASP+ y el NGWS (Next Generation Windows Services), pero Microsoft sorprendió al mundo cuando en Julio anuncio a .NET, C# y el hecho de como el nuevo VB.Net seria un lenguaje totalmente orientado a objetos, luego vendrian las primeras versiones beta de Visual Studio .Net, sobre cuyo primer contacto ya me he explayado.

Como ya he comentado mi adaptación como desarrollador al nuevo entorno fue fácil, gracias a la base OOP proveída por Delphi, así como a las buenas practicas de los cursos de certificación, pero eso no impedia que mantuviera un ojo en los problemas que se iban a presentar en la transición de VB6 a VB.NET….

Estos cambios venian dados por dos lados: cambios en el lenguaje y cambios en el Framework (de COM a .NET) y ambos eran muy agresivos, tan asi que dos respetados gurues de la época dieron su explicación (muy acalorada por cierto) de porque VB.Net era un nuevo lenguaje (para ellos peor) y no una evolución de VB6:

Karl E. Peterson: VB Fred, A Marketeer’s Worst Nightmare
Bruce McKinney: The End of Hardcore Visual Basic

Sugiero dar un vistazo a esos artículos y a los enlaces ahí contenido para entender la magnitud de los cambios, y si bien luego las cosas no fueron tal como se pintaban en la beta, los cambios si que son muy rupturistas e intentar asumir una migración es algo de veras intimidante.

He realizado migraciones de aplicaciones .NET de la version 1.0 a 1.1 y de la 1.1 a la 2.0 y se puede decir que el camino es mas o menos controlado, pues el lenguaje siempre es compatible hacia atrás, y si bien el Framework evoluciona, dejando algunas tecnologías obsoletas (como Remoting) siempre se puede recompilar en una versión superior, siendo que los potenciales problemas están por el lado de:

– Dependencia de una librería de terceros, cayendo en esta categoría el Enterprise Library, el cual si que tuvo cambios que obligaban a modificar el codigo, problema no presentado en una libreria comercial como Dundas.
– Necesidad de apuntar a una plataforma especifica (como x64 o Itanium), lo cual sumado a lo anterior da para pruebas y comprobación de entornos.
– Alguna cosa que compile, pero que en ejecución falle, son pocas pero las hay, aunque siempre se pueden controlar añadiendo un mayor control de excepciones.
– Cambio de estructura de proyectos, como en el caso de los Servicios Windows, pero se soluciona creando el proyecto desde cero e inyectando progresivamente las clases y métodos necesarios.

En resumidas cuentas, un camino que puede ser mas o menos pesado dependiendo de cuan complejo sea el proyecto, pero en el cual siempre hay vías de hacer que la migración funcione.

Planteemoslo de otra manera, ¿para 2002 habían en producción aplicaciones en VB5 o VB4? Lo dudo sinceramente, el punto de quiebre fue VB5 y como comentaba la migración de VB5 a VB6 era totalmente transparente, salvo (como de costumbre) en la dependencia de algún control de terceros.

Osea, que para migrar dentro de versiones de VB.NET o de Visual Basic “clasico” los caminos han estado mas o menos marcados y eran controlables, pero… para migrar de VB6 a VB.Net… ahi es otra cosa.

Como mencionaba lineas arriba los cambios vinieron por dos frentes: cambios traumaticos en el lenguaje y reemplazo del framework basado en COM por uno basado en .NET. (los dos primeros capitulos de este libro pueden dar una vision de los cambios), si le sumas esto al hecho de que cuando Anders Hejlsberg acometio el desarrollo de .Net, decidió crear un nuevo lenguaje C#, e hizo muchos de los diseños del CLR y el CLI sin tomar en cuenta las particularidades (ya muy usadas por los desarrolladores) de VB, esta claro que lidiar con los cambios seria una tarea harto compleja, cito el libro arriba mencionado:

The Decision to Break Compatibility

When did Microsoft decide to break compatibility with Visual Basic 6? It was actually in early December 1999, during the development of Visual Basic .NET. Until that time, Visual Basic .NET was being developed to support the notion of “Visual Basic 6 sourced” projects that allowed you to edit and compile Visual Basic 6 projects in Visual Basic .NET. These projects would have a compatibility switch turned on, meaning that the language would be backward compatible with Visual Basic 6 and would even have access to the old Visual Basic 6 forms package.
By the end of 1999, it was obvious that this strategy wasn’t working.
Little differences were slipping through: The old forms package could not be fully integrated into .NET, and the Visual Basic 6 sourced projects could not use some of the new features of the .NET platform. At that point Microsoft made the decision to break compatibility and instead concentrate on ensuring that people could upgrade their projects from Visual Basic 6 to Visual Basic .NET.

En fin, es cierto que muchos cambios (de lenguaje) eran inevitables y demandados por la comunidad de desarrolladores: soporte de herencia, excepciones y threading, pero esta claro que Microsoft pudo haberlo hecho mejor en algunos aspectos (de hecho, parte de las criticas de los gurus arriba citados van por el lado de que algunos cambios no eran necesarios para hacer a VB un lenguaje OOP) como la gestión de los arrays, no forzando a tener que hacerlos basados-en-zero y si por el contrario permitir que su gestión de limites sea equivalente a la de VB6.

Entonces esa es la situación a la que llegamos cuando sale al mercado Visual Studio .Net 1.0, el gap del Framework y el lenguaje es tan grande que solo proyectos triviales son susceptibles de ser migrados a .NET, para lo demás muchas veces es mejor hacer el proyecto desde 0, o sino… seguir manteniendo y parchando el código en VB6 hasta que se decida tomar la decisión de migrar (pues ya se sabe lo que dicen las gerencias “Si funciona ¿para que tocarlo?”, claro, es así como el COBOL no muere). La dura realidad es esa, que las organizaciones se han quedado con un parque de aplicaciones para un lenguaje/plataforma que ya no evoluciona y si bien ahora se cuenta con una buena herramienta como el Artinsoft Visual Basic Upgrade Companion, la verdad es que en todo proyecto complejo nos toparemos con situaciones como estas:

– Las inevitables llamadas a la API de Win32, ya no funcionan, y no hay forma directa de hacer un cambio que permitan la recompilacion.
– A revisar todos tus arrays para estar seguro de su comportamiento.
– Replantearte como gestiona el ciclo de vida de tus objetos.
– Los controles y clases tienen otras propiedades, no hay equivalente directo en .NET para algunas clases de terceros y la importación (wrapping) no siempre es segura.

Previo al lanzamiento de VS.Net, algunos articulistas pedían que saliera una ultima versión de VB basada en COM que facilitara la transición y/o introdujera algunas mejoras a VB6, la verdad es que mire de lejos esas peticiones, claro… estaba entusiasmado por el giro a OOP que no mire la complejidad que estoy narrando, pero ahora luego de estos 10 años de .Net (contando desde su primer anuncio y Beta) creo que si que Microsoft debió intentarlo y proveer esa herramienta a fines del 2000 o principios del 2001 a fin de que hubiera un punto intermedio en el camino desde VB6 hacia .Net, lo cual me lleva a la pregunta planteada como titulo de este post….

Personalmente yo hubiera excluido la herencia como caracteristica de un VB 6.5, implementarlo es complejo y el problema radica en como gestionar caracteristicas usadas en las aplicaciones hechas en VB 6 para que funcionen luego en .NET, siendo asi, VB 6.5 debería haber tenido (como anticipo al cambio en .Net):

– Soporte dual para arrays con limites libres y basados-en-zero. Si MS seguia terco en lo de no permitir los limites libres, la compilación debería permitirlo pero indicar que esa característica estaba “deprecada” y no soportada en futuras versiones.
– Fin de las propiedades por defecto de los objetos.
– Eliminación del tipo Variant.
– Cambio en el ámbito de la visibilidad de las variables.
– Uso obligado de paréntesis en las subrutinas.
– Ofrecer un mecanismo de llamadas a la API Win32 cercano al uso que luego tendria DLLImport, pero respetando (temporalmente) la mayoría de los hacks que se tuvieron que hacer para trabajar con dicha API.
– Opcional: soporte para try..catch..finally
– Únicos cambios en el IDE: Encontrar todas las referencias, herramienta imprescindible ahora para una adecuada inspección del código, y un compilador que devuelva una lista de errores y no uno por uno.

La idea de esto seria que los desarrolladores acometieran ciertos cambios sin retorno, sin todavía decirle adiós a COM, para de esta manera tener hechos los deberes en el grueso del lenguaje, quedando (en teoría) cambios menores en el lenguaje y “solo” adaptarse al .Net Framework.

Si Microsoft hubiera hecho esto, a estas altura no habria esas aplicaciones en VB6 que se resisten a morir, haciendo a VB6 el nuevo COBOL, pues eso es lo que esta pasando… un nicho de aplicaciones que persisten mas alla de su ciclo de vida.

Y tu ¿que característica le hubieras puesto a un VB 6.5 todavía basado en COM?.