Back to "Trabajando en las bases de códigos legados (o la vida de un contratista)"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Legacy Code

Trabajando en las bases de códigos legados (o la vida de un contratista)

Wednesday, 06 November 2024

Introducción

Como desarrollador independiente uno de los conjuntos de habilidades que necesita para aprender rápidamente es cómo trabajar en las bases de código existentes con eficacia. He tenido la suerte de haber construido un montón de - sistemas de arañazos; este es un JOY como un desarrollador experimentado sin embargo no siempre es el caso.

Los desafíos

Los sistemas de legado tienen retos significativos; especialmente cuando los desarrolladores / arquitectos clave (si tienes la suerte de tenerlos) han seguido adelante.

Documentación

Esto a menudo se pasa por alto especialmente en las empresas más pequeñas. En general, necesita 4 tipos clave de documentación:

  1. Nuevas guías de desarrollo ¿Cómo se ejecuta el proyecto? local (más sobre esto en un minuto), qué consideraciones hay (si por ejemplo se necesita un marco diferente a la actual, especialmente cierto con Nodo tristemente).
  2. Documentos de despliegue en estos días, cuando CI/CD es visto como el estándar dorado de implementación, a menudo se pasa por alto que necesitas saber cómo implementar el sistema manualmente. Esto es especialmente cierto si usted está trabajando en un sistema que ha estado alrededor por un tiempo y tiene un montón de pasos manuales.
  3. Procedimientos de resolución de problemas; ¿qué haces cuando el sistema se cae? ¿A quién llamas? ¿Cuáles son las cosas clave para comprobar? Esto a menudo se pasa por alto, pero es CRICIAL en un sistema de producción.
  4. Documentos Arquitectónicos / Diseño de Sistemas; ¿cómo funciona el sistema? ¿Cuáles son los componentes clave? ¿Cuáles son las dependencias clave? Piense en esto como su hoja de ruta al aprender un nuevo sistema. Proporciona una visión general de alto nivel de cómo funciona el sistema que puede ser crítico (especialmente en sistemas más grandes y distribuidos). Esto DEBERÍA incluir qué repositorios de código fuente se utilizan, qué sistemas CI/CD se utilizan, qué bases de datos se utilizan para cada elemento de la aplicación. Esto puede abarcar desde un diagrama simple hasta un diagrama UML completo de cada componente del sistema. Por ejemplo, en una aplicación React esto normalmente incluiría un diagrama de los componentes y cómo interactúan entre sí y cualquier servicio de backend. En una aplicación.NET Core esto incluiría un diagrama de los servicios y cómo interactúan entre sí (y el front end y otros sistemas de backend) y posiblemente las vistas de ASP.NET y qué servicios pueden utilizar.

La documentación es una de las cosas que nosotros, como desarrolladores, a menudo odiamos hacer (no es código), pero es crucial. Como puedes ver I LOVE Markdown, con los gustos de Sirena y Plantuml puedes crear algunos diagramas y diagramas de flujo realmente agradables que pueden ser incluidos en tu documentación.

Tiene que ser CURRENT; los desarrolladores a menudo hablan de bit-rot; donde las dependencias de un proyecto se vuelven obsoletos / riesgos de seguridad downright. Esto también es cierto de la documentación; si no es actual no es útil.

Hay diferentes niveles de documentación, pero en general cualquier vez que cambie el código al que se hace referencia en un documento que debe actualizar ese documento.

Funcionamiento del sistema a nivel local

Esto es a menudo un desafío, especialmente si el sistema ha estado alrededor por un tiempo. Usted necesita saber qué versión de Nodo /.NET etc., qué versión de la base de datos, qué versión del marco, etc. Esto a menudo se pasa por alto, pero es CRUCIAL para levantarse y funcionar rápidamente.

A menudo he visto a desarrolladores diciendo que esto no es relevante en estos días de sistemas de nube y aplicaciones distribuidas de gran tamaño, pero no estoy de acuerdo; necesitas ser capaz de ejecutar el sistema localmente para depurar los problemas de forma rápida y efectiva.

  1. Profiling - esto parece haberse convertido en una habilidad'solo para desarrolladores avanzados', pero es crucial poder perfilar tu código para ver dónde están los cuellos de botella. Esto es especialmente cierto en un sistema heredado en el que es posible que no tengas el lujo de poder reescribir el sistema desde cero. Herramientas como dotTrace y dotMemoryson invaluables aquí. Especialmente en las aplicaciones ASP.NET; los problemas más grandes son generalmente 'permisos' en el sistema; fugas de memoria, fugas de conexión, etc. Mientras que su aplicación parecerá ejecutar FINE por un momento usted de repente encuentra que está fallando / el uso de toda la memoria en el servidor.
  2. Encontrar problemas en el depurador - Me guste o no los días de Response.Write en Classic ASP han terminado. Para una aplicación incluso modestamente compleja (especialmente aquellas que no escribiste) es crucial 'pasar por' el código. Seguir una solicitud desde su inicio a través del servicio para identificar qué PUEDE estar sucediendo, qué excepciones pueden no ser capturadas, etc.
  3. Registro - A menudo se pasa por alto (lo siento, pero no hay un filtro de excepción ASP.NET Core en la parte superior de la pila no es suficiente). Remitiéndose a mi Puesto anterior, el registro es una parte crítica en el trabajo en código LOCALLY. Recuerde que puede utilizar tipos de registro de clientes (por ejemplo, utilizando el Generadores de fuentes de registro) en ASP.NET Core. A continuación, puede especificar qué tipos de eventos deben ser registrados y cuáles no. Esto es crucial en un sistema heredado en el que usted no puede tener el lujo de ser capaz de reescribir el sistema desde cero. Para Insights de aplicaciones es posible que desee Viajes de usuario y otras características; sin embargo, tenga en cuenta esto se vuelve caro rápido si se utiliza LogInformation para cada petición. Es posible que desee utilizar un procesador de telemetría personalizado para filtrar las solicitudes que no desea registrar.
  4. Pruebas - Con demasiada frecuencia veo desarrolladores 'testing in preprod' o pensando que la prueba de unidad entonces comprobar en es suficiente en términos de pruebas. Su RARELY ES, hay un montón de valor en ambos usando una herramienta como Resharper / NCrunch para ejecutar automáticamente pruebas de unidad; sin embargo recuerde que las pruebas de unidad son sólo eso; prueban una "unidad" de código. Es necesario mantener la visibilidad en la forma en que el sistema funciona realmente en concierto con otros componentes. Aquí es donde entran las pruebas de integración; prueban cómo funciona el sistema en su conjunto. Puede utilizar una herramienta como SpecFlow para escribir estas pruebas en un formato legible por el ser humano. Una vez más; crucial en un sistema heredado donde usted no puede tener el lujo de ser capaz de reescribir el sistema desde cero.

En CADA proyecto trabajo en el primer paso es conseguir que el sistema (o una gran parte de él) funcione localmente. Al ver el código, ejecutar el código, depurar el código se puede obtener una idea de cómo funciona el sistema.

La base de códigos

En cada sistema este es tu fuente de la verdad, no importa lo que digan los médicos, lo que otros te digan sobre cómo debería funcionar así es como funciona.

Esto a menudo es un reto, es como encontrar tu camino en una nueva ciudad sin hoja de ruta. Por suerte en las aplicaciones usted tiene un punto de entrada (una página de carga / una llamada de API frontal, etc.) Escoge un punto y empieza por ahí.

Use lo que necesite para interactuar con él, ya sea PostMan, Rider's HttpClient o incluso una página web. Engancha a tu depurador, haz la llamada y síguela. Enjuague y repita para cada parte del sistema.

Refactorización

Por lo general deje esto hasta que entienda el sistema. SIEMPRE es tentador 'tirarlo y empezar de nuevo', sin embargo, resistir esta tentación. Especialmente para un sistema en ejecución (generalmente) Funciona reconstruir / incluso refactorizar un sistema es un gran riesgo. Sí, es divertido, pero por cada línea de código que cambies, corres el riesgo de introducir nuevos y emocionantes errores.

Como todo lo demás (especialmente cuando se contrae) es necesario justificar el trabajo que realiza en un sistema. O bien esta justificación debe centrarse en uno de los siguientes aspectos:

  1. Desempeño - el sistema es lento y necesita ser acelerado (de nuevo tenga cuidado, lo que usted encuentra lento puede ser cómo el sistema está diseñado para funcionar). Hacer que una parte fume rápidamente puede introducir problemas más abajo en el sistema. ¿Matarás al servidor de la base de datos, si haces demasiadas peticiones, tienes un mecanismo que no causará excepciones en cascada?
  2. Seguridad - el sistema es inseguro y necesita ser protegido. Este es un sistema complicado, especialmente en un sistema heredado. Con bit-rot puede encontrar que el sistema está utilizando versiones antiguas de bibliotecas que tienen problemas de seguridad conocidos. Esta es una buena justificación para el trabajo; sin embargo, tenga en cuenta que es posible que necesite refactorizar un montón del sistema para actualizarlo; de nuevo, lo que conduce a la cuestión de los "nuevos errores".
  3. Manutención - el sistema es difícil de mantener y debe ser más fácil de mantener. En general, esto se convierte en un montón de trabajo; ¿la vida actual de la base de código justifica esto? Si usted está gastando más tiempo haciendo estos cambios para mejorar la mantenibilidad de lo que nunca ahorraría para el cliente entonces no vale la pena (y de nuevo, cambió el código == nuevos errores ).
  4. Experiencia del usuario - Generalmente doy prioridad a estos temas. Por una usuario no importa si tu código es 1% mejor; ellos son los que PAGAN por el trabajo que haces al final. Si puedes hacer que su experiencia sea mejor de lo que generalmente vale la pena. Sin embargo, tenga en cuenta que esto puede ser una "slippery pendiente" de los cambios; usted puede encontrar que usted está cambiando un montón del sistema para hacer un pequeño cambio para el usuario.

El negocio de trabajar en sistemas de legado

Esto a menudo se pasa por alto; especialmente por los desarrolladores. Tienes que ser capaz de justificar el trabajo que estás haciendo en un sistema. Esto es especialmente cierto en un ambiente de contratación donde te pagan por hora. Al final, no es tu código y no tu dinero. El POR QUÉ estás haciendo un cambio es a menudo más importante que el cambio mismo.

He trabajado como contratista durante más de una década, no es fácil; como contratista cada hora de su tiempo es un "costo" para el cliente. Usted necesita agregar valor de movimiento al sistema de lo que cuesta. Si no lo estás, pronto estarás buscando un nuevo contrato.

Como desarrolladores, tendemos a ser gente de negocios de mierda que estamos enfocados en la "perfección" en cada momento. En realidad, usted no necesita hacer un sistema 'perfecto' (Yo diría que no hay tal cosa); usted sólo necesita entregar valor al cliente.

En un compromiso a más largo plazo esto INCLUYE asegurar cualquier nuevo el código es mantenible y rentable de ejecutar. En los sistemas heredados esto es MUCH HARDER. Usted a menudo tiene que vadear a través de un pantano, estar ansioso de que a medida que aprende el sistema que NO PUEDE ofrecer mucho valor. I'm not making any changes ¿Crees que I'm just learning the system. Esto es una falacia; estás aprendiendo el sistema para hacerlo mejor. Estás aprendiendo el sistema para hacerlo más eficiente. Estás aprendiendo el sistema para hacerlo más sostenible. Si un cliente no puede aceptar este paso, entonces usted necesita ser muy cuidadoso sobre cómo se comunica esto (o buscar un nuevo contrato).

El pueblo

Una vez más, a menudo se pasa por alto, la mayor parte del tiempo que te traen como contratista porque alguna persona clave se ha ido (no te involucres en la política de esto; no es asunto tuyo). Usted necesita ser capaz de trabajar con las personas que están allí para lograr los objetivos del proyecto. En un contrato por lo general tendrá los términos de su compromiso explicados (en mi contrato actual es'mejorar la fiabilidad y reducir los costos de funcionamiento'); concéntrese en esto. Si no estás seguro de lo que esto significa entonces pregúntale.

Tenga en cuenta quién es su contacto directo; especialmente en los primeros dos meses. Manténgalos informados (es probable que no tenga nuevas características sobre las que alardear a medida que se gira sobre cómo funciona el sistema). Generalmente envío un resumen de correo electrónico cada semana / dos semanas a mi contacto directo; esta es una buena manera de mantenerlos informados de lo que estás haciendo y lo que estás encontrando.

Recuerde, esta es la persona que aprobará su factura; no debe haber sorpresas en el momento de la factura. Una vez que usted comienza a comprobar en código regularmente esto es menos de un problema; usted tiene un registro de exactamente lo que hizo y el impacto que tuvo. Hasta entonces, tienes que mantenerlos informados. Necesitan saber lo que hiciste y por qué deberían pagarte por ello.

Fiabilidad

De nuevo, de vuelta al código heredado; si usted hace un cambio en general usted necesita implementarlo.Incluso los mejores de nosotros FOLLARÁN ESTO de vez en cuando, especialmente en los sistemas de código heredado habrá algo No podías saberlo. cuando te despliegas. Esto vuelve a registrar - si usted tiene un servidor de puesta en escena tiene que registrar un LOT (pero retenido por un período corto) entonces cuando se despliega a ESTE usted puede recopilar más información sobre lo que falló.

No importa lo bueno que seas, las pruebas locales que hayas hecho todos somos humanos. Esta es una parte clave de la regla de 'no desplegarse en viernes'; espere que un despliegue cause un nuevo problema en un sistema. Prepárate para trabajar hasta que se resuelva. Si no SABES por qué falló, agrega más pruebas para reproducir el problema y más registros para asegurarte de que captures problemas similares en el futuro.

Especialmente para los sistemas de producción su sistema de estadificación puede no ser 1:1 (especialmente en lo que se refiere a la carga), herramienta como k6 puede ayudarle a simular la carga (incluso mejor localmente donde se puede hacer el perfil adecuado como se mencionó anteriormente).

Despliegue

Una vez más, a menudo se pasa por alto en el fervor para CI/CD es el POR QUÉ de estos. Sencillo, te vas a cagar. Tener una forma muy rápida y eficiente de implementar un sistema significa que cuando lo rompes también puedes arreglarlo más rápidamente. Si su sistema de revisión de código CI significa que tarda 2 días en fusionarse con un PR, entonces es el más rápido que puede fiar razonablemente un sistema. Si su sistema de CD significa que usted quita el sistema en ejecución; acostúmbrese a largas noches.

Un mecanismo eficiente para fijar e implementar código es esencial para una tubería de desarrollo eficiente. Si tarda más tiempo en implementar una solución de la que tardó en encontrar e implementar esa solución en código, entonces es menos probable que arregle cosas.

'Arreglar aplicaciones de legado'

Puse esto entre comillas ya que esto es un problema; para las aplicaciones heredadas (especialmente cuando la reelaboración a gran escala está fuera de límites) hay dos enfoques principales.

  • Parches en ejecución

Este es el proceso de simplemente fijar el código existente; de nuevo asegúrese de probar a fondo y tener procesos en su lugar para revertir / redesplegar rápidamente cualquier corrección. No voy a mentir este tipo de desarrollo es rara vez FUN ya que es probable que todavía vadeando a través del pantano de la base de código existente. Sin embargo, es un mal necesario en muchos casos.

Como de costumbre debe asegurarse de que tiene ALGO FORMULARIO de prueba para excercisar el código actual; idealmente también debe probar el fallo para el problema que está tratando de resolver ANTES de hacer la corrección . Ellos IDYLL para esto es tener una prueba de unidad que apunta de cerca el área de código que usted necesita fijar pero esto está a menudo fuera del alcance del trabajo para los sistemas grandes y distribuidos.

Generalmente usaría un sistema de pruebas en este orden de preferencia:

  1. Pruebas de unidad - de nuevo estos son preferidos, ya que puede dirigirse a ellos para ejercer sólo el código en el que está trabajando. Sin embargo, en un sistema heredado estos a menudo no están presentes y son extremadamente difíciles de adaptar (por ejemplo, en un sistema con muchas llamadas externas / que es desordenado en cómo se construye; no usar DI, por ejemplo).
  2. Pruebas de integración - Este es un término sobrecargado ya que puede cubrir cualquier cosa desde una prueba de unidad que pasa a través de múltiples capas de código (estos se evitan mejor) a utilizar los gustos de la excelente Verificar incluso hasta las pruebas de selenio. En general, desea probar el sistema en su conjunto; sin embargo, tenga en cuenta que estas pruebas pueden ser lentas y quebradizas. Utilizando Verify a menudo puede ser un gran enfoque para los sistemas heredados, ya que al menos puedes'verificar' que no estás rompiendo la superficie de API del sistema.
  3. Pruebas manuales - Cada desarrollador debe probar y ejecutar pruebas manuales para cualquier chequeo de código. Esto es sólo asegurarse de que lo que usted espera que el "usuario" (esto puede ser un usuario real u otro componente del sistema que interactúa con su código) para ver es lo que realmente ven. Esto puede ser tan simple como ejecutar el sistema localmente y comprobar la salida de una página o tan complejo como ejecutar un conjunto completo de pruebas en un servidor de puesta en escena.
  • Desollamiento de la cebolla Para trabajar en sistemas heredados por lo general no tendrá el lujo de una revisión completa. Aquí es donde utilizo el enfoque de "despellejamiento de cebollas".

Para poder actualizar PARTS de un sistema puede identificar componentes que pueden separarse de un monolito existente en un microservicio (para algún valor de'micro'). Esta puede ser una gran manera de empezar a modernizar un sistema sin el riesgo de una revisión completa. Ejemplos comunes podrían ser dividir los puntos finales de API en nuevos proyectos que utilizan elementos más actualizados. Sin embargo, el terror de la DRY puede entrar en juego aquí. Una solución mal estructurada a menudo tiene muchos componentes de 'ayuda' o'servicio' que realmente deberían estar en diferentes proyectos (o incluso en paquetes de nuget para una reutilización más global).

Cubriré más este enfoque en un artículo futuro, ya que es un elemento clave de cómo trabajo en sistemas heredados y no es obvio para muchos desarrolladores.

Obtener una paga

Ahora que tenemos todo esto fuera de la manera allí viene el problema espinoso de pago. Tengo una regla general:

Cualquier problema de pago que estoy buscando para seguir adelante

Si están atrasados en el pago; depende de su contrato pero al menos dentro de 30 días, pero generalmente más cerca de 7; esto es una mala señal. Si hay quibbles sobre tu factura (nickle-and-dimeing) piensa si la compañía es capaz de pagar tus servicios de forma continua.

No te metas con esto; si has hecho tu trabajo necesitas que te paguen de una manera oportuna. No importa cuánto lo disfrutes; si un mal cliente puede explotarte lo harán. Nunca vale la pena.

Sé honesto; sólo cobra por el tiempo que realmente trabajaste; asegúrate de que está claro lo que hiciste y por qué lo hiciste.

He dirigido equipos de desarrolladores y he sido desarrollador; he sido contratista y he sido cliente. He visto todos los lados de esto y puedo decirte; si no te pagan a tiempo entonces tienes que seguir adelante. En el otro lado si usted tiene un contratista (o un EFT) que no está entregando entonces usted necesita abordar esto rápidamente. Todo el mundo tiene problemas personales, pero especialmente en un entorno de contrato que necesita para entregar valor o no cobrar por el tiempo cuando no lo está.

En cuanto a la tasa, encontrarás tu nivel; personalmente cobro más por proyectos donde tengo más responsabilidad (o que no parecen divertidos). Cobro menos por proyectos donde estoy aprendiendo una nueva tecnología o por startups. También he trabajado menos días, pero mantuve mi tarifa estable. Pero no aceptes una tarifa baja; eres un profesional y deberías ser pagado como tal.

Conclusión

Bueno, eso es todo. Estoy fuera del trabajo hoy y mañana para el funeral de mi abuela y francamente en pánico un poco de que tengo un sistema de legado enorme para aprender, así que pensé en vomitar mis pensamientos sobre lo que el trabajo en estos sistemas es como y los desafíos.

logo

©2024 Scott Galloway