Sesión interna de refactoring

El pasado miércoles impartí una sesión sobre calidad en Luce I.T., concretamente sobre Refactoring. Las slides están aquí (lamentablemente dependen mucho de la explicación).

Intenté explicar los conceptos fundamentales basándome en el ejemplo de Refactoring de Martin Fowler. Para los que no hayáis leído el libro, en el primer capítulo se describe un pequeño proyecto de alquiler de películas. El código del mismo está aquí.

La mayor parte de la sesión fue la ejecución en público de las refactorizaciones del ejemplo, que originaron interesantes discusiones con todos los presentes (unas 15 personas) sobre los pasos más controvertidos. Los pasos más ‘polémicos‘ fueron los siguientes:

  • Sustitución de los comentarios por código descriptivo que les hacía redundantes (ya tuvimos una sesión al hilo de Clean Code sobre este asunto por lo que este tema concreto no originó preguntas).
  • Usar más de un return statement… en el ejemplo Martin Fowler contradice las ‘buenas prácticas’ que se suelen enseñar en la carrera de Informática para poder eliminar flags de control o simplemente hacer el código más claro. En este caso concreto si que hubo una discusión interesante, sobre todo teniendo en cuenta que la siguiente refactorización que sugiere es…
  • Usar un operador ternario, en un caso concreto en el que el código queda poco legible (sin haber extraído constantes tampoco). Esta refactorización no despertó la simpatía de los presentes y creo que es bastante discutible. A mi los operadores ternarios no me molestan para situaciones muy puntuales, pero entiendo que son poco claros.
  • Hacer un inline de un temporal sustituyendo varias llamadas al temporal por la ejecución de un método (replace temp with query). Este refactoring despertó críticas por la supuesta pérdida de eficiencia, pero enseguida se llegó a un consenso, ya que en el ejemplo la sustitución era inofensiva en términos de rendimiento (y aprovechamos para explicar el concepto de optimizacion temprana). Ejemplo:
    - double thisAmount = rental.getCharge();
    - String.valueOf(thisAmount) + "\n";
    - totalAmount += thisAmount;
    + String.valueOf(rental.getCharge()) + "\n";
    + totalAmount += rental.getCharge();
  • Reemplazar switch con polimorfismo y la máxima de que si tienes que tener un switch, que sea en el código de la clase propietaria para que sólo esté en una única clase. Esta refactorización despertó muchas simpatías, aunque sea compleja de seguir. Aprovechamos para explicar las maldades de los switch, el código polimórfico era sustancialmente más simple.
  • Explicamos los problemas en Java con los temporales al extraer métodos (con 0 o 1 la refactorización es trivial) y la raíz del problema, que Java utiliza paso por valor de la referencia, por lo que las modificaciones de más de un temporal (si es sólo uno puedes devolverlo en el return) dentro de un método hacen que extraer métodos de ese código sea sustancialmente más complejo.
  • Otro refactoring polémico fue este:
    for (final Rental rental : rentals) {
    -        frequentRenterPoints += rental.getRenterPoints();
    (...)
    }
    + public Double getTotalAmount() {
    +    Double totalAmount = 0D;
    +    for (final Rental rental : rentals) {
    +      totalAmount += rental.getCharge();
    +    }
    +    return totalAmount;
    +  }

    Sustituyendo un cálculo que se realizaba dentro de un bucle, junto con otras cosas, a un método aparte y dejando el bucle (más código y dos bucles). La principal pega era rendimiento, ya que la refactorización separaba cálculos y dejaba el código más claro, pero teniendo en cuenta de nuevo la optimización temprana no es un problema.

Por último, resumí brevemente las slides y tuvimos una ronda de dudas y preguntas. Se quedaron en el tintero ejercicios prácticos por parejas, estilo los videos de Jason Gorman.

Realizaremos más sesiones sobre refactoring en un futuro. Si estás por Valladolid y te apetece pasarte a una sesión (miércoles cada 2 semanas), dame un toque. Estáis todos invitados :)

PD: @jmbeas tiene una serie de posts más extensos sobre los refactors empleados en el libro, recomiendo echarles un ojo: http://blog.jmbeas.es/tag/refactor/

Review de Refactoring: improving the design of existing code

La semana pasada, para preparar decentemente la charla que impartí en Luce I.T., leí Refactoring: improving the design of existing code de Martin Fowler y Kent Beck. Refactoring es un libro que tenía pendiente desde hace tiempo y no me ha defraudado.

Estructura

El libro comienza explicando el concepto básico de Refactoring mediante un ejemplo práctico. De esta forma introduce rápidamente las ventajas de aplicar refactorización. Posteriormente, en capítulos posteriores, introduce las definiciones, ventajas e incovenientes y consejos de cuando aplicar refactoring.

El gran núcleo del libro se dedica a los code smells y a la lista de posibles refactorings con guías de aplicación paso a paso. El libro se completa con tres capítulos escritos por invitados, uno sobre general sobre refactoring, otro de la herramienta específica de Smalltalk y un capítulo final escrito por Kent Beck.

Un pequeño resumen destilado está en estas slides (pulsa ‘c’ para ver los comentarios).

Citas destacadas y comentarios

Comento algunas citas que han gustado o provocado especialmente (el resto las he dejado en quotista.com):

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand”

Sobre la importancia de refactoring y del naming concretamente. Esta cita ya la había visto con otras formas.

“With refactoring you find the balance of work changes. You find that design, rather than occuring all up front, occurs continuously during development”

Sobre refactoring y diseño. Ante el BDUF, Martin Fowler defiende una mejora del diseño continua, la refactorización soluciona el deterioro del diseño a lo largo del tiempo de vida del proyecto.

“Here’s a guideline Don Roberts gave me: The first time you do something, you just do it. The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway. The third time you do something similar, you refactor.”

La polémica regla de la tercera vez… discuss!

“The other time you should avoid refactoring is when you are close to a deadline. At that point the productivity gain from refactoring would appear after the deadline and thus be too late.”

El problema de este tipo de sentencias es que sirven para justificar nuestros pecados.

“It is better to write and run incomplete tests than not to run complete tests. You should concentrate on where the risk is. Look at the code and see where it becomes complex.”

Mejor algo que nada…

Conclusiones

Me ha gustado especialmente el capítulo de Kent Beck, sobre la importancia de establecer objetivos claros y saber parar. Martin Fowler menciona el problema de ‘cavar demasiado’ y refactorizar sin un rumbo claro y recomienda la suma de pequeñas refactorizaciones para lograr el mismo objetivo.

A lo largo del libro se menciona la importancia de saber con certeza la actividad que estás realizando en todo momento, con una metáfora de sombreros (llevar puesto el sombrero de refactoring o de desarrollador) y no mezclar en el mismo instante las dos activades.

Me gusta especialmente la lista exhaustiva de refactorings, de code smells y las razones por las que refactorizar, cuando, los problemas y qué contar al jefe (si no le importa la calidad, no se lo cuentes y hazlo!).

Por poner pegas al libro, las guías de refactorización pierden un poco el sentido ante el avance de los entornos con refactorizaciones automatizadas (aunque no para todos los lenguajes) y echo de menos alguna refactorización más larga (estilo Clean Code).

Son pequeños comentarios a un libro editado en 1999 que sigue totalmente vigente. Lectura recomendada.

Curso de entorno Ruby

Los días 22 y 23 de Julio se celebró un curso sobre el entorno Ruby impartido por @jacegu. El curso se realizó en las instalaciones de I+D+i del Ayuntamiento de Valladolid, en la calle Vega Sicilia.

Contenido

En el curso tratamos las herramientas que componen el entorno Ruby, entre ellas:

  • Manejo de rvm
  • Los fundamentos de ri e irb y realizamos algún ejercicio a partir de ellos
  • Uso de MiniTest
  • Introducción a Rake
  • Uso de Bundler e instalación de gemas
  • Realización de tests con RSpec
  • Test de aceptación con Cucumber
  • Las posibilidades de Capybara y una pincelada de Sinatra

El curso en sí fue excepcional, avanzamos por cada tema aprendiendo usos avanzados y consejos de un desarrollador con mucha experiencia en el manejo de esas herramientas. En todo momento estuvo dándonos soporte y resolviendo todas las dudas.

Hasta entonces no había visto ejemplos de usos reales de Cucumber y me pareció muy interesante, junto con la facilidad de testear Sinatra con Capybara.

Conclusiones

La calidad del curso fue sobresaliente, me parece increíble (no me deja de sorprender) que la gente elabore y dé cursos de esa calidad gratuitamente. Por ello, tengo que dar las gracias a @jacegu por su tiempo y dedicación. El único punto negativo es que el curso marca su probable marcha a Madrid :’(.

Como comentario atrasado, las instalaciones de la Agencia de Innovación y Desarrollo Económico son impresionantes, las conocí en las presentaciones de la aceleradora de proyectos y las utilizamos para el taller de Groovy con Alberto Vilches. Sus trabajadores han estado siempre atentos y colaboradores, por lo que se merecen un Gracias Enorme, en especial @ditelnet.

Ahora @jacegu ha levantado el guante, así que esperamos próximamente más cursos sobre lenguajes o tecnologías (a quién no le gustaría una introducción a python :P ).

Nueva apariencia

No he resistido la tentación y he cambiado de theme (una vez más). Intento encontrar la mejor combinación de sencillez y legibilidad, pero se me resiste. He hecho algún retoque en el archivo .css pero no se si va a mejor. Todavía tengo algún retoque pendiente y el color de la fuente por defecto y el estilo de los enlaces no me terminan de convencer.

Si tenéis cualquier sugerencia o recomendación, es más que bienvenida! Si aborrecéis este theme, comentádmelo también y doy marcha atrás.

Imagino que al final terminaré creando un theme nuevo o desarrollando algo por mi cuenta, pero hasta que no tenga el tiempo necesario voy a intentar seguir así.

Espero vuestro feedback :P

Update con joins

Estos últimos días he estado integrando una base de datos SQL Server en otra existente en Oracle (not fun). Llevaba mucho tiempo sin trabajar directamente con sentencias SQL, así que no me ha costado nada de tiempo descubrir algo que no sabía.

Así que, exponiendo mi ignorancia: no sabía que se podían hacer cruces en sentencias update de SQL. Y al parecer es algo bastante común. Para el que no lo supiera, como yo, aquí va un pequeño resumen.

La sintaxis habitual de una sentencia UPDATE es:

UPDATE tabla t SET t.columna = valor;

Si quieres hacer un cruce con otra tabla, siempre he hecho algo así:

UPDATE tabla t SET t.columna = (SELECT e.valor FROM tabla2 e WHERE t.columna = e.columna);

Esa sentencia es muy ineficiente, por lo que tarda mucho. Puedes hacer apaños y poner un índice en el cruce o bien, utilizar joins directamente! . Esto es válido en MySQL:

UPDATE t SET t.columna = e.valor FROM tabla t INNER JOIN tabla2 e ON t.columna = e.columna;

Puedes completar la consulta con los cruces que necesites. Es sustancialmente más rápida, por lo que ahora pienso en los minutos que he gastado optimizando updates con subconsultas.

En Oracle la sintaxis es diferente:

  UPDATE /*+BYPASS_UJVC*/
   (SELECT t.columna antiguoValor,e.columna nuevoValor
    FROM tabla t, tabla2 e
    WHERE t.columna = e.columna) 
  SET antiguo = nuevo

El hint (/*+BYPASS_UJVC*/) es necesario ya que por defecto sólo actualiza si los valores están indexados. La sintaxis de este UPDATE no se parece a nada de lo que haya visto antes pero funciona. Escribes tu consulta de selección y luego aplicas unos alias para actualizar.

Ya me diréis si os es útil o si ya lo sabíais desde hace mucho tiempo.

PD: Comentaba @germanDZ que el hint es feo feo. Adjunto la respuesta, porque da más claridad al post:

A mi me parece feísimo. En AskTom comentan que nunca debería usarse para una query repetible (en PL o aplicación) pero para una manipulación manual con resultado acotado (como era el caso) te sirve para salir del paso.

Muchísimo más bonita la sintaxis estándar que Oracle no soporta :S.

Otra posibilidad es hacer un Merge :)