La actividad de Codificación, también llamada Desarrollo o Programación, transforma la idea o concepto de solución definida en la actividad de Diseño en código que puede ejecutarse. Durante la codificación el diseño de la solución es implementado.
En la actividad de Diseño se abstraen conceptos y se encapsulan reglas del negocio. Se definen las principales clases (atributos y métodos) y cómo interactúan entre ellas, se “imagina” el código. Durante la codificación se detalla la implementación. Los bocetos e ideas del Diseño son aterrizados en código.
El diseño establece los fundamentos que sustentarán el código. Por ejemplo, durante la actividad de diseño se “idea” la clase “Alumno”. El diseño concluye las propiedades y métodos que esta abstracción encapsula. El método “SeHaLicenciado()” de la clase es definido indicando únicamente su firma (parámetros de entrada y salida), no se codifica su contenido. Es en la actividad de codificación cuando se concretará el código del método.
En las metodologías predictivas, el diseñador comunica al codificador el concepto de la solución, las principales abstracciones y encapsulaciones de reglas. Para ello se hace uso de diagramas UML. El codificador hace uso de estas ideas y bocetos para codificar el software.
En las metodologías adaptativas, los roles de diseñador, codificador y tester son interpretados por un mismo técnico. El diseño, la codificación y las pruebas se solapan, realizando todas estas actividades casi simultáneamente. El desarrollo guiado por tests facilita el solapamiento de estas actividades
Antes incluso del nacimiento de la ingeniería del software, los científicos de la computación eran conscientes de la importancia de escribir código “agradable” para los humanos. En 1964, Peter J. Landin, en su artículo The mechanical evaluation of expressions, hizo uso del término “azúcar sintáctico” para referirse a instrucciones no esenciales del lenguaje cuyo principal objetivo es facilitar al programador la escritura y lectura de código. Un ejemplo de azúcar sintáctico es el operador += de java. Esta instrucción es sustituida por el compilador por operaciones para incrementar una variable. El bytecode generado por el compilador al interpretar la operación a=a+1; y al interpretar a+=1; es idéntico. El “azúcar sintáctico” hace el código más “dulce” al programador.
💬 Cualquier programador es capaz de escribir código que un ordenador puede entender. Un buen programador es capaz de escribir código que un humano puede entender.
Martin Fowler
Es posible implementar una misma funcionalidad escribiendo muy diferentes códigos. Si bien todos los códigos obtienen el mismo resultado, no todos los códigos son fácilmente mantenibles y extensibles (y por tanto testables). El código limpio o sostenible es aquel que es fácil de entender por un humano y, por lo tanto, fácil de modificar, extender y probar en el tiempo.
💬 “El código sostenible es aquel que se puede mantener fácilmente a lo largo del tiempo. Para ello es necesario que su diseño sea intuitivo, que su complejidad sea la mínima imprescindible y que esté bien cubierto por pruebas automatizadas”
Carlos Ble, Código Sostenible (2022)
De la misma manera que una novela puede contar una buena historia, pero una escritura descuidada, abusando por ejemplo de grandes oraciones subordinadas o empleando un pobre léxico, puede hacer abandonar al lector más ávido, un producto software puede contar con un correcto diseño, pero una equivocada codificación, abusando de cláusulas IF o haciendo uso de un pobre naming, puede lastrar su mantenibilidad y extensibilidad. El buen programador es capaz de comunicar y expresar sus intenciones haciendo uso del lenguaje de programación como si del lenguaje natural se tratara. Al igual que conocer con profundidad la gramática y vocabulario de un idioma es fundamental para una buena comunicación, conocer la sintaxis y comandos de un lenguaje de programación es esencial para generar un buen código.
Los patrones de diseño técnico ayudan al desarrollador a ingeniar los componentes y clases adecuados. Análogamente, las buenas prácticas de codificación ayudan al desarrollador a aterrizar y concretar el diseño escribiendo código fácil de mantener y extender (y por lo tanto fácil de probar). Así como un buen escritor emplea una gran riqueza léxica para plasmar con prosa una idea narrativa, un buen programador hace uso de patrones y buenas prácticas para plasmar con código el diseño ideado.
Para entender qué es el código limpio, es fundamental comprender qué es código “sucio”. El término habitual para referirse al código “sucio” es “código espagueti”. El código espagueti es un término peyorativo utilizado para los programas que tienen una estructura de control de flujo compleja e incomprensible. Su nombre deriva del hecho que este tipo de código parece asemejarse a un plato de espaguetis, es decir, un montón de hilos intrincados y anudados.
El código difícil de mantener, extender y probar desprende “malos olores” (code smell). Esto nos permite identificarlo. Algunos de los más habituales en la POO son:
Métodos o clases muy grandes. Es un síntoma de falta de cohesión. Un método con muchas líneas de código suele tener demasiadas responsabilidades.
Obsesión por los tipos primitivos. Los únicos tipos utilizados son los que ofrece el lenguaje de forma nativa (Integer, String, Boolean). No se construyen tipos específicos (clases) como Nombre, Teléfono o DNI.
Métodos con muchos parámetros de entrada. Es un síntoma de falta de cohesión. Un método que requiere muchos parámetros suele tener demasiadas responsabilidades.
Clases sin métodos. Las clases son usadas para estructurar datos, no para modelar el negocio. El código que opera con esos datos es “desperdigado” por otras clases.
Abuso de variables globales. Los diferentes componentes del software hacen uso de las mismas variables globales para persistir el estado del software o comunicar eventos.
💬 El camino al infierno de la programación está pavimentado con variables globales
Steve McConnell, Code Complete
Los programadores invierten mucho más tiempo en leer código que en escribirlo. Por ello, escribir código fácil de leer ofrece un gran retorno de inversión. A la larga el código fácil de leer, el “buen” código, es más barato que el código espagueti.
💬 [...] de hecho, la relación entre el tiempo dedicado a leer y escribir es más de 10 a 1. Estamos constantemente leyendo código antiguo como parte del esfuerzo por escribir código nuevo. [Por tanto,] facilitar la lectura facilita la escritura.
Robert C. Martin
Existen una serie de principios y patrones del desarrollo que guían al desarrollador para generar código “limpio”. Estas buenas prácticas han sido recopiladas por algunos autores de referencia: Steve McConnell (Code Complete, 1993), Andrew Hurt (The Pragmatic Programmer, 1999) y Robert C. Martin (Clean Code, 2008):
El código limpio se lee como prosa bien escrita. El código debe ser agradable: los nombres de variables deben ser reveladores, el flujo de control debe ser sencillo. El código limpio cuenta una historia.
El código limpio es sencillo y conciso. Debe evitar flujos complejos, abstracciones innecesarias, o división de funciones excesiva. Es mejor el código simple que el complejo.
El código limpio es honesto. El código debe mostrar claramente sus intenciones, sin ambigüedades, sin sorpresas, sin efectos colaterales, sin “mentiras”. Por ejemplo, si se invoca la función fillComboBox, no se espera escribir un fichero. Algunos autores dan mucha importancia a esta práctica de codificación, al punto de elevarla al rango de principio. El Principio de Menor Sorpresa (Principle of Least Surprise) o también llamado Principio de Menor Asombro fue muy difundido por Ward Cunningham y las comunidades de Extreme Programming. El principio dice que el código debe comportarse como cabe esperar. Este principio también se aplica al diseño de interfaces de usuario o al diseño de APIs.
El código limpio expresa lenguaje del dominio del negocio. Por ejemplo, en vez de print(inputRecord), es preferible sendPromotionLetterTo(employee). print(inputRecord) puede ser escrito en cualquier ámbito, tanto para enviar una carta de recomendación a un empleado como para escribir un registro traza en un fichero de log. La sentencia print(inputRecord) no "habla" del negocio.
El código limpio es fácilmente testable. El programador debe poder ejecutar segmentos de código sin necesidad de “montar” la aplicación completa.
Algunas buenas prácticas para escribir código limpio son:
Naming adecuado. El nombre de variables, objetos, tipos y clases debe ser suficientemente ilustrador. La lectura del nombre debe trasladar rápidamente al desarrollador el objeto del código que está leyendo. Los nombres deben ser intencionados y descriptivos. Deben evitarse abreviaciones, prefijos, usar secuencias de números en variables y las palabras redundantes.
💬 Es paradójico que a la hora de programar no haya ninguna otra habilidad con tanto impacto en la calidad de código, como la de poner nombre, y que a la vez esté tan infravalorada o sea tan ignorada, pese a ser bien conocida.
Carlos Blé, Código Sostenible
Además, deben seguir las convenciones de codificación del lenguaje. Las convenciones son un conjunto de normas para un lenguaje de programación específico que recomiendan estilos, buenas prácticas y métodos para mantener el aspecto del código fuente. Estas convenciones incluyen la organización de archivos, la tabulación, los comentarios, las declaraciones, los espacio en blanco, las llaves de apertura y cerrado, etc. Ejemplos de convenciones: CamelCase, snake_case, kebab-case, Screaming Case y notación húngara (bStopped). Algunas de estas convenciones no han resistido bien el paso del tiempo.
💬 Un nombre pronunciable no empieza por símbolos como el guion bajo, ni utiliza convenciones/notaciones que se usaban en otra era (como la húngara), cuando el código se enviaba a la impresora matricial. Si utilizamos la regla de nombres pronunciables, no podemos comernos letras de la palabra para acortarla. Los nombres han de ser tan largos como sea necesario para representar el concepto adecuado.
Carlos Blé, Código Sostenible
Funciones pequeñas. Deben ser reducidas apenas unas decenas de líneas y con nombres descriptivos (sin importar su longitud). Deben evitar el anidamiento excesivo (funciones que invocan otras funciones que a su vez invocan otras funciones). No deberían tener más de dos o tres parámetros.
Comentarios. Los comentarios solo están justificados cuando el programador no es capaz de expresar la motivación del código con la semántica del propio código. Deben responder al “por qué” se ha escrito el código de esta forma.
💬 El buen código es su propia documentación. Antes de añadir un comentario, pregúntate: ¿Cómo puede mejorar el código para que este comentario no sea necesario?
Steve McConnell, Code Complete
Formato. Debe establecerse un orden dentro de la clase, por ejemplo, constantes estáticas, variables estáticas, variables de instancia y funciones.
Para mantener el código limpio, Robert C. Martin propone en su libro “Clean Code” aplicar al código la regla del boy scout: “Deja el código más limpio de cómo lo encontraste”. Ejemplos de la aplicación de esta regla es borrar código comentado, cambiar el nombre de algunas variables o métodos, modificar un comentario, etc.
Es necesario aclarar que la aparición de nuevas herramientas y lenguajes pueden hacer cambiar los criterios comúnmente aceptados sobre las buenas prácticas a la hora de escribir código. Por ejemplo, la intelligence de los nuevos IDEs, ha hecho caducar la antigua buena práctica “una función solo debe tener un punto de salida”. Hoy, los IDEs ayudan al desarrollador a detectar bifurcaciones de código que acaban sin respuesta. De esta forma, lo que antes se consideraba una buena práctica hoy es considerado un antipatrón. La buena práctica actual consiste en escribir “cláusulas de guardia”, ofreciendo múltiples puntos de salida, facilitando la comprensión del código al eliminar las “puntas de flecha” (largas secuencias de if-else anidadas).
Los ingenieros de software poseen el “superpoder” de automatizar tareas. La industria de software emplea este “superpoder” para beneficiarse a sí misma, creando herramientas software para construir software. Son las denominadas herramientas lower CASE:
Entorno de Desarrollo Integrado (IDE)
Depuradores
Control de versiones
Integración Continua
Frameworks
Analizadores
Autogeneradores de código
Referencias:
• Titus Winter, “Software Engineering at Google”, O`really, 2020
• Carlos Blé, “Código Sostenible”, savvily, 2020
• A. Hunt, "The Pragmatic Programmer. From Journeyman To Master", Addison-Wesley, 2000
• R.C. Martin "Código Limpio", Anaya, 2009
• S.McConnell "Code Complete", Microsoft, 1993
• R.C. Martin "Arquitectura Limpia", Anaya, 2018
• Autentia. Software Design: Principios y patrones del desarrollo de software