Resumen de Clean Code - tercera parte
Vamos con los ultimos temas teoricos de clean code:
Capitulo 9: Unit Tests
Como me gusta este capitulo!, un libro sobre buenas practicas tiene que hablar sobre buenas practicas de código de test, y casi ninguno lo hace. Hace algo menos de dos años que comenzamos a trabajar usando seriamente unit testing, primero haciendo test depues de escribir el código, y ultimamente vamos consiguiendo escribirlos primero y hacer TDD, cuesta mucho el cambio de mentalidad pero esta es la practica que, con mucha diferencia, más a contribuido a que mejore como programador. Si quieres hacer algo para mejorar como profesional mi recomendación es que pongas al unit testing y al TDD los primeros en tu lista, por delante incluso de aprender nuevos lenguajes y muy por delante de aprender el nuevo framework pepito que piden en las ofertas de trabajo.
La recomendación más importante que se ofrece sobre los test es tomarse el código de test en serio, es decir, el código de test no es un código de segunda categoria, si aplicas X practicas en tu código de producción aplicalas también en el código de test!, no caigas en el error de "como es un test vale todo". Y lo digo desde la experiencia de caer en este mismo error cuando empezamos a escribir pruebas, pruebas que a los dos meses no hay quien entienda ni quien mantenga y que lo que hacen más que mejorar la calidad de desarrollo es ralentizarlo por el costo de mantenerlos.
El tio bob también habla de TDD y ofrece su receta para hacer TDD, son tres pasos:
- No puedes escribir código de producción antes de tener un test que falle.
- No puedes seguir escribiendo código de un test si ya has escrito lo suficiente para que falle, y los errores de compilación son fallos (recordar que con TDD escribes el test incluso antes de que las funciones a probar existan!).
- No puedes escribir más código de producción que el estrictamente necesario para que pase el test que actualmente esta fallando.
Tela eh?, parece facil pero cuando te pones ha hacerlo en la practica es muy dificil lograr la displina y la habilidad escribiendo test (y escribiendo código testable, que es lo más dificil!) para cumplir con estas 3 reglas, a mi me ha costado como os digo casi dos años y todavía no siempre soy capaz de seguir estos 3 principos a simple vista simples. Pero cuando lo logras, cuando construyes de esta forma una funcionalidad, poco a poco pasando pequeños test y añadiendo pequeños trozos de código a un sistema que siempre se mantiene estable, la sensación que te queda de estar haciendo realmente un buen trabajo es fantastica.
Posteriormente se explica el principio FIRST, que son 5 cualidades que debe tener un buen test:
- Fast: Los test deben ejecutarse rapido. Si no al final el equipo terminará por no pasarlos. Yo añadiria que esto es cierto para los test unitarios, para los funcionales no me importa tanto la velocidad sino hacer el test con un sistema real (base de datos, sistema de fichero etc,etc incluidos)
- Independant:Un test nunca debe depender de otros para pasar. Los puedes ejecutar en cualquier orden y deben funcinar igual.
- Repeteable:El test se debe ejecutar encualquier entorno. En el equipo de desarrollo, en el servidor de IC. Lo ideal es bajarselo del sistema de control de versiones ejecutar un comando (mvn clean install by example) y que toda la aplicación compile y los test pasen.
- Self-validating:Un test debe devolver: "correcto" o "fallo" y no debe requerir ningún tipo de intervención manual posterior (mirando un log por ejemplo) que determine si el test paso o no.
- Timely:Con esto se refiere a que los test deben ser escritos en el momento adecuado, es decir, antes que el código que prueban.
En resumen, un gran capitulo sobre test unitarios que si hubiera leido hace dos años me hubiera ahorrado varios dolores de cabeza. Por ponerle alguna pega, no se entra en suficiente detalle en como escribir código testable (aunque si se trata en otros capitulos del libro), que en mi experiencia es lo más dificil de conseguir y de hacer entender a quienes empiezan con el unit testing, para esto os recomiendo el blog de misko hevery, que tiene articulos fantasticos sobre el tema y una guia de como escribir código testable.
Capitulo 10: Classes
La recomendación más importante en este capitulo coincide con la del capitulo sobre funciones, hacer clases pequeñas!. Tio bob habla sobre mantener el principio de cohesión y el SRP (Single Responsability principle o principio de responsabilidad única) en las clases y como consecuencia de estos terminamos con sistemas formados por muchas clases pequeñas con responsabilidades bien definidas. No podría estar más de acuerdo, no sabría dar un número de lineas maximo porque además me parece un poco arbitrario y dependiente de cada sistema, digamos que personalmente más de 500 lineas debería hacer que saltasen las alarmas y pararse a mirar si podemos subdividir esa funcionalidad en varias clases.
Otra recomendación importante es la de depender de interfaces o bien clases abstractas en lugar de implementaciones concretas. Esto es importante por dos motivos: mejorar la flexibilidad del diseño y mejorar la testabilidad del código. Esto puede ser un poco más discutible porque en ocasiones da lugar a sistemas donde tenemos 1 clase por cada interfaz, y a ojos de muchos resulta excesivo. Yo personalmente si soy partidario de depender siempre de interfaces aún que tenga que escribir un poco más, me parecen más interesantes los beneficios (sobre todo el testing) que lo que me cuesta escribirlos. Aunque para esto se me hace casi indispensable utilizar inyección de dependencias, de esto habla el tio bob en el siguiente capitulo precisamente.
Capitulo 11: Systems
En este capitulo se habla de algunos elementos importantes para la arquitectura general de un sistema orientado a objetos. En primer lugar se habla de algo que me parece realmente interesante, un sistema orientado a objetos tiene dos partes fundamentales que conviene separar:
- Construcción de objetos: Al inicio del sistema cuando se crean los objetos necesarios para su posterior funcionamiento
- Uso de estos objetos: Cuando el sistema esta en marcha y los objetos se usan para proporcionar la funcionalidad requerida
Esta separación sirve para justificar el uso de la inyección de dependencias, que será la encargada de la construcción de los objetos. De modo que nosotros declarativamente indicamos al sistema de inyección de dependencias que objetos queremos que coja para construir nuestro sistema y posteriormente usamos esos objetos cuando el sistema esta en marcha. Esta forma de ver la arquitectura de un sistema OO me parece realmente interesante y es una buena forma de mantener el desacoplamiento entre clases, es el sistema de DI el encargado de saber que implementaciones tiene que instanciar y a que objetos colocarselas como dependencias. Esto da lugar a sistemas más flexibles y cuyas partes (que además son simples y cohesivas) podemos probar facilmente mediante pruebas unitarias y dobles de pruebas (stubs o mocks). Nosotros actualmente seguimos este enfoque utilizando google guice como motor de DI, y estamos muy contentos con la aproximación, tenemos clases más cohesivas, menos acopladas, más facilmente testables y un sistema más flexible donde se puede cambiar una implementación por otra en cada parte del sistema.
Por ultimo el tio bob también habla en este capitulo sobre los "cross-cutting concerns" en castellano sería algo como "funcionalidades transversales", es decir aquellas funcionalidades tipo logging, seguridad, transacciones etc, que están dispersas por todas las clases del sistema. Lo que propone aqui esta claro, usar programación orientada a aspectos. Para esto podemos usar un framework AOP completo como AspectJ o aproximaciones más modestas como el soporte para AOP de spring o el mismo guice. Nostros actualmente usamos interceptores de guice para controlar algunas cosas como transacciones o tratamiento de excepciones.
Capitulo 12: Emergence
Este capitulo es basicamente una apuesta por el diseño incremental frente el diseño up-front. Muy en la linea del desarrollo ágil y de lo propuesto en XP por Kent Beck. Se trata de no tratar de diseñar mucho antes de tiempo, diseñar sólo lo necesario para la funcionalidad a implementar hoy sin pensar en la de mañana. Cuando llegue la funcionalidad de mañana ya pensaremos en ella, y como además hemos seguido todos los principios anteriores (clases pequeñas, metodos pequeños, cohesión, SRP etc) nuestro sistema será lo suficientemente flexible para aceptar nuevos cambios y además mantenerse limpio (que es el objetivo de este libro ¿no?: clean code). Algo que también esta muy en la linea de unos de los principios de LEAN, no decidir hasta el ultimo momento responsable para hacerlo.
Para lograr que un sistema emergente no se convierta en un caos se mencionan los 4 principios del diseño simple (estos son de kent beck), en orden de importancia:
- Todos los test pasan
- No existe duplicación de código
- El sistema expresa la intención del programador
- Minimizar el número de métodos y clases
Estos principios basicamente son la base del Red-Green-Refactor-Green de TDD, del 2 al 4 corresponderían a la fase de refactorización.
De nuevo coincido plenamente con todo lo que se dice en este capitulo, he metido la pata muchas veces por anticipar diseños antes de tiempo y he complicado muchos sistemas por incluir cosas que luego no se han usado jamas. En mi opinión el diseño simple, los test y la refactorización, son la mejor aproximación que conozco para el desarrollo de software y la que trato de aplicar todos los días, unos días mejor y otros peor eso si :P
Capitulo 13: Concurrency
Este capitulo es el que menos me gusta y sinceramente me parece algo fuera de lugar en este libro. Supongo que lo habrá incluido por el interes creciente en la programción multihilo. Sin ser un experto ni de lejos en este tema las recomendaciones que se dan me parecen muy basicas y no me a aportado gran cosa, hay libros dedicados a estas cuestiones mucho más completos.
Conclusión final del libro
Como os decia además de los capitulos que he comentado teneis los capitulos puramente practicos, donde se coje un trozo de código de proyectos reales y se destripa para mejorarlo con los principios contados, estos son altamente recomendables pero no tiene mucho sentido que los resuma.
Las practicas y las ideas que defiende el tio bob en este libro están muy en linea con las practicas de desarrollo ágil (unit testing, diseño simple etc,etc) y también están muy en linea con mi forma de hacer las cosas, quizas por eso mi opinión pueda ser un poco sesgada jeje, pero para mi es un libro fundamental que devuelve la importancia y el foco de un desarrollo de software al sitio del que nunca tenia que haber salido: el código por supuesto. Un libro de diseño OO que no dibuja UML's, coje el código de un proyecto Open Source real y lo modifica aplicando las buenas practicas descritas, un libro que habla de profesionalidad a la hora de escribir código en estos días de carnicas y programadores licenciados en filologia hispanica a 100 pesetas unidad. Quien se quiera seguir engañando (o engañando a otros) que siga, pero el buen software lo construyen los buenos programadores que escriben CLEAN CODE, da igual que pongas un jefe de proyecto con un MBA y con un PMP otorgado por el PMI experto en el uso del project, o que hayas pasado las evaluaciones SCAMPI para el CMMI nivel 3 y 1/3. Si quieres desarrollar buen software preocupate de tener buenos PROGRAMADORES (en mayusculas) profesionales y con talento, el resto es papel mojado.
Un libro fundamental para gente con poca experiencia, sobre todo si va a participar en algún proyecto ágil, y un libro que puede hacer a gente con más experiencia replantearse la forma de hacer muchas cosas. Eso si, como consejo para los que tengais experiencia, leer el libro con mente abierta y dispuestos a replantearos vuestra forma de ver las cosas en muchos aspectos, si lo leeis pensando que lo sabeis todo antes de abrir el libro no vais a sacar mucho beneficio, y además, ¿si ya lo sabeis todo para que leeis libros?, escribirlos! :P.
Maestro, tus conclusiones finales me han gustado muchísimo. Espero seguir el camino del TDD y volverme un maestro como tú.
Por cierto, vendría bien un podcast al respecto. Yo lo escucharía con mucho agrado. Y de pasadita podrías invitar a Greeneyed a que participe del podcast a ver qué opina.
Saludos!
Enviado por Germán G. en octubre 13, 2009 a las 01:17 AM GMT+01:00 #
Excelente resumen acabo de leer las tres entradas, desde hace unos meses ando muy interesando en todos estos temas de agilidad, buenas practicas y unit testing y la verdad es que aunque requiere esfuerzo cambiar de mentalidad y aplicar lo que se tiene que aplicar, los beneficios a largo plazo y con el tiempo a corto plazo se notan.
Tomare este resumen y reorganizare y agregare algunas cosas para que sirva de guia rapida de cosas que TENEMOS que hacer en mi equipo de desarrollo.
continua...
Enviado por Marioko en octubre 13, 2009 a las 05:54 AM GMT+01:00 #
En mi empresa, algo en lo que todavia estamos muy crudos es en el tema del Unit Testing. Por todo lo que he leido y lo que medio he probado se y tengo grabado de que es algo importante que TENEMOS que hacer si queremos incrementar la calidad de nuestros desarrollos, el problema es que requiere mucho mas esfuerzo, obviamente hay que escribir mas codigo y casi siempre el tiempo es muy cruel en los proyectos de software, ademas (soy sincero) da pereza tener que escribir codigo que pruebe el codigo que tengo que escribir normalmente.
Alfredo, tu que ya llevas un buen tiempo implementando y de seguro luchando por ser un buen "Uniter Tester" ¿ podrias mencionar algunas sugerencias para superar esos inconvientes..??
muchas gracias por todo
Enviado por Marioko en octubre 13, 2009 a las 05:57 AM GMT+01:00 #
Hola Alfredo.
Estupendo ¿resumen?. Te lo has currado . Muchas gracias, ya puedo poner otros libros delante de éste en mi mesilla!
La verdad que más que un libro es también una declaración de intenciones con la que estoy practicamente al 100% de acuerdo. Y por supuesto con tus conclusiones.
Para complementar un poco, te dejo la referencia al libro de testing con junit de Pragmatic Programmer[1] que es donde primero lei lo de FIRST y que da muy buenas prácticas sobre cómo escribir buen código de test. Es barato y se lee muy rápido. Algunas de ellas las recogí en mi blog hace tiempo[2] aunque de forma excesivamente esquemática.
Sigue...
Enviado por jcesarperez en octubre 13, 2009 a las 10:42 AM GMT+01:00 #
A ver si le decimos a J. Rubira de hacer un podcast sobre testing, yo me apuntaría!
Hablas de desarrollar buen software, de profesionalidad y de buenos programadores. Yo añadiría que un desarrollo profesional no es tal sin una parte de testing medianamente automatizado.
[1]: http://www.amazon.com/Pragmatic-Unit-Testing-Java-JUnit/dp/0974514012
[2]: http://jcesarperez.blogspot.com/2009/04/buenas-practicas-para-programar-tests.html
PD: ¿Puedes desactivar eso de los mil caracteres?
Enviado por jcesarperez en octubre 13, 2009 a las 10:42 AM GMT+01:00 #
Estaría bien lo del podcast sobre testing, habra que comentarselo a jorge a ver si hay hueco para nuevos temas.
marioko: Lo del tiempo es realtivo, cuando cojes ritmo escribiendo test no creo que tardes mucho más porque te enfocas en escribir sólo el código necesario, te evitas sesiones de debug para probar cada funcionalidad y sobre todo porque al aumentar la calidad de tu código tienes menos bug y dedicas menos tiempo a resolver incidencias. Pero logicamente tienes un tiempo de adaptación inicial que superar.
Luego, para empezar hacer TDD es complejo, como comienzo tratar de hacer test (despues del código) de las partes usualmente más conflictivas del proyecto, para iros familiarizando con la tecnica, ver que problemas actuales tiene vuestro código que lo hacen dificil de testear y pensar en como resolverlos. Y ya cuando os sintais seguros con la tecnica y tengais más claro el valor que aporta el unit testing a vuestros proyectos podeis meteros con el TDD.
Enviado por Alfredo Casado en octubre 13, 2009 a las 12:16 PM GMT+01:00 #
julio cesar:
"Yo añadiría que un desarrollo profesional no es tal sin una parte de testing medianamente automatizado."
Estoy muy de acuerdo con eso, de echo creo que es un factor diferenciador muy gordo para medir la madurez de un equipo de desarrollo. Lo más basico como profesional es entregar software que funcione, y sin testing automatico asegurar que un software funciona es MUY costoso, habría que hacer a mano todas las pruebas de regresión y según crezca el sistema esto es inviable, la otra opción es hacer pruebas manuales sólo de lo ultimo que ha cambiado y confiar en la intuición de que no nos hemos cargado nada. Como esto ultimo es lo que hace todo el mundo... la conclusión es que sin pruebas automaticas entregamos software con nuestra intuición como prueba de su funcionamiento, y no, esto no parece muy profesional, tenemos que dar un paso adelante y mejorar mucho en esto.
Enviado por Alfredo Casado en octubre 13, 2009 a las 12:29 PM GMT+01:00 #
Estupendo tu resumen del libro, pero sobre todo tus conclusiones. Parece que mucha gente en la industria todavía no se da cuenta de la importancia de las buenas prácticas y de programadores que sepan aplicarlas.
Sobre el mismo tema, estoy leyendo Code Complete 2 (http://cc2e.com/), seguro te gustará. Por lo mientras ya iré pidiendo Clean Code.
Un tema que me interesa mucho es algo de lo que ya ha escrito jcesarperez en su blog: como garantizar que estas prácticas se lleven a cabo. Por lo mientras me estoy montando un ambiente de integración continua con Hudson y Sonar para verificar algunos de estos temas. ¿Qué usáis en vuestra empresa?
Por cierto, más que un podcast sobre TDD, valdría la pena una serie de podcasts sobre estas prácticas. Ya alguna vez lo han pedido en el portal.
Saludos
Enviado por Erick Camacho en octubre 13, 2009 a las 02:24 PM GMT+01:00 #
Hola erik: Precisamente nosotros tenemos un entorno con hudson y sonar. Anteriormente usabamos hudson con varios plugins para mostrar resultados de metricas (checkstyle, hudson, cobertura de pruebas) y nos cambiamos a sonar hace poquito (un mes aprox) que tiene un UI sencillamente espectacular, no por lo bonito (que también) sino por lo bien organizado y la visibilidad que te da sobre los proyectos.
Enviado por Alfredo Casado en octubre 13, 2009 a las 02:45 PM GMT+01:00 #
Sobre la misma, quisiera comentar: En la lista de plugins de Hudson aparecía uno que me llamó la atención que se llama Crap4J Plugin, desarrollado por Alberto Savoia. La idea básica es presentar una métrica de código "más fuzzy" por decirlo de algún modo, con el fin de hacer que los desarrolladores se interesen más acerca de la métrica que se está calculando.
Savoia indica en una charla dada en businessofsoftware que las métricas denominadas "cobertura", "complejidad ciclomática" y otras, no son muy atractivas como decirle a tu compañero: "your code is 66% crappy".
Les dejo los links que encontré en su momento:
http://wiki.hudson-ci.org/display/HUDSON/Plugins (buscar por Crap4J)
http://network.businessofsoftware.org/video/2352433:Video:21
Vaya! Erick ya había comentado en JavaHispano acerca de Crap4J
http://www.javahispano.org/contenidos/es/crap4j_es_tu_codigo_basura/
Enviado por Germán G. en octubre 14, 2009 a las 05:13 AM GMT+01:00 #
Excelente resumen Alfredo y muy buenas aportaciones. Me compré Clean Code nada mas salir del Agile Open 2009. Y espero terminarlo pronto.
En mi empresa también se utiliza Hudson con Sonar...una maravilla.
Un placer haber sido alumno tuyo.
Saludos,
Israel
Enviado por Israel Alcazar en diciembre 09, 2009 a las 01:00 AM GMT+01:00 #
Bueno, en efecto tu opinión es sesgada, como lo has anotado. La programación es nada si no se tiene un proyecto (y por supuesto gerentes del mismo, entre otros) que desarrollar, así sea para una sola persona. Claro está que también es errada la afirmación de que la programación son solo las ollas, pues ¿qué sería de los modelos MDA sin la programación que la que les da la plataforma? Es como decir que las manos funcionan por si mismas, sin necesidad de un cerebro: La ingeniería de software (gerencia de proyectos, arquitecturas de sw, metamodelos, etc) necesita de la programación, y la programación necesita de la ingeniería de software. El "foco" debe estar centrado en todo el proceso; tanto en las fases no estructurada (representada por la ingeniería, por los modelos) como en las estructuradas (representadas por la programación, por el código).
Éxitos
Enviado por Christian en diciembre 29, 2009 a las 11:53 PM GMT+01:00 #