Páginas

Mostrando las entradas con la etiqueta buenas prácticas. Mostrar todas las entradas
Mostrando las entradas con la etiqueta buenas prácticas. Mostrar todas las entradas

20150608

Una revisión de código sin tus lentes



Ésta entrada es una traducción y adaptación de la titulada «Code Review Without Your Glasses», escrita originalmente por Robert Heaton el 20 de Junio de 2014.


Imagina que eres parte de un equipo de desarrollo con una sabiduría más allá de lo normal y han destinado un día completo sólo para revisiones de código. Sin embargo, después de las dos primeras horas, te das cuenta que has olvidado tus lentes y has estado viendo fijamente coloridos difuminados toda la mañana. ¿Qué puedes hacer?

La respuesta correcta es ir de regreso a casa y obtener tus lentes, ya que se encuentra a sólo 10 minutos caminando y es un día agradable. Pero asume que, en cuanto dejabas tu casa ésta mañana, descubriste que un enjambre de avispas particularmente aguerridas recién había completado la construcción de un nido en el guardarropa donde guardas tus lentes y no se observaban deseosas de ser molestadas. ¿ENTONCES QUÉ?

Ahora la respuesta correcta es, por supuesto, evitar la pena de pretender que estás usando lentes de contacto y recordar que puedes decir mucho de un archivo sin tener que leer su contenido.

Imagen 1
Imagen 1

Podemos acordar que las responsabilidades deben estar absolutamente separadas. Y, por supuesto, cada clase sólo debe ser responsable de hacer una cosa. Pero, éste objeto CreadorDeUsuarios que has construido aquí probablemente está llevando las cosas demasiado lejos. Si esto es todo lo que el CreadorDeUsuarios tiene que hacer, entonces los Usuarios pueden crearse a sí mismos por ahora. De otra forma, lo que era antes un simple Usuario.new innecesariamente se vuelve una infernal pesadilla de  tener que usar grep durante la mitad del camino alrededor del mundo, a través de múltiples pequeños archivos en el momento que quieras cambiar algo o entender que foo está pasando.

imagen 2
Imagen 2

Observando éste largo método disfrazado cómo clase, se ve técnicamente todo muy DRY y no hay nada que pueda ser refactorizado en el sentido literal de la palabra. Pero algo me dice que no usaste pruebas unitarias. Y mientras que, si me das una tarde y algo de café fuerte, puedo trabajar a través de ese bloque de 20 líneas de en medio que es usado para decidir a que usuarios necesitamos enviarles correos, humildemente te sugeriría que metieras en medio un def usuarios_a_enviar_correo para no tener que hacerlo.

imagen 3
Imagen 3

Bien, así que los métodos en ésta clase son mucho más cortos y eso es probablemente un progreso. Sin embargo, haces algo bueno en exceso. Mientras que al intérprete de Ruby no le importan tus continuos saltos entre métodos, pero a la mayoría de los intérpretes humanos le importa. Soy tan feliz cómo cualquiera de deslizarme alrededor del archivo un poco, pero cuando tengo que comenzar a escribir un registro del stack en mi brazo para recordar desde donde llegué, es probablemente tiempo de agrupar y consolidar algunos de esos métodos.

imagen 4
Imagen 4

Veo que estás dispuesto a hacer que ésta clase ocupe exactamente el namespace correcto. Muy bien, crear namespaces es bueno. Pero para el momento en que llegaste al sexto nivel, parece que estás intentando meter mucho en un espacio pequeño. Considera bien dejar de dividir namespaces de forma tan fina (sí, puedo ver que esas dos clases Ayudante podrían tener su propio namespace Ayudante, pero ¿están realmente dañando algo si se colocan en el siguiente nivel arriba?), o desacopla y divide parte del código en un namespace completamente diferente.

Imagen 5
Imagen 5

Explicaciones en comentarios, ¡muy bien! Código que requiere se acompañado de un ensayo de varios capítulos, muy mal.

Imagen 6
Imagen 6
Observa detenidamente al segundo método hacia abajo. Si el método necesita 8 argumentos para saber que tiene qué hacer y cómo hacerlo, ese método está muy sobrecargado. Distribuye la carga y quita algo del peso de sus hombros con una refactorización temprana. Divídelo en dos (o más), o tal vez tiene más sentido pasar algunos de esos argumentos a la instancia en su inicializador. ¿Podrías tú manejar 8 argumentos de forma simultánea? Entonces no esperes que tus métodos lo hagan.

- o -

Así es el cómo hacer una revisión de código cuando has olvidado tus lentes o has estado mirando fijamente el sol por más tiempo del que los médicos recomiendan. Si fuera mejor programando tal vez hubiera podido ofrecer algunos ejemplos mucho más sutiles. Por otro lado, podría argumentar (y tengo la completa intención de hacerlo) que la trivialidad puede ser interesante aveces. Por más claro, simple y bien formado que pueda ser tu diseño, no sirve de nada si lo construyes con lodo y palillos de madera.

20150520

Buenas prácticas para revisiones de código




Ésta es una traducción y adaptación del artículo titulado «Code Review Best Practices», escrito y publicado originalmente por Kenvin London el 5 de Mayo de 2015.


En Wiredrive realizamos bastantes revisiones de código. Nunca había hecho una antes de comenzar a trabajar aquí, así que fue una nueva experiencia. Creo que es una buena idea cristalizar algunas de las cosas en las que me enfoco cuando hago revisiones de código, hablando sobre la mejor forma que he encontrado para acercarme a ellas.

Rápidamente, una revisión de código es una discusión entre dos o más desarrolladores a cerca de cambios en el código para solucionar un problema o agregar una nueva característica.

¿Qué observar al hacer una revisión?


Arquitectura / Diseño


  • Principio de la responsabilidad única: La idea de que una clase debe tener una y sólo una responsabilidad, es más difícil de lo que uno podría esperar. Usualmente también aplico éste principio a los métodos: si se tiene que usar una conjunción «y» para terminar de describir lo que un método es capaz de hacer, puede tratarse de un mal nivel de abstracción.
  • Principio abierto/cerrado: Si el lenguaje usado es orientado a objetos, ¿los objetos pueden ser extendidos pero están protegidos para no ser modificados? ¿Qué pasa si necesitamos agregar un atributo, función o pieza de código?
  • Duplicación de código: Prefiero seguir la regla de los tres  strikes. Si el código está replicado una vez, usualmente está bien aunque no me agrade. Si está replicado más de una vez, debe hacerse una refactorización para separar las funciones comunes.
  • Prueba de los ojos entornados: Si entorno los ojos, ¿la forma de éste código se ve idéntica a otras? ¿Hay patrones que pueden indicar otros problemas en la estructura del código?
  • Código en un mejor estado: Si estoy cambiando un área desordenada del código, es tentador agregar un par de líneas y dejarlo así. Recomiendo ir un paso más allá y organizar el código para dejarlo mejor a cómo lo encontré.
  • Errores potenciales: ¿Hay errores de diferencia por un paso? ¿Los ciclos terminan en la forma que se espera? ¿A caso terminarán?
  • Eficiencia: Si se ha implementado un algoritmo en el código, ¿se está usando una implementación eficiente? Por ejemplo, iterar sobre una lista de llaves en un diccionario es una forma ineficiente de buscar el valor deseado.


Estilo


  • Nombres de los métodos: Darle nombre a las cosas es uno de los problemas difíciles en ciencias de la computación. Si un método es llamado `obtener_nombre_del_mensaje_en_la_fila` y en realidad está hace algo completamente diferente, como sanear el HTML de una entrada, entonces no es un nombre descriptivo para el método y causará confusión.
  • Nombres de variables: `foo` y `bar` probablemente no son nombres útiles para estructuras de datos. De forma similar, `e` no es tan útil comparado con `excepcion`. Se tan explicito como necesites (dependiendo del lenguaje). Nombres de variable expresivos hacen más fácil entender el código cuando se tiene que volver a leer.
  • Longitud de una función: Mi regla del pulgar es que una función debe tener menos de 20 líneas, aproximadamente. Si veo un método de más de 50 líneas, siento que lo mejor es dividirlo en partes más pequeñas.
  • Longitud de una clase: Creo que las clases deben tener menos de 300 líneas en total, e idealmente menos de 100. Es usual que clases muy largas puedan ser separadas en objetos diferentes, lo que hace más fácil entender el propósito de cada clase.
  • Longitud de un archivo: Es algo variable, dependiendo del lenguaje, pero para lenguajes dinámicos como Python, creo que alrededor de 1,000 líneas de código es lo máximo que deberíamos tener en un archivo. En caso de ser más grande, es una buena señal de que el contenido debe ser dividido en archivos más pequeños y enfocados. Mientras más grande sea un archivo, es más difícil encontrar información.
  • Documentación en comentarios: Para métodos complejos, los que su funcionamiento no es obvio o aquellos con una larga lista de argumentos, ¿hay un comentario (docstring) explicando la función de cada argumento?
  • Código comentado: Es una buena idea remover código que ya no se utiliza y que se ha dejado cómo comentarios.
  • Número de argumentos en un método: Para métodos y funciones, ¿tienen 3 o menos argumentos? Más de 3 probablemente significa que pueden ser agrupados de otra forma.
  • Facilidad de lectura: ¿El código es fácil de entender? ¿Tienen que detenerte frecuentemente durante la revisión para descifrarlo?


Pruebas


  • Cobertura de las pruebas: Me gusta ver pruebas para nuevas características. ¿La pruebas indican que se pensó en ellas antes de escribirlas? ¿Cubren los casos en que habría fallos? ¿Qué tan frágiles son? ¿Qué tan amplias son? ¿Son lentas?
  • Pruebas en el nivel correcto: Cuando reviso las pruebas también me aseguro que estén probando el nivel correcto. En otras palabras, ¿están al nivel suficientemente bajo para corroborar la funcionalidad esperada? El desarrollador Gary Bernhardt (Destroy All Software) recomienda una relación de 95% de pruebas unitarias con un 5% de pruebas de integración. Me he dado cuenta que en proyectos con Django, es fácil crear pruebas de alto nivel por accidente, terminando con un conjunto de pruebas lento, así que es importante mantenerse alerta.
  • Número de objetos simulados: Crear objetos simulados (mocks, dobles) es genial, pero simular todo no lo es. Como regla del pulgar, si hay más de tres objetos simulados en una prueba, el código debe ser reestructurado. Puede ser que la prueba sea demasiado amplia o que la función que evalúa sea demasiado grande. Tal vez no necesite una prueba a nivel unitario y con una prueba de integración sea suficiente. De cualquier forma, es algo que se debe discutir.
  • Cobertura de los requerimientos: Usualmente, como la parte final de una revisión, observo los requerimientos de la historia, tarea o bug que dio origen al código. Si no cubre alguno de los requerimientos, es mejor no aceptar el código antes de que sea enviado a QA.


Revisa tu propio código


Antes de enviar mi propio código, hago un `git add` de los archivos y/o directorios afectados, y luego ejecuto un `git diff --staged` para examinar los cambios que aún no he integrado al repositorio. Usualmente busco cosas como:

  • ¿Dejé un comentario o TODO en los cambios?
  • ¿Los nombres de las variables tienen sentido?
  • ...y alguna violación a lo discutido en puntos anteriores.

Quiero estar seguro de que pasaría mi propia revisión de código antes de pedirle a otras personas que lo revisen. También, duele menos recibir comentarios de uno mismo que de otros.

Manejando las revisiones de otros


He encontrado que la parte humana de la revisión de código ofrece tantos retos como la parte técnica. Aún sigo aprendiendo como manejar ésta parte. Aquí presento algunas aproximaciones que han funcionado cuando discuto sobre código:


  • Haz preguntas: ¿Cómo funciona ese método? ¿Si éste requerimiento cambia, qué más tendría que cambiar? ¿Cómo podemos hacer esto más fácil de mantener?
  • Refuerza y felicita las buenas prácticas: Una de las partes más importantes de la revisión de código es recompensar a los desarrolladores por su crecimiento y esfuerzo. Pocas cosas se sienten mejor que recibir el reconocimiento de un compañero. Yo trato de ofrecer la mayor cantidad posible de comentarios positivos.
  • Obtén más detalles en persona: En ocasiones, un cambio en la arquitectura puede ser muy grande y es más fácil discutirlo en persona que a través de comentarios. De forma similar, si la discusión de un punto genera comentarios de ida y vuelta, con frecuencia prefiero retomarlo en persona para finalizar la discusión.
  • Explica el razonamiento: He encontrado que es mejor preguntar si hay alguna alternativa superior y justificar porque es importante arreglarlo. Algunas veces puede haber la sensación de que los cambios sugeridos son superficiales si no hay algún contexto o explicación.
  • Mantén el enfoque en el código: Es fácil tomar de forma personal los comentarios en las revisiones de código, especialmente si nos enorgullecemos de nuestro propio trabajo. No hay que olvidar que las discusiones son acerca de cómo mejorar la calidad del código y no son específicos a los desarrolladores. Esto disminuye la resistencia.
  • Haz notar los cambios importantes: Es común que ofrezca varias sugerencias, pero no es necesario actuar en todas. Aclarar si es importante arreglar un punto antes de considerarlo terminado es útil tanto para quien revisa como para quien somete el código a revisión. Hace los resultados de la revisión claros y de valor práctico.


En cuanto a la mentalidad


Como desarrolladores, somos responsables de crear código funcional y fácil de mantener. Puede ser fácil posponer la segunda parte debido a las presiones para entregar el código. La refactorización no modifica la funcionalidad por diseño, así que no dejes que los cambios sugeridos te desanimen. Mejorar la mantenibilidad del código puede ser tan importante cómo arreglar la línea de código que causa un bug.

Además, recuerda mantener una mente abierta durante las revisiones. Creo que es algo con lo que la mayoría tenemos problemas. Uno puede ponerse a la defensiva porque puede tomarlo personal cuando alguien te dice que tu código podría ser mejor.

Si la persona que revisa el código hace una sugerencia y no tengo una respuesta clara de porque esa sugerencia no debería ser implementada, usualmente hago el cambio. Si me hacen una pregunta acerca de una línea de código, quiere decir que podría confundir a otros desarrolladores en el futuro. Otro beneficio, es que el hacer los cambios puede revelar otros problemas de arquitectura u otros bugs.

(Gracias a Zach Schipono por recomendar que se agregará ésta sección.)

Atención a los cambios sugeridos


Nosotros usualmente dejamos comentarios en cada línea de código con nuestro pensamiento sobre ellas. Yo observo las notas de la revisión en el stash, y al mismo tiempo, jalo el código para hacer los cambios sugeridos. Encontré que con frecuencia olvido los puntos que tengo que atender si no lo hago de inmediato.

Referencias adicionales


Hay bastantes libros en el arte de crear código limpio. He leído menos de los que me gustaría (aunque estoy trabajando en cambiar eso). Estos son algunos de los que están en mi lista:


Son un gran fanático de las charlas, así que aquí algunas que relacionadas que considero útiles y que recordé mientras escribía ésta entrada:

  • All the Small Things por Sandy Metz: Cubre bien el tema, particularmente desde la perspectiva de escribir código limpio y reutilizable. (Charla en inglés.)
  • How to Design a Good API and Why it Matters de Joshua Bloch: Una API en el sentido y con el significado de ser la forma en que una aplicación es construida y cómo interactuamos con ella. Varios de los puntos del vídeo hablan de temas similares a los discutidos en éste documento. (Charla en inglés.)


Discusión en Hacker News.

20141004

Cuatro razones para mantener un diario de desarrollo

Usar 60 segundos al día para escribir algunas notas pueden producir impresionantes beneficios profesionales.

* Difícilmente olvidaras como arreglaste cierto error

¿Alguna vez has encontrado un error y pensado «Sé qué he visto esto antes pero, ¿cómo lo resolví?». Con un diario en el que puedas buscar, esa experiencia desaparecerá.

* Aprenderás cuáles patrones de diseño funcionan bien cuando el código cambia.

La verdadera prueba de un diseño no es cuando lo escribes por primera vez, sino cuando necesitas modificarlo tiempo después.

Si tomas nota de cuando tus elecciones de diseño funcionan particularmente bien o si funcionan de manera pobre, pronto construirás un catálogo de técnicas que puedes adoptar y otras que debes evitar. Después de que tu tercer intento por implementar el patrón Observador en la aplicación foo sigue confundiendo a nuevos desarrolladores que se unen al proyecto, aprenderás a meditar antes de usarlo en un futuro.

* Una lista de tus logros es bastante útil ante una revisión

Enviar a tu jefe un correo, una semana antes de una revisión, listando tus mayores logros en los pasados N meses, es una acción recomendable.

* Te darás cuenta de cuando necesitas cambiar de empleo

Como seres humanos, tendemos a tener algunos días buenos y algunos malos. Debido a esto, puede ser difícil detectar tendencias en tu estado de ánimo. Escribir con consistencia puede ayudarte a detectar cuando los días malos son más que los buenos. En ese caso, tal vez sea mejor comenzar a buscar.


Cómo construir el hábito de forma exitosa


Comenzar un diario de desarrollo no es difícil, pero mantener uno lo es. Lo principal para construir el hábito no es la excitación inicial, es la consistencia. El mayor enemigo de la consistencia es el olvido.

Si quieres intentar el construir éste hábito, sería bueno que configures un recordatorio diario durante las primeras semanas. Cuando apagues la alarma, lanza tu editor y escribe una entrada.

Otra opción es una app como Trailmix, que te envía correos cada día, preguntando en lo que has estado trabajando, a los cuales puedes responder directamente para crear automáticamente una entrada en un diario. Además, incluye una entrada anterior en el correo que recibes, ayudando a mantener fresco lo que has aprendido.

También puedes optar por usar la plataforma para blogs Jekyll en conjunto con GitHub Pages, pero requiere de más configuración.


Recursos adicionales



Ésta entrada es una traducción y adaptación de «Trailmix - 4 Reasons To Keep A Development Journal», recuperada el 4 de Octubre de 2014. El contenido se incluye en ésta bitácora con fines informativos y de divulgación.