Software Engineering Guide

IMPORTANTE: El propósito de este Roadmap es tratar de plasmar una ruta de aprendizaje de cara a especializarme como .NET Full-Stack Developer con una visión integral de los aspectos de la producción de software (ingeniería de software).

Contexto

Expresado de manera simplificada, el desarrollo de un sistema de software puede verse como una transformación hacia la solución técnica de determinada problemática u oportunidad con el fin de resolverla. Este cambio enfrenta a menudo restricciones en relación con el tiempo, el costo y la calidad. 

Figura 1: Visión simplificada del desarrollo de sistemas

La única forma conocida para ejecutar la estrategia, o plan de negocios, es mediante proyectos. El hacer proyectos rápidos y correctamente tiene ventajas obvias. Las tareas y estrategias se ejecutan con agilidad, se utilizan solo los recursos necesarios, tanto de dinero como de personas, y el producto resultante y sus beneficios se ven más rápido. 

Nota: Si tu competidor puede ejecutar proyectos a la mitad del tiempo que tú, te está ganando en el aquí y ahora.

Las actividades en un proyecto se realizan en dos frentes paralelos: El frente gerencial y el frente técnico. 

Figura 2: Frentes gerencial y técnico del proyecto

La Figura 2 correlaciona estos dos frentes y además indica que en el frente gerencial siempre se realizan, fase por fase, los siguientes grandes grupos de actividades: Iniciar, planificar, ejecutar, monitorear/controlar y cerrar, esto en consonancia con lo presentado en el PMBOK.

Es importante resaltar que las actividades que se desarrollan en el frente gerencial no dependen del tipo de industria. Aplican por igual en las industrias de extracción, construcción, manufactura, farmacéutica, desarrollo de software, consultoría, telecomunicaciones, banca, seguros, comercialización, producción, energía, gas, petróleos, etc.

Nota: Este enfoque de que un proyecto no depende de su naturaleza o dominio se realiza con fines puramente académicos para concentrarnos en los aspectos propios de la dirección de proyectos.

En el frente técnico del proyecto actúan los diversos miembros del equipo del proyecto encargados de materializar los entregables -productos, servicios o resultados- con los cuales se ha comprometido el proyecto. Obsérvese que este grupo está desarrollando los constitutivos del alcance con los cuales se ha comprometido el proyecto. Las actividades que se relacionan con este frente sí son muy dependientes de la naturaleza de la empresa y el tipo de proyecto a desarrollar. Por ejemplo, las actividades técnicas que requieren los proyectos de fidelización de clientes, de reingeniería de procesos de producción, de construcción de carreteras, de desarrollo de software, etc. pueden ser muy diferentes entre sí. En el desarrollo de software las actividades técnicas que se realizan desde el dominio del problema hasta el dominio de la solución son: Requerimientos, Diseño, Construcción, Pruebas, Despliegue y Mantenimiento.


Flujo de Valor

Como desarrolladores tenemos la misión de entregar software funcional de manera frecuente. En nuestro día a día esto implica recorrer un Flujo de Valor donde realizamos actividades para diseñar, producir y entregar software al cliente. 

Figura 3: Flujo de valor

Dicho flujo de valor sigue las siguientes etapas:

  • (a) Todo comienza con una necesidad del usuario, una oportunidad de negocio.
  • (b) Creamos una visión del proyecto.
  • (c) Identificamos las funcionalidades para crear el backlog del producto.
  • (d) Empezamos a trabajar de manera iterativa e incremental.
  • (e) Al final de cada iteración tenemos un incremento del producto.
  • (f) Lo verificamos: Testing, control de calidad.
  • (g) Si todo está de acuerdo con las expectativas, entonces nuestro producto está potencialmente listo para su lanzamiento. Para ello, nuestro artefacto de software puede tener que pasar por diferentes entornos en su camino hacia producción.
  • (h) Donde finalmente se encuentra con el usuario final.
  • (j) Es en ese momento, en manos del usuario, cuando el software aporta valor (se generan utilidades).
  • (i) Tenemos que operarlo para garantizar su disponibilidad.
  • (k) Y necesitamos obtener métricas en tiempo de ejecución para comprender el comportamiento del usuario y de esta forma buscar formas de incrementar las utilidades.
Una vez completado el ciclo, el proceso vuelve a comenzar, mejorando continuamente el flujo de valor.

Tradicionalmente, la Ingeniería de Software se ha centrado en los aspectos relacionados con el desarrollo (los requisitos, la arquitectura, el diseño, las pruebas, etc.), es decir, la zona verde de la Figura 3. Por otro lado, iniciativas como ITIL y MOF han tratado las preocupaciones de la zona roja, pero ninguna de ellas ha sido ampliamente difundida y adoptada. En los últimos años, la iniciativa DevOps ha logrado un gran éxito optimizando todo el flujo lo cual lo sitúa en la zona azul de la Figura 3.

Desarrollo con eXtreme Programming

La propuesta es guiar el desarrollo con pruebas. Desarrollar una aplicación siguiendo la metodología de eXtreme Programming (moderno) considerando las siguientes prácticas:

  • BDD / TDD
  • CI / CD
  • Ensemble programming (aka "Pair/Mob programming")
  • Otros: Infra as Code, Feature Flags, Monitoring, etc.

El stack de tecnología a utilizar es el siguiente:

  • Runtime
    • .NET 8
    • PostgreSQL
    • Docker (runtime)
    • Render
    • Uptime Robot
  • Desarrollo
    • Gitlab (backlog, repo, CI/CD, etc)
    • Visual Studio / Co-Pilot

---

Links



I. Visión de proyecto y Kick-off

El "negocio" del desarrollo

  • En general, el desarrollo de software implica resolver problemas de negocio.

Visión de negocio

  • abc

Mínimo Producto Viable

  • Registración
  • Lista de expertos


II. Planning y Bootstrap

https://blog.nicopaez.com/2013/12/19/project-bootstrap-como-inicio-mis-proyectos/



IV. Walking Skeleton

El Walking Skeleton es una versión mínima pero funcional del sistema que recorre toda la arquitectura de punta a punta. Su objetivo es establecer las bases técnicas y arquitectónicas del proyecto desde el inicio.

Notas importantes:

  • Se debe comenzar con un walking skeleton que permita validar la estructura general de la aplicación, desde la interfaz hasta la base de datos o servicios externos.

  • Este walking skeleton debe estar cubierto por una prueba de aceptación end-to-end, asegurando que todo el flujo básico funcione correctamente.

  • Además, debe incluir desde el inicio el proceso completo de versionado, build, test y deploy hacia un entorno similar a producción, siguiendo un esquema de integración continua (CI).


En resumen, el walking skeleton permite tener una primera versión del sistema que “camina” de extremo a extremo, demostrando que la arquitectura, el pipeline y la automatización están correctamente configurados.


VI. Creación de un proyecto .NET paso a paso

Este apartado explica cómo construir una aplicación .NET completa desde cero, siguiendo buenas prácticas de desarrollo profesional, automatización y testing. El enfoque no es solo técnico, sino también arquitectónico y metodológico: busca sentar las bases de un proyecto sólido, automatizable y probado end-to-end.

  • Crear solución: 
    • dotnet new sln --name <nombre solucion>
  • Crear proyecto usando plantilla MVC dentro de la solución:
    • dotnet new mvc --auth None --no-https --framework net8.0 -o WebApp
    • dotnet sln add WebApp/WebApp.csproj
    • dotnet add WebApp/WebApp.csproj package Npgsql
    • dotnet build
  • Generar .gitignore
    • El archivo .gitignore sirve para indicarle a Git qué archivos o carpetas debe ignorar al hacer seguimiento de cambios en un repositorio.
  • Generar Build Script
    • Un build script en un archivo .gitlab-ci.yml define el conjunto de acciones que GitLab CI/CD debe ejecutar durante la etapa de construcción de tu pipeline. Generalmente, incluye una lista de comandos que se ejecutan para compilar tu código, ejecutar pruebas o preparar tu aplicación para el despliegue.
    • No es recomendable poner código en el build script porque no lo podemos testear localmente. Lo recomendable es colocar el código en scripts.
    • How to create file execute mode permissions in Git on Windows?
  • Instalar Linter
    • Cuando se trabaja con distintos SO y diferentes IDE, se debería agregar un EditorConfig en el proyecto. Este sirve para colocar las convenciones a seguir en el desarrollo. Ver ejemplo de archivo EditorConfig
    • Luego, tenemos que configurar nuestro IDE para que "entienda" las convenciones ubicadas en el EditorConfig. En Visual Studio tenemos que habilitar la opción "Code Style".
    • Cuando se suba el código al repositorio, se tiene que asegurar de que se sigan dichas convenciones. Linter hace esto. Parsea tu código y asegura que tu código cumple con las convenciones del lenguaje. 
    • En .NET el comando dotnet format aplica automáticamente las reglas de estilo definidas en .editorconfig y mantener tu código limpio y consistente.
    • Dentro del pipeline se debería hacer una verificación (sin modificación) de que el código está correctamente formateado según las reglas del archivo .editorconfig. Para esto usamos el comando dotnet format --verify-no-changes 
  • Definir el arnés de pruebas
    • Un arnés de pruebas (en inglés, test harness) es un conjunto de herramientas, código y configuraciones que se utilizan para automatizar la ejecución de pruebas en una aplicación o componente de software.
    • En un desarrollo guiado por pruebas, el objetivo no es escribir las pruebas. Las pruebas son un medio para escribir la aplicación. Estas pruebas tempranas suelen ser distintas a las que uno hace a posteriori del desarrollo con el objetivo de testear. Si no tienes en cuenta las pruebas de manera temprana terminarás solo haciendo pruebas de caja negra. Esto es costoso.
    • ¿Que tipos de pruebas haremos en nuestra arquitectura?:
      • Aceptación: Deben ser end-to-end (e2e), expresadas en términos de la aplicación. Estas pruebas testean la aplicación a nivel de historia de usuario (tipo caja negra), son lentas y no dan buen feedback. Son pruebas que las puede escribir el usuario, por tanto están escritas en un lenguaje natural. Herramientas: nunit + Gherkin + Reqnroll + Selenium.WebDriver. Reqnroll ejecutará el Gherkin. Por debajo, usaremos el Selenium.WebDriver para automatizar tareas en el navegador.
      • Unitaria: Prueban la lógica de negocio sin dependencia de infraestructura. Herramientas: nunit + moq
      • Integración: Probar como se integra código que escribiste tú con código que tú no escribiste. Herramientas: nunit
  • Agregar a la solución un proyecto de test NUnit (con pruebas e2e) para probar la Web.
    • Sintaxis Gherkin para especificar los tests
    • Usar Reqnroll para interpretar Gherkin en C#
    • Con el WebApplicationFactory vamos a instanciar un objeto que va a representar a la aplicación web dentro del proceso que corre los tests sin necesidad de levantar la aplicación por fuera.
    • Usar la librería FluentAssertions la cual extiende los métodos de aserción tradicionales (Assert.Equal, Assert.True, etc.) y permite escribir pruebas más naturales y fáciles de leer.
    • Configurar la medición de cobertura
  • Aplicación de BDD
    • En un enfoque tradicional, tenemos al analista que habla con el usuario y genera una especificación (un documento, con casos de uso por ejemplo), luego el developer codifica y finalmente el tester define y ejecuta casos de prueba. 
    • En un enfoque de testing ágil / XP, Usuario, Dev y Tester se juntan para armar una especificación basada en ejemplos. Estos ejemplos son casos de prueba. El Dev a construir teniendo a mano los casos de prueba, es decir, ya sabe como se va a testear la funcionalidad que está desarrollando. Luego el Tester ejecuta los mismos casos de prueba que el Dev utilizó para guiarse en el desarrollo (esto ahorra muchas idas y vueltas). Para que el usuario pueda trabajar colaborativamente en la definición de estos casos de prueba se utilizan una herramienta como Gherkin. Luego se utilizan otra serie de herramientas para convertir Gherkin a código. Reqnroll ejecutará el Gherkin. Por debajo, usaremos el Selenium.WebDriver para automatizar tareas en el navegador. La idea entender los requerimientos a partir de ejemplos concretos los cuales se construyen en conjunto con el usuario. A partir de herramientas como Reqnroll y Cucumber podemos automatizar los requerimientos expresados en Gherkin. Cada escenario representa un test. Selenium.WebDriver es un paquete que maneja Google Chrome. Esto requiere que esté instalado el Chrome. Si queremos ejecutar esto dentro de Gitlab, vamos a necesitar crear una imagen de Docker que tenga .NET y Google Chrome para que esto pueda correr dentro del Pipeline (Crear imagen custom docker para usar en el CI)
    • BDD implica entender los requerimientos a partir de ejemplos concretos.
    • User stories are typically broken down into acceptance criteria or business rules. Focusing on examples makes the intention of these rules clear—each rule should be illustrated by one or more examples.

  • Escribir un escenario (test)  nos ayude a recorrer los puntos centrales de la arquitectura
    • Completar la idea de Walking Squeleton
    • En Gherkin @wip sirve para excluir escenarios. Luego configurar tu runner para no ejecutarlas: > dotnet test --filter "TestCategory!=wip" 
  • Armar arnés de prueba en NET
    • Armar una configuración de compose que levanta 2 bases de datos. Una BD para cuando ejecute la aplicación de forma manual y otra BD para usarla en el contexto de las pruebas automatizadas.
      • Levantar BDs: > docker compose up
      • Puedes acceder a las bases de datos mediante el Management Studio.

    • Para las pruebas ¿por qué no usar una BD en memoria? Poque necesitamos usar un entorno lo más parecido a producción, para mitigar riesgos.
    • Cuando ejecutemos las pruebas, tenemos que apuntar a la BD de pruebas a través del Connection String. Los archivos appsettings son dinámicos por ambiente.
    • Ejecutar los Tests (es importante indicar la variable de ambiente de test para no pegarle a la base de datos de desarrollo)
      • > set ASPNETCORE_ENVIRONMENT=test
      • > dotnet test
      • dotnet test --filter "TestCategory!=wip"
    • La BD se creará a través de Migrations (scripts que crean la estructuras de tablas).
      • > Add-Migration InitialCreate
    • Crear proyecto Web.Test el cual tendrá tests desde la perspectiva del usuario escritos en lenguaje Gherkin.
    • En la clase Hooks de Reqnroll, 
      • En el método BeforeTestRun() se crea instancia de la aplicación
      • En el meétodo BeforeScenario() me conecto a la BD y borro todo para que los escenarios estén limpios. Luego levanto el Chrome Driver para que en cada escenario usar una instancia del navegador distinta.

Nota: A esta altura deberíamos tener la aplicación con su arnés de pruebas, además de una prueba básica que nos sirvió para armar el esqueleto (levantar la aplicación web, que reciba un request, va hasta la base de datos y vuelve). Si bien es cierto que esta prueba básica es de una funcionalidad que no tiene utilidad para el usuario, sí para nosotros, para mitigar riesgos respecto a la arquitectura.


















https://gitlab.com/untref-ingsoft/trinchera/jobvacancynet

Introducción


Desarrollar una aplicacion siguiendo la metodología de eXtreme Programming (moderno)  considerando las siguientes prácticas:
  • BDD / TDD
  • CI / CD
  • Ensemble programming (aka "Pair/Mob programming")
  • Otros: Infra as Code, Feature Flags, Monitoring, etc.
El stack de tecnología a utilizar es el siguiente:
  • Runtime
    • .NET 8
    • Postgresql 17
    • Docker (runtime)
    • Render
    • Uptime Robot
  • Desarrollo
    • Gitlab (backlog, repo, ci/cd, etc)
    • Visual Studio / Rider / Co-Pilot
---

Preparar ambiente de desarrollo
  • .NET, Git, Docker, IDE
Crear repositorio y backlog (https://gitlab.com/ingsoft2630115/trinchera/jobvacancy-net/-/boards)

Guiamos el desarrollo con pruebas
  • Pruebas de aceptación
    • Relatadas desde la perspectiva del usuario
    • ¿Cliente o usuario? El cliente es el que paga, el usuario es el que usa el software.
    • El usuario tendría que ser el que escribe las pruebas (en el mejor de los casos)
    • Herramienta: reqnroll
  • Pruebas uniarias
    • Pruebas técnicas
    • Herramienta: xUnit

Diseño de alto nivel desacoplado de la tecnología



Investigar/Definir las herramientas a utilizar (ORM, View Engine, etc)

Las decisiones de diseño se tienen que documentar en un log de arquitectura.

Con NET vamos a generar un scafolding básico y sobre eso generamos un walking skeleton con una funcionalidad de healthcheck.


De Walking Skeletons y Tracer Bullets



Walking Skeleton
  • A Walking Skeleton is a tiny implementation of the system that performs a small end-to-end function. It does not need to use the final architecture, but it should link together the main architectural components.
  • Implementar una funcionalid de punta a punta, de tal forma que podomas cablear todas las partes de nuestra arquitectura.

Tracer Bullet
  • A bare-bones skeleton of your system that is complete enough to hang future pieces of functionality on. It's exploratory, but it's not really a prototype because you are not planning to throw it away - it will become the foundation of your real system.

Scaffolding

Scaffolding se refiere a la generación automática de código.
Para generar una solución con un proyecto ASP.NET MVC con Entity Framework usando PostgreSQL:
  • dotnet new sln --name <nombre solucion>
  • dotnet new mvc --auth None --no-https --framework net8.0 -o WebApp
  • dotnet sln add WebApp/WebApp.csproj
  • dotnet add WebApp/WebApp.csproj package Npgsql
  • dotnet build

Para instalar las herramientas de Entity Framework
  • dotnet tool install --global dotnet-ef

Motor de Templates

  • Razor pages
  • Blazor
  • Razor
  • Liquid
  • Scriban
  • Handlebars.NET

Opciones para acceso a base de datos

  • NHibernate
    • Es un ORM completo de la comunidad
    • Muchas funcionalidades
    • https://nhibernate.info/
  • Dapper
    • No es un ORM completo
    • Comunidad
  • Entity Framework
    • ORM de Microsoft

52min: code time

Crear un Log de Decisiones en el repositorio

Si queremos hacer desarrollo guiado en el dominio, entonces tenemos que ajustarnos en el dominio. Si mi cliente habla sobre su negocio en español, entonces quiero que ello esté reflejado en el código. Cuando hacemos traducciones caemos en riesgo de caer en la ambiguedad. 

Agregamos un build script en el proyecto

Agregamos un EditorConfig en el proyecto. Este sirve para colocar las convenciones a seguir en el desarollo.
https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options
Cuando se suba el código al respositorio se tiene que asegurar que se siguen dichas convenciones. Linter hace esto. Parsea tu código y asegura que tu código cumple con las convenciones del lenguaje. 
El "dotnet format" lee el editorconfig 
https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format
Cada vez que suba el proyecto al repo se debe verificar: 1) que compile, 2) que siga las convenciones, 3) que pasen los tests, ...

Crear proyecto de test
  • dotnet new nunit --framework=net8.0 --name jobvacancynet.Web.Tests
  • dotnet sln add jobvacancynet.Web.Tests/jobvacancynet.Web.Tests.csproj
  • dotnet add jobvacancynet.Web.Tests/jobvacancynet.Web.Tests.csproj reference jobvacancynet.Web/jobvacancynet.Web.csproj
  • cd jobvacancynet.Web.Tests
  • dotnet add package Microsoft.AspNetCore.Mvc.Testing --version 8.0.14
  • https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-9.0


MVC, Patrones y Arnés de Pruebas






MVC
  • Problema que intenta resolver: como estructurar una aplicación interactiva (el usuario hace algo, la aplicación contesta). Un proceso batch no es una aplicación interactiva (aqui podrías usar un batch filter). Las aplicaciones interactivas tienen que interpretar el input del usuario, luego hacer algún tipo de cómputo y finalmente entregar una respuesta.
  • 3 componentes: Vista, controlador y modelo
    • controlador: interpreta intención del usuario 
    • modelo: resuelve la lógica de negocio
    • vista: da una respuesta al usuario
  • El modelo no debe conocer la vista ni el controlador
  • El patrón MVC se puede combinar con otros


Arquitectura Hexagonal
  • Puerto = Interfaz
  • Adaptador = Implementación
  • Problema: aislar la lógica de negocio de la infraesctructura.
  • Beneficios: 
    • Independizarme de la infraestructura (cambiable)
    • Testear la lógica de negocio sin depender de la infraestructura

Combinación MVC y Arquitectura Hexagonal



Arnés de Pruebas
  • En un desarrollo guiado por pruebas, el objetivo no es escribir las pruebas. Las pruebas son un medio para escribir la aplicación. Estas pruebas tempranas suele ser distintas a las que uno hace a posteriori del desarrollo con el objetivo de testear.
  • Si no tienes en cuenta las pruebas de manera temprana terminarás solo haciendo pruebas de caja negra. Esto es costoso.
  • La arquitectura propuesta hasta el momento permite realizar distintos tipos de tests. Nota: los tipos de tests no están estandarizados. 
  • Según el libro "Growing Object-Oriented Software, Guided by Tests" existen los siguientes tipos de tests:
    • Unit: 
      • Prueba un componente, una clase
    • Integration
      • Cómo se integra código que escribiste tú con código que tú no escribiste.
    • Acceptance
      • Una prueba que la puede escribir el usuario 
      • Expresada en términos de la aplicación
      • Deben ser end-to-end (e2e)
      • Son lentas, no dan buen feedback
  • La estrategia de pruebas sería la siguiente


Behavior-Driven Development




Enfoque Tradicional




Enfoque Testing Ágil / XP
  • Usuario, Dev y tester se juntan para armar una especificación basada en ejemplos. Estos ejemplos son casos de prueba.
  • Para que el usuario pueda trabajar colaborativamente en la definición de estos casos de prueba se utilizan una herramienta como Gherkin. Luego se utilizan otra serie de herramientas para convertir Gherkin a código. 
  • Reqnroll ejecutará el Gherkin. Por debajo, usaremos el Selenium.WebDriver para automatizar tareas en el navegador.
  • La idea entender los requerimientos a partir de ejemplos concretos los cuales se construyen en conjunto con el usuario.
  • A partir de herramientas como Reqnroll y Cucumber podemos automatizar los requerimientos expresados en Gherkin. Cada escenario representa un test.



Selenium.WebDriver es un paquete que maneja Google Chrome. Esto requiere que esté instalado el Chrome. Si queremos ejecutar esto dentro de Gitlab, vamos a necesitar crear una imagen de Docker que tenga .NET y Google Chrome para que esto pueda correr dentro del Pipeline. 

La idea es que un Test nos ayude a recorrer los puntos centrales de la arquitectura.

BDD / TDD
  • Escribimos una prueba de aceptación en Gherkin
  • Luego pasamos a las pruebas unitarias



Arnés de Prueba para .NET




La herramienta reqnroll permite asociar código a lenguaje gherkin


Definir el Requerimiento:
  • Historia de Usuario
  • Criterios de aceptación

Crear la Feature con Gherkin
  • Dado una funcionalidad, esta tiene una serie de reglas y asociado a las reglas hay distintos escenarios.
  • Un camino feliz y caminos alternativos.
  • ¿Es necesario que todo los escenarios tengan pruebas de aceptación e2e? El escenario feliz seguramente si, los escenarios alternativos tal vez no todos. 



























Software Requirements


Software Architecture

Software Design


Software Construction


Software Testing

Software Configuration Management


Software Engineering Management


Software Engineering Operations











Enlaces







Poner foco en las siguientes prácticas de desarrollo de software:

  • Code Review
  • Unit Test
  • Coding Standards
  • BDD
  • Performance Testing
  • Cloud Development
  • CI/CD

No comments:

Post a Comment