El programador «copiar y pegar»

Me gusta habitualmente aprender, reflexionar y aplicar a mi día a día buenas prácticas de programación, ya que indudablemente hacen que mi trabajo sea de mejor calidad en general, logrando productos de software más robustos, eficientes y, sobre todo, fáciles de mantener.

Parte del proceso de aplicar buenas prácticas es, sin duda, identificar las malas prácticas y sustituirlas por buenas. En esa línea de pensamiento, me gustaría hablar hoy del programador «copiar y pegar».

Menú Copiar y pegar

 

Este programador es aquel chico o chica (muchas veces no tan chico o chica), que en el desarrollo de una tarea X, encuentra un trozo de tamaño arbitrario de código y, sin pensarlo mucho, presiona la temida combinación «ctrl+c», «ctrl+v», ajusta el nombre de 2 variables, compila y se va a casa creyendo haber realizado una buena y rápida tarea.

Comienzo aclarando que copiar y pegar no es, en si mismo, una mala práctica, pero encierra dos grandes riesgos:

  • Cuando la fuente del código que se copia está dentro del mismo proyecto, usualmente violamos el principio DRY, lo que nos lleva a producir código duplicado (altamente repetitivo), que es más propenso a errores y difícil de mantener.
  • Cuándo la fuente del código que se copia es el Internet, la introducción de bloques de código oscuro®, en esencia código que no se ha comprendido del todo y, por tanto, no se está en capacidad de corregir o mantener.

En este artículo, voy a centrarme en el primero de los riesgos. Los casos típicos donde esto ocurre son:

Funcionalidad duplicada

Se copia el código de una ubicación a otra, algunas veces algo distante, sin hacerle modificación alguna, o ajustando solamente nombres de variables u otros detalles menores para que encaje en la nueva ubicación —en otras palabras, para que compile.

La funcionalidad es básicamente la misma, por ejemplo, crear un cliente nuevo. A partir de ahora, cada vez que cambie la estructura de datos del cliente o se actualicen las reglas de negocio para validar y ejecutar su creación, alguien olvidará actualizar una de las múltiples ubicaciones donde se realiza la tarea y se habrá añadido un nuevo bug al sistema.

Otra posibilidad es que el código copiado contenga ya un error, y luego de copiarlo 20 veces por aquí y por allá, de pronto con una actualización al sistema operativo o la base de datos el error se hace aparente y nos encontramos no con uno, sino 20 distintos lugares a donde ir a corregirlo.

En este caso el máximo de veces que se vale copiar y pegar es CERO.

Falta de análisis y abstracción

Se copia el código de una ubicación a otra, regularmente cercana, muchas veces contigua, se cambia dos o tres cosillas por aquí y allá. La funcionalidad se altera solo un poco en el proceso. El código ¡compila, funciona y listo!.

En este caso, el máximo de veces que se vale copiar y pegar es una, máximo dos. Antes de la tercera, realmente debes detenerte y pensar en abstraer y extraer esta funcionalidad a un nuevo método, procedimiento o función e invocarlo con algunos parámetros que determinen «lo que cambia» entre llamada y llamada. Diría que el 99% de las veces es posible hacerlo en tan solo un par de minutos.

He visto este caso recientemente en una rutina que crea elementos visuales y luego realiza una consulta de base de datos según el item seleccionado por el usuario. De pronto, por un nuevo requerimiento, en los elementos que se van creando hay que configurar dos nuevas propiedades y las reglas de creación de la consulta cambiaron para incluir un par de nuevos filtros.

De pronto, una modificación que hubiera tomado 2 minutos sobre código bien estructurado, puede convertirse en una tarea significativamente más larga y cuyo resultado nadie se atrevería a garantizar. Pasé una o dos horas factorizando el código para evitar este problema en el futuro, lo que también pudo evitarse con solo dos minutos de análisis en un principio.

En ese tiempo eliminé más de 1000 líneas de código duplicado de una unidad. Si, ¡mil líneas de código!

La solución: Abstraer, factorizar, dividir

En todos los casos, la idea básica principal es que exista un solo lugar (método, procedimiento o similares) donde se realiza una tarea específica. Cualquier punto del sistema donde deba llevarse a cabo esta operación, se invocará esta rutina pasando los parámetros adecuados. A más alto nivel, esta funcionalidad se encapsulará en clases con interfaces bien definidas.

Siguiendo con el ejemplo de la creación de un cliente, ofrece un claro beneficio el hecho de existir una única rutina que realiza la tarea. Los datos como el nombre del cliente, su dirección, etc., se reciben como parámetros y toda la lógica está contenida en esta pieza de código. Si la estructura de datos del cliente cambia, o si las reglas de su creación son actualizadas, hay un único punto del sistema que debe modificarse.

En caso que se rompa la interfaz de la rutina, el compilador hará por nosotros la tarea de «detectar» todos los puntos desde donde esta se llama, y por tanto no habrá omisiones o sitios del sistema que se queden fuera de sincronía luego de este cambio.

En el segundo caso, luego de solo un poco de análisis, usualmente se logra encontrar un patrón que nos permite de la misma manera definir una rutina que será llamada con distintos parámetros.

Imaginemos este código escrito mediante la técnica de «copiar y pegar»:

begin
  Item := TMyItem.Create;
  Item.Caption := FCliente.Nombre;
  Item.Tipo := itCliente;
  Item.Color := clBlue;
  
  Item := TItem.Create;
  Item.Caption := FProveedor.Nombre;
  Item.Tipo := itProveedor;
  Item.Color := clGreen;

  //probablmente muchas más repeticiones del mismo!
end;

¿Identificas rápidamente un patrón?

¿Qué tal esta alternativa?


  procedure CrearItem(ACaption: string; ATipo: TItemType; AColor: TColor);
  begin
    Item := TMyItem.Create;
    Item.Caption := ACaption;
    Item.Tipo := ATipo;
    Item.Color := AColor;
  end;

begin
  CrearItem(FCliente.Nombre, itCliente, clBlue);
  CrearItem(FProveedor.Nombre, itProveedor, clGreen);
end;

El resultado es el mismo, pero el código es más fácil de mantener y la estructura de la rutina, al ser más corta, también más fácil de comprender de una mirada, lo cual también ayuda en el mantenimiento, que es habitualmente la etapa más larga de la vida del software.

Seguiremos con el tema de buenas y malas prácticas en una próxima entrega.

¿Te gusta el contenido?

Autor: jachguate

Entrepreneur, traveler and IT enthusiast passionate about Delphi, databases and networks.

5 opiniones en “El programador «copiar y pegar»”

  1. Este articulo me parece muy importante para la comunidad delphi, ya que estoy buscando ejemplos de como elaborar un mejor codigo, me gustaria que tuvieras un ejemplo completo de mantenimiento de el archivo , donde tambien se incluyera la parte de validacion
    Graciaws

  2. Es algo muy típico entre los programadores, lo que no lo es, es analizar el código solo se busca que funcione para después “una revisión” muy buen articulo

  3. Me parece que estoy se ve mucho en sitios como es.stackoverflow.com, donde el autor de la pregunta dice que el código no funciona o no hace lo que quiere. En el mejor de los casos pone el enlace a la fuente del código. También me parece que es algo común en los usuarios finales que programan una macro, función de hoja de cálculo personalizada. Me parece que sería interesante revisitar el tema en conjunto con la comunidad de es.stackoverflow.com.

Deja un comentario