La ética en la ingeniería de software: Parte 4

Leonardo
Coder's Pen
Published in
8 min readNov 12, 2017

Red, Green Refactor. La mejora continua de las organizaciones, equipos y profesionales como mecanismo de prevención

Para disminuir la posibilidad de crear daños accidentes las empresas, los equipos, los departamentos y las organizaciones deben enfocarse en la mejora continua de sus procesos de ingeniería de software, para combatir los inconvenientes internos que nos llevan a producir daños accidentales. Esto implica una mejora en el ámbito técnico pero tambien en la comunicación entre equipos, en la planificación y estimación del trabajo y en la creación de equipos multidisciplinarios con expertos en varios dominios más allá del software y la computación.

Individualmente los ingenieros, técnicos y desarrolladores también tenemos una responsabilidad en la mejora continua de nuestras habilidades técnicas y tambien en la mejora de nuestras habilidades de comunicación, de planificación y estimación del trabajo, y de nuestra capacidad de trabajar en equipo; las llamadas habilidades blandas. Este proceso de mejora continua tiene el fin de crear sistemas cada vez más robustos, resilientes y confiables, minimizando la probabilidad de ocasionar daños accidentales.

La visión de la ética de las virtudes debe ser nuestra principal guía en esta tarea de mejora continua. Nuestra práctica de la ingeniería de software debe estar en constante revisión y mejora y debemos ser disciplinados y rigurosos al momento de crear sistemas computacionales.

Como profesionales tenemos el deber ético de enfocarnos en la mejora constante de nuestras prácticas, un deber que abarca muchísimas áreas de la computación como la seguridad informática, el control de calidad, el desarrollo guiado por pruebas, los patrones arquitecturales y de diseño que ayudan en la escalabilidad del código y la mejora de la planificación y comunicación en nuestros equipos.

Ciclo de desarrollo bajo TDD: Pruebas fallan (Red), pruebas pasan (Green), mejoras tu código (Refactor)

Don’t get pwned: Nuestra responsabilidad en la protección de datos y la seguridad de los sistemas

Cuando transportamos y almacenamos los datos privados de nuestros usuarios adquirimos de forma automática la responsabilidad de protegerlos para evitar filtraciones de información que puedan tener consecuencias devastadoras, como el caso de Equifax que vimos en uno de los artículos previos.

Los datos privados son vulnerables a robos internos en nuestra organización y externos por parte de cibercriminales, esto nos obliga a implementar políticas de seguridad y control de acceso de cara al mundo exterior pero tambien internamente.

Como desarrolladores de software, no tenemos que ser expertos en seguridad informática ni llegar a ser los mejores h4x0r5 del mundo con habilidades que superen a las de Elliot “Mr. Robot” Alderson pero sí debemos tener conocimiento sobre los distintos tipos de ataque que pueden sufrir los sistemas que creamos y las formas de protegernos.

Sanitizar las entradas de usuarios para evitar ataques de SQL Injection, Cross-site scripting o cualquier otro tipo de ejecución remota de código deben ser una práctica tan común para nosotros como respirar. Si hacemos programación de sistemas debemos tener un sólido conocimiento de permisos para usuarios y grupos, permisos de lectura, escritura y ejecución de archivos y en general manejo de privilegios dentro del sistema operativo. Por supuesto, los conocimientos sobre el funcionamiento y seguridad en redes, en todas las capas del modelo OSI y en el protocolo TCP/IP son obligatorios también para todas las actividades relacionadas a la ingeniería de software y el desarrollo de sistemas, porque actualmente la mayoría de sistemas y aplicaciones tienen conexión a internet.

Todas las prácticas anteriores nos ayudarán a crear sistemas más robustos antes las amenazas externas, y digo más robustos porque no existe ningún sistema totalmente impenetrable pero nos queda la responsabilidad de proteger los datos de nuestros usuarios de brechas de seguridad dentro de nuestra organización.

Como nos enseñó el caso de Ashley Madison, o más recientemente, la eliminación de la cuenta de Twitter del presidente de Estados Unidos Donald Trump, por parte de un empleado de atención al cliente; debemos tener dentro de nuestra organización políticas de seguridad robustas que ayuden a prevenir este tipo de incidentes, y en el caso de que se produzcan, llevar a cabo auditorías exhaustivas y control de daños que nos permitan hallar a los responsables de los incidentes y minimizar el impacto negativo.

Las políticas de seguridad interna tienen muchas dimensiones pero todas tienen más o menos los mismos elementos en común, limitar el acceso a datos reales de producción a la mínima cantidad de gente necesaria, tener roles bien claros que permitan determinar los permisos de cada empleado sin ambigüedades ni confusiones y en la medida de lo posible dejar registros de todas las acciones de creación, edición y borrado de entidades del sistema para poder investigar la responsabilidades de cada incidente. En el caso puntual de los registros de acciones éstos tienen un doble propósito, primero, hacer que nuestro sistema sea auditable y segundo, servir como mecanismo de disuasión porque al tener sistemas auditables los miembros de nuestra organización lo pensarán dos veces antes de llevar a cabo acciones en contra de nuestra organización, de nuestro sistema y de los usuarios finales.

You are not that good: Desarrollo guiado por pruebas, pruebas automatizadas, quality assurance

Hablemos con honestidad, el software es inestable.

Diariamente extendemos y corregimos sistemas enormes llenos de complejidades que muchas veces conocemos medias y cajas negras que a veces no se comportan exactamente como dicen las especificaciones. Ya se acabaron esos años en que un programador conocía cada una de las líneas de código que se ejecutaban en sus sistemas y a medida que agregamos capas de abstracción a los sistemas agregamos complejidades: sistemas operativos, compiladores, librerías, frameworks, módulos, runtimes, máquinas virtuales, containers, etc. Estas capas de abstracción son muy útiles porque ayudan a eliminar eso que Fred Brooks llamaba la complejidad accidental en el desarrollo de software, es decir, problemas relacionados a la arquitectura y funcionamiento de nuestro sistema pero son cajas negras que agregan incertidumbre y a nuestros sistemas y aumentan la probabilidad de obtener comportamientos inesperados.

Eliminar la complejidad accidental a través de abstracciones nos permite desarrollar aplicaciones más rápidamente, ya que nos preocupamos menos de la complejidad accidental para poder enfocarnos en la complejidad incidental, el otro tipo de complejidad que describe Fred Brooks, relacionada al problema real de negocios que queremos resolver, por esta razón aceptamos el trade-off de confiar en abstracciones, aunque a veces parte de la complejidad e incertidumbre que éstas intentan encapsular se filtre hacia nuestros propios sistemas.

Si a esas cajas negros le vamos agregando nuestro propio código y sus comportamientos inesperados, terminamos con sistemas que tienden a la inestabilidad. Estamos en una constante carrera perdida contra la entropía, o como dice el título de una charla: Todo el software está roto.

Aunque parezca que todo está perdido, no hay que perder las esperanzas, tenemos a nuestra disposición muchísimas herramientas y prácticas que nos ayudan a movernos con un poco más de seguridad en medio del mar de incertidumbre de la ingeniería de software, repasemos rápidamente algunas de estas prácticas.

Desarrollo Guiado por Pruebas (TDD: Test driven development)

El desarrollo guiado por pruebas es un proceso de desarrollo de software donde los desarrolladores comienzan por escribir casos de prueba para los requerimientos que deben implementar y luego van escribiendo iterativamente el código mínimo necesario para hacer que estas pruebas pasen. El proceso de TDD nos permite validar constantemente que nuestros programas cumplen con las especificaciones de las pruebas automatizadas, podemos añadir nuevas funcionalidades, corregir bugs y refactorizar nuestros componentes con la confianza de que no estamos rompiendo funcionalidades existentes. Este ciclo de iteración y retroalimentación corto donde nuestro software va creciendo poco a poco nos da más control sobre el comportamiento final de nuestro sistema.

Además de disminuir la probabilidad de incluir bugs y regresiones en nuestro código, hacer TDD nos permite separar el proceso de crear la especificación del proceso de implementar la solución, pero de manera ágil, con ciclos de desarrollo cortos. Esto se contrapone a lo que sucede con metodologías tradicionales como waterfall, donde los ciclos de retroalimentación son muy largos y añaden complejidades organizacionales innecesarias y aumentan el riesgo de crear sistemas que no cumplen con las especificaciones originales ni producen el resultado esperado.

La mayoría de herramientas de programación moderna cuentan con módulos, frameworks y librerías para escribir todo tipo de pruebas automatizadas: Pruebas unitarias, pruebas de integración, pruebas end-to-end de flujos dentro de un sistema, pruebas end-to-end de la interfaz gráfica, pruebas de performance, etc, etc…

Quality Assurance: Pruebas manuales y automatizadas

Aunque practiquemos al pie de la letra los principios del test dirven development y logremos 100% de cobertura de nuestro código, existen infinitos bugs que pueden producirse en nuestros sistemas y que no somos capaces de anticipar a través de nuestras casos de prueba, por esta razón debemos contar con un equipo de QA independiente que se encargue de probar nuestras aplicaciones, realizando pruebas manuales y tambien pruebas automatizadas.

En equipos pequeños los product owners, managers y otros stakeholders suelen encargarse de labores de QA pero casi siempre carecen de entrenamiento formal como testers y suelen probar únicamente el llamado happy-path de las funcionalidades que implementamos, enfocándose sólo en la correctitud de nuestros programas pero dejando de lado pruebas que validen la robustez del código que escribimos para manejar casos límite; tarde o temprano cualquier proyecto de cierto tamaño necesitará analistas de QA especializados, dedicados totalmente a encontrar fallos en nuestros sistemas, cooperando con nosotros para crear software más correcto y más robusto.

Calidad y legibilidad del código

Los programas deben ser escritos para que las personas los lean, y sólo incidentalmente para que las máquinas los ejecuten
Harold Abelson
Estructura e Interpretación de Programas de Computadora

Nuestro código será leído, modificado, corregido y extendido por muchos desarrolladores a lo largo del ciclo de vida de nuestros programas, mientras más difícil de leer sea este código fuente mayor será la probabilidad de introducir bugs.

Entender la arquitectura y funcionamiento de un sistema a través de su código fuente es una tarea compleja que implica ejecutar mentalmente corridas en frío y mantener en nuestra inexacta “memoria RAM” cerebral cientos de modelos y estructuras que nos asisten durante el proceso. Hacer que nuestro código sea legible y extendible con facilidad es una tarea que aumenta nuestra productividad permitiendo que entreguemos más valor en menos tiempo y también nos asiste en garantizar la calidad, correctitud y robustez del software que creamos.

Al crear software más robusto estamos disminuyendo la probabilidad de introducir daños accidentales a través de nuestros sistemas.

Se escapa del alcance de este artículo hablar sobre calidad, escalabilidad y legibilidad del código pero las técnicass y buenas práticas más comunes deben ser conocidas por todos los buenos ingenieros de software, hay soluciones probadas durante años y que cuando las aplicamos bien producen excelentes resultados: Diseño orientado a objetos, diseño guiado por el dominio, patrones de diseño, patrones arquitecturales, los principios SOLID, desacoplamiento y modularidad, etc.

Toda la información relacionada a buenas prácticas de ingeniería es extensa. Hay años de investigación, desarrollo, ensayo y error que nos han permitido evolucionar y modernizar nuestro ejercicio de la profesión. Para no seguir extendiendo este artículo dejo por fuera temas como la rigurosidad al escribir documentación, la mejora en la planificación y estimación de esfuerzos, la integración y el despliegue continuos y la gestión ágil, en este blog publicaré con frecuencia articulados sobre estos temas, mientras tanto continúa leyendo la última parte de esta serie de artículos acá.

--

--