ChatGPT en el Desarrollo de Software

En esta ocasión voy a plantear el uso de ChatGPT, la principal herramienta de inteligencia artificial de uso común en el 2023-2024. Esta herramienta está cambiando la forma en que trabajamos y quiero compartir con cómo pueden beneficiarse de ella, para mejorar la productividad y la eficiencia de los desarrolladores.

Chat GPT: ¿Qué es?

Es un modelo de lenguaje basado en inteligencia artificial, desarrollador por OpenAI, basado en arquitectura GPT (Generative Pretrained Transformer). Su objetivo principal es generar texto coherente y relevante en respuesta a las entradas de los usuarios, simulando una conversación humana.

Características de ChatGPT:

  • Entrenamiento Previo: El modelo se entrena utilizando grandes volúmenes de texto disponibles en Internet. Durante este entrenamiento, el modelo aprende a predecir la próxima palabra en una secuencia dada, lo que le permite construir oraciones y párrafos completos.
  • Amplia Base de Conocimiento: ChatGPT ha sido entrenado con una vasta cantidad de información que abarca numerosos temas. Aunque anteriormente solo utilizaba este conocimiento para proporcionar sus respuestas, ahora dispone de acceso a internet. Esto brinda a los usuarios la posibilidad de acceder a datos frescos y enlaces directos a las fuentes, lo que antes resultaba inalcanzable.
  • Atención y contexto: Utiliza una arquitectura de modelo llamada Transformer, diseñada para entender el contexto de un texto de manera profunda. Esto le permite generar respuestas coherentes y relevantes a la conversación. La arquitectura de Transformer se basa en mecanismos de atención que permiten al modelo ponderar la importancia de diferentes palabras en el texto de entrada. Esto ayuda a entender el contexto y generar respuestas más precisas.

Aplicaciones de ChatGPT en el Desarrollo de Software

Generación de Código: mediante el lenguaje natural podemos transformar este en fragmentos de código funcionales. Por ejemplo, un desarrollador puede describir una función cómo si estuviera describiendo su funcionamiento y ChatGPT generará el código correspondiente en Python, JavaScript, u otro lenguaje de programación. Esto no solo ahorra tiempo, sino que también ayuda a los desarrolladores a superar bloqueos creativos y a enfocarse en tareas más complejas.

Depuración de Código: Detectar, Identificar y corregir errores en el código aveces se puede convertir en una tarea ardua. ChatGPT puede analizar el código y sugerir correcciones, ahorrando tiempo y reduciendo el margen de error humano. Por ejemplo, al presentar un error específico, ChatGPT puede ofrecer una solución detallada, explicando los cambios necesarios para resolver el problema.

Documentación: Escribir documentación técnica y comentarios en el código puede ser tedioso. ChatGPT puede generar documentación detallada basada en el código y sus funciones, asegurando que la documentación esté siempre actualizada y sea precisa. Esto es especialmente útil en proyectos grandes, donde mantener una documentación coherente puede ser un desafío.

Ventajas y Desventajas

Ventajas:

  • Aumento de la productividad: ChatGPT acelera el proceso de desarrollo, permitiendo a los desarrolladores centrarse en tareas más complejas y creativas.
  • Reducción de errores: ChatGPT puede identificar errores y sugerir correcciones, mejorando la calidad del código.
  • Aceleración del desarrollo: La generación automática de código y documentación agiliza significativamente el desarrollo de software.

Desventajas:

  • Dependencia excesiva: Confiar demasiado en la IA puede llevar a una pérdida de habilidades técnicas fundamentales.
  • Errores en el código sugerido: Aunque ChatGPT es potente, no es infalible y puede sugerir código incorrecto.
  • Consideraciones éticas: El uso de IA plantea cuestiones sobre la propiedad del código y la privacidad de los datos.

Conclusión

La llegada de ChatGPT está transformando industrias, haciendo que está sean cada vez más productivas y eficaces, en el desarrollo del software como es normal esto se ve reflejado todavía más, por la naturaleza de la actividad proporcionando beneficios significativos en términos de productividad y calidad del código. Por esta razón y lo descrito en el artículo, animamos a los desarrolladores a probar esta herramienta y integrarla en su conjunto de herramientas de uso corriente.

Novedades sobre C# 13 & .NET 9

C# 13

La nueva versión de C# 13 salida recientemente, que estará disponible en .NET 9, disponible en la versión más actualizada de Visual studio 2022. Esta versión incluirá una serie de características que estarán disponibles en la página de novedades de #13 (Novedades de C#), por el momento están en versión preliminar y no disponibles oficialmente.

Novedades importantes publicadas actualmente.

Atributo InlineArray en estructuras record no estará disponible.
El atributo InlineArray en C# es una característica utilizada para optimizar el almacenamiento de matrices (arrays) pequeñas en la pila en lugar de en el heap, mejorando así el rendimiento. Este atributo permite definir un campo como un array in-line, es decir, que se almacena directamente dentro de la estructura en lugar de ser una referencia a una ubicación separada en el heap. La combinación de InlineArray  con los registros de estructuras nos permite obtener los beneficios de ambas en conjunto.
En esta versión pasará a retirarse esta estructura:

Los iteradores introducen un contexto seguro en C# 13 y versiones posteriores. Lo que significa que el comportamiento predeterminado será como si estuviera en un contexto seguro, permitiéndonos en caso de no desearlo indicarlo explícitamente que no deseamos este comportamiento.

Otras características anunciadas

Nueva secuencia de escape: puede usar \e como una secuencia de escape literal de caracteres para el carácter de ESCAPE (U+001B en unicode). Anteriormente, se usaba \u001b o \x1b.

Tipos naturales de grupo de métodos: Esta característica realiza pequeñas optimizaciones para solucionar sobrecargas con grupos de métodos.                         Lo que  proporciona:

  • Mejor Eficiencia: Al reducir el conjunto de métodos candidatos a considerar, mejora el rendimiento del compilador y reduce el tiempo de compilación.
  • Claridad y Mantenimiento: Proceso de resolución más claro y fácil de entender, facilitando la depuración y el mantenimiento del código.
  • Predictibilidad y Consistencia: Resultados de la resolución de sobrecargas más consistentes y predecibles, aumentando la confiabilidad del compilador.

Acceso a índices implícitos:

En C# 13, se introduce la capacidad de usar el operador de índice implícito “from the end” (^) en inicializadores de objetos. Esto permite acceder a los elementos de una colección o matriz desde el final en lugar de desde el principio, lo cual no era posible en versiones anteriores.

Esta característica es especialmente útil cuando se trabaja con colecciones donde solo se necesitan modificar los últimos elementos, proporcionando una sintaxis más directa y menos propensa a errores.

Características que podríamos esperar(No todas anunciadas oficialmente):

Escape character

  • Mejora en el manejo de caracteres de escape.

Method group natural type improvements

  • Mejora en la tipificación natural de grupos de métodos.

Lock object

  • Mejoras en el manejo de objetos de bloqueo (lock).

Implicit indexer access in object initializers

  • Acceso implícito a indexadores en inicializadores de objetos.

Params-collections

  • Soporte para colecciones en parámetros (params).

Ref/unsafe in iterators/async

  • Permite código no seguro (unsafe) y referencias (ref) en iteradores y métodos asincrónicos.

Allows ref struct constraint

  • Permite restricciones de ref struct en interfaces.

Overload Resolution Priority

  • Mejoras en la prioridad de resolución de sobrecargas.

Partial properties

  • Soporte para propiedades parciales.

.NET 9

La nueva versión de .NET 9, sucede a la versión .NET8, con un enfoque basado en el rendimiento y las aplicaciones nativas en la nube.

Podemos ver que para la versión preview 2, se ven algunos cambios el las bibliotecas principales .NET.

Estos serán los siguientes puntos mas relevantes que se verán actualizados::

  • Serialización
  • LINQ
  • Colecciones
  • Criptografía
  • Reflexión
  • Rendimiento
  • Pruebas unitarias

Serialización

La biblioteca System.Text.Json incorpora nuevas opciones y un nuevo singleton que facilita la serialización.

Opciones de sangría

JsonSerializerOptions permiten personalizar el carácter y el tamaño de sangría del json.

Opciones web predeterminadas

El nuevo singleton JsonSerializerOptions.Web permite serializar de la forma que lo hacer ASP.NET Core para aplicaciones web.

LINQ

Se han incluido dos nuevos métodos:

  • CountBy:  que permite calcular la frecuencia de cada clave.
  • AggregateBy: permite agrupar datos basados en una clave específica y aplicar una operación de agregación (como una suma) sobre los valores asociados a esa clave.

Ejemplo:

Colecciones

En las colecciones  PriorityQueue<TElement,TPriority>, incluye un nuevo método Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) , que puede usarse para modificar la prioridad de un elemento de la cola.

    // Scan the heap for entries matching the current element.

    queue.Remove(element, out _, out _);

    // Re-insert the entry with the new priority.

    queue.Enqueue(element, priority);

Eliminación del Elemento:

  • queue.Remove(element, out _, out _) busca y elimina el elemento especificado de la cola de prioridad. Los parámetros out permiten capturar el elemento eliminado y su prioridad anterior, pero en este caso no los usamos (out _, out _).

Reinserción del Elemento con Nueva Prioridad:

  • queue.Enqueue(element, priority) inserta el elemento nuevamente en la cola de prioridad, pero con la nueva prioridad especificada.

Criptografía

  • CryptographicOperations.HashData(): la API CryptographicOperations.HashData. introducido en .NET 9, permite generar un hash o HMAC a través de una entrada como una captura en la que un algoritmo utilizado viene determinado por HashAlgorithmName. en lugar de tener que llamar a la clase SHA256.HashData o HMACSHA256.HashData dependiendo del tipo de  algoritmo que queremos.
  • Algoritmo KMAC: .NET 9 proporciona el algoritmo KMAC especificado por NIST SP-800-185. El código de autenticación de mensajes (KMAC) de KECCAK es una función pseudoaleatoria y una función hash con clave basada en KECCAK.KMAC está disponible en Linux con OpenSSL 3.0 o posterior, y en la compilación 26016 o posterior de Windows 11. Puede usar la propiedad estática IsSupported para determinar si la plataforma admite el algoritmo deseado.

Reflexión

En .NET 9, se introduce la capacidad de guardar ensamblados dinámicamente creados utilizando la nueva API AssemblyBuilder.DefinePersistedAssembly. Esto soluciona una limitación en versiones anteriores de .NET Core y .NET 5-8, donde los ensamblados creados dinámicamente no se podían guardar.

Características Clave:

1. Creación de Ensamblado Persistente:

  • API Nueva: AssemblyBuilder.DefinePersistedAssembly.
  • Independiente del Entorno de Ejecución: Funciona en cualquier plataforma soportada por .NET 9.
  • Sólo Guardar: La implementación actual solo admite la funcionalidad de guardar, no ejecutar.

2.Compatibilidad con Código Existente:

  • Los pasos para definir módulos, tipos, métodos, escribir lenguaje intermedio (IL) y otros usos de System.Reflection.Emit permanecen sin cambios.
  • Esto significa que el código existente que utiliza System.Reflection.Emit puede seguir siendo utilizado para crear y guardar ensamblados.

3.Uso del Ensamblado Guardado:

  • Una vez guardado, el ensamblado puede ser cargado y utilizado como cualquier otro ensamblado .NET.

Rendimiento

La nueva versión de :NET, .NET 9 incluye mejoras en el compilador JIT de 64 bits. Estas mejoras del compilador incluyen:

  • Mejor generación de código para bucles.
  • Más métodos de inserción para AOT nativo.
  • Comprobaciones de tipos más rápidas.

Pruebas unitarias

Ejecución de pruebas en paralelo

En .NET 9, dotnet test está totalmente integrado con MSBuild. Dado que MSBuild admite la compilación en paralelo, puede ejecutar pruebas para el mismo proyecto en diferentes marcos de destino en paralelo. De forma predeterminada, MSBuild limita el número de procesos paralelos al número de procesadores del equipo. También puede establecer su propio límite mediante el modificador -maxcpucount. Si desea no participar en el paralelismo, establezca la propiedad TestTfmsInParallel de MSBuild en false.

Pantalla de prueba del registrador de terminal

Los informes de resultados de pruebas para dotnet test ahora se admiten directamente en el registrador de terminales de MSBuild. Obtendrá informes de pruebas más completos tanto mientras se ejecutan pruebas (muestra el nombre de la prueba en ejecución) y después de que se completen las pruebas (los errores de prueba se representan de una manera mejor).

Conclusión

C# 13 y .NET 9 traen consigo una serie de mejoras y nuevas características que prometen mejorar la eficiencia, la claridad y la funcionalidad de los desarrolladores. Con la introducción de innovaciones como la capacidad de usar el operador de índice implícito “from the end” en inicializadores de objetos, la optimización de la resolución de sobrecargas y la incorporación de nuevas secuencias de escape, C# 13 facilita un desarrollo más intuitivo y menos propenso a errores.

Por otro lado, .NET 9 no se queda atrás, ofreciendo mejoras significativas en áreas críticas como la serialización, LINQ, colecciones, criptografía, reflexión y rendimiento. La nueva API `CryptographicOperations.HashData` simplifica la generación de hashes y HMACs, mientras que las mejoras en la biblioteca `System.Text.Json` y en el compilador JIT de 64 bits demuestran un claro enfoque en el rendimiento y la eficiencia.

Además, la integración completa de `dotnet test` con MSBuild para ejecutar pruebas en paralelo, junto con un mejor soporte para los informes de resultados de pruebas en el registrador de terminales, subraya el compromiso de .NET 9 con la mejora continua de las herramientas de desarrollo y pruebas.

En resumen, estas actualizaciones no solo mejoran la experiencia de desarrollo, sino que también preparan a los desarrolladores para enfrentar los desafíos modernos con herramientas más robustas y flexibles. Con C# 13 y .NET 9, Microsoft sigue demostrando su dedicación a la innovación y la excelencia en el desarrollo de software.

Referencias

1. Novedades de .NET 9, https://learn.microsoft.com/es-es/dotnet/core/whats-new/dotnet-9/overview, accedido el 30 de julio de 2024.

2. Novedades de C# 13, https://learn.microsoft.com/es-es/dotnet/csharp/whats-new/csharp-13, 20 julio 2024.

3. .NET 9 Preview 2 – Release Notes, https://github.com/dotnet/core/tree/main/release-notes/9.0/preview/preview2, 20 julio 2024.

4. This document lists known breaking changes in Roslyn after .NET 8 all the way to .NET 9, https://learn.microsoft.com/es-es/dotnet/csharp/whats-new/breaking-changes/compiler%20breaking%20changes%20-%20dotnet%209, 20 julio 2024.

Principios SOLID 

Principios SOLID 

SOLID(Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion). Establece unos consejos que son usados como base para desarrollar y que generan en lo que ello respecta un código más duradero y robusto, en lo que desarrollo de software respecta en base a patrones y arquitectura que conforma un buen código. En definitiva, desarrollar un software de calidad.

Los 5 principios SOLID de diseño de aplicaciones de software son:

  • S – Single Responsibility Principle (SRP)
  • O – Open/Closed Principle (OCP)
  • L – Liskov Substitution Principle (LSP)
  • I – Interface Segregation Principle (ISP)
  • D – Dependency Inversion Principle (DIP)

En este sentido la aplicación de los principios SOLID está muy relacionada con la comprensión y el uso de patrones de diseño, que nos permitirán mantener una alta cohesión y, por tanto, un bajo acoplamiento de software.

¿Qué son la cohesión y el acoplamiento?

Son dos conceptos muy relevantes a la hora de diseñar y desarrollar software. Veamos en qué consisten.

Acoplamiento

El acoplamiento se refiere al grado de interdependencia que tienen dos unidades de software entre sí, entendiendo por unidades de software: clases, subtipos, métodos, módulos, funciones, bibliotecas, etc.

Si dos unidades de software son completamente independientes la una de la otra, decimos que están desacopladas.

Cohesión

La cohesión de software es el grado en que elementos diferentes de un sistema permanecen unidos para alcanzar un mejor resultado que si trabajaran por separado. Se refiere a la forma en que podemos agrupar diversas unidades de software para crear una unidad mayor.

1. Principio de Responsabilidad Única(Single Responsibility Principle (SRP))

Según este principio una clase tendría que tener una y solo una razón para cambiar. A lo que Robert C.Martin identifica como responsabilidad.

2. Principio de Abierto/Cerrado(Open/Closed Principle (OCP))

Debería ser capaz de extender el comportamiento de la clase sin modificarla. Las clases deben ser abiertas para extenderse y cerradas para modificarse.

3. Principio de Sustitución de Liskov(Liskov Substitution Principle (LSP))

Las clases derivadas deben poder sustituirse, por sus clases base. Deberíamos poder usar cualquiera de sus subclases sin interferir en la funcionalidad del programa.

4. Principio de Segregación de la Interfaz(Interface Segregation Principle (ISP))

En el cuarto principio de SOLID, el tío Bob sugiere: “Haz interfaces que sean específicas para un tipo de cliente”, es decir, para una finalidad concreta.

En este sentido, según el Interface Segregation Principle (ISP), es preferible contar con muchas interfaces que definan pocos métodos que tener una interface forzada a implementar muchos métodos a los que no dará uso.

5. Principio de Inversión de Dependencias (Dependency Inversion Principle (DIP))

Llegamos al último principio: “Depende de abstracciones, no de clases concretas”.

Así, Robert C. Martin recomienda:

  • Los módulos de alto nivel no deberían depender de módulos de bajo nivel. Ambos deberían depender de abstracciones.
  • Las abstracciones no deberían depender de los detalles. Los detalles deberían depender de las abstracciones.

Implementación de RabbitMQ

Implementación de RabbitMQ

(Con Docker y .NET)


Selección de la imagen

Utilizaremos la imagen de docker rabbitmq:3-management

El tag 3-management en la imagen rabbitmq:3-management de Docker Hub indica que se trata de la versión 3 de RabbitMQ que incluye el plugin de administración habilitado.

Realizaremos la inclusión de la imagen en docker-compose.

Estableciendo el puerto 5672 donde nos conectaremos y el puerto 15672 por el que accederemos al panel de administración.

Así como estableciendo el resto de configuraciones.

Implementación en código

En este ejemplo llevaremos a cabo una  implementación sencilla que nos de como resultado una versión ejecutable sencilla y funcional, que demuestre su funcionamiento.

Llevaremos a cabo la implementación de la clase RabbitMQService que contendrá los métodos:

  •  Publish:  Se encargará de publicar en la cola de RabbitMq
  •  StartConsumer: Se encargará de leer de la cola los mensajes que se encolen.

Para llevar a cabo la configuración le pasaremos los parámetros de conexión necesarios que serán los que establecimos en el archivo docker-compose, estos serán: HostName, UserName, Password. En nuestro caso estarán el el appsettings.json

Indicando los parámetros mencionados para la conexión y especificando el nombre de la cola no conectaremos a ella, en caso de no existir creando.

Métodos

Publish

Método que publicará nuestro mensaje de texto en la cola.

Aquí podemos indicar el exchange, que en caso de no introducirlo será el default y por lo tanto routingkey será el nombre de la cola a la que nos dirigimos para encolar.

Aquí enviamos nuestro mensaje como array de bytes.

StartConsumer

Este método ejecutará la escucha de una cola

En este método de la cola que indiquemos, desencolará los mensajes que lleguen a esta y convertirá de un arreglo de bytes a texto, finalmente ejecutará el método que le indiquemos como parámetro el cual será un método que recibirá un string y realizará las acciones que considere.

Aplicación

Ejemplo realizado

Simularemos que un endpoint suspende un usuario y este debe registrarlo en un log, este log es manejado por otro microservicio y por lo tanto debe enviar el mensaje a este mediante rabbitmq para que cuando pueda lo reciba y lo procese.

Encolar mensaje

Suponiendo que tenemos un endpoint que desea comunicar la suspensión de un usuario enviando un registro log el cual otro microservicio registra en base al objeto CreateLogDto.

Crearemos el objeto que vamos a enviar como string en formato json. y lo enviaremos utilizando el método anteriormente descrito.

Tratamiento de mensajes

En esta parte tendremos una clase que se ejecutará en segundo plano y al inicio de la aplicación y ejecuta este método ExecuteAsync, que recuperará de la inyección de dependencias de  los servicios necesarios y utilizando el método StartConsumer anteriormente descrito, creará la expresión lambda encargada de tratar el mensaje recibido y se lo pasará StartConsumer.

Panel de administración

Finalmente mostraremos el panel de administración donde se podrá ver información sobre los procesos que se están realizando en nuestro servicio de rabbitmq.

En este panel podremos ver información detallada de nuestro servicio de mensajería por colas

Accederemos mediante la siguiente dirección: http://localhost:15672

indicando usuario y contraseña.

En la imagen anterior podemos apreciar que disponemos de una cola, si hacemos click iremos al apartado de queues and Streams.

En la siguiente imagen podremos ver información sobre esta; podemos apreciar que la cola se llama notify y tiene información sobre esta. Esta es la cola que hemos generado por código, también podríamos crearla, así como hacer gestión de esta de forma manual desde este panel de administración.

¿Qué es Test Driven Development (TDD)?

Test Driven Development (TDD), o Desarrollo Guiado por Pruebas, es una metodología o estrategía de trabajo en el campo del desarrollo software, que pone la importancia de escribir pruebas antes de desarrollar el código funcional. La metodología TDD puede considerarse no solo como una técnica de programación, sino como una filosofía de trabajo que guía el proceso de desarrollo con el objetivo de mejorar la calidad del código y proporcionar claridad en los objetivos funcionales del software.

Pilares del TDD

1. Test First: Prueba Primero

En el núcleo de TDD está el concepto de “Test First”. Este enfoque establece que antes de iniciar el desarrollo de una nueva funcionalidad, se deben escribir pruebas automatizadas que expresen los requisitos de esa funcionalidad. Estas pruebas fallarán inicialmente, ya que el código correspondiente aún no existe. Este proceso obliga al desarrollador a pensar en la funcionalidad desde el punto de vista del usuario final, asegurando que el software cumpla con las expectativas y requerimientos especificados antes de que se escriba la lógica que lo hará funcionar.

2. Red, Green, Refactor (RGR): Rojo, Verde, Refactorizar

El ciclo de Red, Green, Refactor es una práctica cíclica que consta de tres etapas principales:

  • Rojo: El desarrollo comienza escribiendo una prueba que define una mejora o una nueva funcionalidad. Dado que el código correspondiente aún no existe, la prueba fallará (indicado por el color rojo

en la interfaz de pruebas).

  • Verde: Luego, el desarrollador escribe el código mínimo necesario para que la prueba pase a verde, es decir, que la prueba sea exitosa. Esto ayuda a evitar la sobreingeniería y asegura que el sistema cumpla estrictamente con los requisitos establecidos por la prueba.
  • Refactorizar: Una vez que la prueba pasa y el código funciona (indicado por el color verde), el siguiente paso es refactorizar. En esta etapa, se mejora el código escrito sin alterar su comportamiento. El objetivo es optimizar la estructura, la legibilidad y, en algunos casos, el rendimiento, mientras se asegura que las pruebas sigan pasando.

Este ciclo se repite a lo largo del proceso de desarrollo. Con cada iteración, el código se vuelve más robusto y limpio, garantizando que se mantenga alineado con los requisitos y que cualquier cambio futuro no rompa las funcionalidades existentes.

Ventajas de TDD

  • Mejora la Calidad del Código: Las pruebas continuas descubren errores en etapas tempranas, lo que facilita su corrección y reduce el costo asociado al proceso de depuración.
  • Documentación Viva: Las pruebas actúan como documentación del código, explicando qué se espera de él y cómo se debe comportar en diferentes escenarios.
  • Diseño Modular y Flexible: Al escribir pruebas primero, los desarrolladores se ven forzados a diseñar módulos desacoplados y con interfaces claras, lo que facilita un

sistema más modular y flexible.

  • Confianza en el Código: Al tener un conjunto de pruebas que pasan, los desarrolladores pueden hacer cambios con la confianza de que no introducirán errores sin darse cuenta, ya que las pruebas existentes actuarán como una red de seguridad.
  • Desarrollo Orientado a Funcionalidades: TDD obliga a los desarrolladores a concentrarse en las funcionalidades requeridas, evitando la distracción de agregar funcionalidades adicionales que no son esenciales para el proyecto.
  • Integración Continua: TDD se complementa bien con la integración continua, ya que las pruebas automatizadas se ejecutan con frecuencia, asegurando que los cambios se integren y validen de manera constante.

Desafíos y Consideraciones

  • Curva de Aprendizaje: Implementar TDD requiere un cambio de mentalidad y puede llevar tiempo acostumbrarse a escribir pruebas primero.
  • Tiempo de Inicio: Escribir pruebas antes de escribir el código puede parecer que ralentiza el proceso de desarrollo al principio, pero los beneficios a largo plazo en la calidad y mantenibilidad del código generalmente superan este costo inicial.
  • Complejidad en Manejo y Mantenimiento: Puede ser desafiante escribir y mantener pruebas para sistemas complejos.