Mejores paquetes

Qué es buen código

La única forma de escribir buen código es escribir primero toneladas de código de mierda. Sentir vergüenza por el código malo te impide llegar al código bueno.

Traducción de una entrada en Mastodon de Hadley Wickham

Taller de hoy

Presentaré una colección de cosas muy útiles que he aprendido en los últimos años.

Después de cada sección haré un resumen y te pediré que comentes.

Después, elige una cosa para mejorar en tu paquete.

Interfaz

Mensajes agradables

Conoce a los paquete cli

variable <- 42
cli::cli_alert_info("Set {.field parameter} to {.val {variable}}")
#>  Set parameter to 42

Viñeta para migrar de las funciones usethis::ui a cli

Viñeta sobre “clases” de formatos

Bonitos mensajes

¿Cómo controlar la verbosidad?

cli_alert_info <- function(...) {
  if (!getOption("usethis.quiet", default = FALSE)) {
    cli::cli_alert_info(...)
  }
}

Bonitos mensajes

Para leer más: https://ropensci.org/blog/2024/02/06/verbosity-control-packages/

🧰 ¿Hay mensajes en tu paquete que podrías mejorar?

Mensajes de error

cli::cli_abort(
  c(
    "Can't find good error message.",
    i = "Read the tidyverse style guide."
  )
)
#> Error:
#> ! Can't find good error message.
#>  Read the tidyverse style guide.

Mensajes de error

🧰 Revisa los mensajes de error de tu paquete (busca stop() y equivalentes). ¿Podrían mejorarse algunos de ellos aplicando la guía de la tidyverse?

Comprobación de argumentos

  • Tipo de argumento del documento, por defecto.

  • Comprueba los argumentos. rlang::arg_match() por ejemplo.

Más información: Comprobar las entradas de tus funciones en R por Hugo Gruson , Sam Abbott , Carl Pearson.

Comprobación de argumentos

🧰 ¿Tu paquete documenta y valida los argumentos? Mejora esto en una sola función o más.

Interfaz 🎤 stop() 🎤

  • Bonitos mensajes con {cli}.
  • Mensajes de error con {cli}, guía de estilo de la tidyverse.
  • Comprobación de argumentos con rlang, entrada del blog R-hub.

Por favor, publícalo en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala reciente con estas herramientas?

Menos código o menos dolores de cabeza

Sopesa tus dependencias

¿Esta dependencia provoca alegría? 😉

  • Una dependencia es un código que otra persona ha elaborado y probado cuidadosamente.
  • Una dependencia es un punto de fallo.

Más información: Dependencias: Mentalidad y antecedentes en el libro R Packages de Hadley Wickham y Jenny Bryan.

Sopesa tus dependencias

En la Guía de desarrollo de rOpenSci

  • curl, httr2, crul, httr. No RCurl. Para un nuevo paquete, httr2 en vez de httr.

  • jsonlite. No rjson ni RJSONIO.

  • xml2. No XML

  • sf, suites espaciales desarrolladas por las comunidades r-spatial y rspatial. No sp, rgdal, maptools, rgeos.

Sopesa tus dependencias

🧰 ¿Hay dependencias que podrías añadir, sustituir o eliminar en tu paquete?

¿Menos código? Más allá del uso de dependencias

Feature creep: “ampliación o adición excesiva y continua de nuevas funciones en un producto” https://en.wikipedia.org/wiki/Feature_creep

Está bien dividir el paquete.

Está bien decir no a las peticiones de funciones. Ejemplo

Menos código

🧰 ¿Hay peticiones de funciones a las que te gustaría decir que no? Guardar respuesta como Respuesta GitHub?

Menos código 🎤 stop() 🎤

  • Elegir dependencias.
  • Dependencias a evitar.
  • Definir el ámbito del paquete.

Por favor, publícalo en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala reciente con estas herramientas?

Código

Código lo más simple posible: “early return”

👀

do_this <- function() {
  if (!is_that_present()) {
    return(NULL)
  } else {
    # more code
    return(blip)
  }
}

Código lo más simple posible: “early return”

do_this <- function() {
  if (!is_that_present()) {
    return(NULL)
  }
  # more code

  blip
}

Código lo más sencillo posible: switch()

👀

if (type == "mean") {
  mean(x)
} else if (type == "median") {
  median(x)
} else if (type == "trimmed") {
  mean(x, trim = .1)
}

Código lo más sencillo posible: switch()

switch(
  type,
  mean = mean(x),
  median = median(x),
  trimmed = mean(x, trim = .1)
)

Código lo más simple posible: lógica

Para leer más: El código huele y se siente por Jenny Bryan

🧰 Examina la lógica de una o varias funciones. ¿Podrías simplificarla con retornos anticipados, funciones auxiliares? ¿Cambiar la ordén de las condiciones de ifelse?

Menos dolores de cabeza: lintr+flir

lintr encuentra problemas en código.

flir también, puede reparar algunos.

Para leer más: Cómo uso lintr en mi blog.

🧰 Intenta mejorar tu paquete con flir y/o lintr

Estética del código

Algunos de ellos sólo son relevantes si ves código.

  • ¿Usar alineación?
  • Utiliza párrafos
  • Utiliza comentarios de “cabecera” para la navegación.

Alineación de códigos

  • Alinea los argumentos en las definiciones de función.

  • ¿Más alineación vertical? No soy sensible a ello 😇

Párrafos

Un párrafo = una idea (¡también sirve para escribir prosa!).

El espacio vertical es costoso (¿qué cabe en la pantalla?)

head <- collect_metadata(website)
head_string <- stringify(head)

body <- create_content(website)
body_string <- stringify(body)

Comentarios en la cabecera

En RStudio IDE o Positron. En cualquier caso, es bueno para indicar la estructura de alto nivel dentro de un script.

# Header level 1 ----
more
code

## Header level 2 ----
more
code

Estética del código

🧰 Abre uno o varios scripts, ¿puedes mejorar la estética? ¿Usar Air en todo el paquete?

Menos comentarios / código autoexplicativo

Los comentarios son como pequeñas alertas. ¡No generes fatiga!

Los comentarios que repiten el código quedan desfasados.

Menos comentarios / código autoexplicativo

# use only non empty strings
if (!is.na(x) && nzchar(x)) {
  use_string(x)
}

Menos comentarios / código autoexplicativo

is_non_empty_string <- function(x) {
  !is.na(x) && nzchar(x)
}

if (is_non_empty_string(x)) {
  use_string(x)
}

Menos comentarios / código autoexplicativo

Más información: https://blog.r-hub.io/2023/01/26/code-comments-self-explaining-code/

🧰 ¿Existen posibilidades de hacer menos comentarios (¡o más comentarios!) en algunos de tus guiones?

Código 🎤 stop() 🎤

  • “Early return”.
  • Estética del código.
  • Menos comentarios/código autoexplicativo.

Por favor, publica en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala reciente con estas herramientas?

Código de tests

HUMEDO / SECO

“DAMP (descriptive and meaningful phrases)”

“DRY (don’t repeat yourself)”

¡Un intercambio!

Código de tests: diferente de otro código

El código está cubierto por el código de tests, ¡así que podemos asumir más riesgos!

Tests ideales

Ejemplo: {swamp}

Exploremos https://github.com/maelle/swamp

🧰 ¿Algunas de tus tests tienen código de nivel superior? ¿Puedes crear archivos y funciones de ayuda, y repetir la creación de objetos en cada prueba?

Mocking

Mi código


is_internet_down <- function() {
  !curl::has_internet()
}

my_complicated_code <- function() {
  if (is_internet_down()) {
    message("No internet! Le sigh")
  }
  # blablablabla
}

¿Cómo comprobar el mensaje?

Mocking

En el test,


test_that("my_complicated_code() notes the absence of internet", {
  local_mocked_bindings(is_internet_down = function(...) TRUE)
  expect_message(my_complicated_code(), "No internet")
})

Mocking

Para leer más: https://www.tidyverse.org/blog/2023/10/testthat-3-2-0/#mocking

Mocking

🧰 ¿tienes una situación de este tipo para probar?

Tests 🎤 stop() 🎤

  • DAMP/DRY
  • Código de prueba frente a código
  • Pruebas ideales (autónomas, pueden ejecutarse interactivamente, sin fugas)
  • Simulación

Por favor, publica en el chat

  • ¡Algo que te haya parecido interesante!
  • ¡Algo con lo que no estabas de acuerdo!
  • ¿Una experiencia buena/mala reciente con estas herramientas?

Elige tu propia aventura

…¡con tu propio paquete! En salas de descanso.

Nos reuniremos en XX minutos como grupo para debatir.

De vuelta de la aventura

¿Comentarios? ¿Preguntas?

Muchas gracias.

Nos vemos en el #package-maintenance canal? 😉