| name | r-package-development |
| description | R package development guide covering dependencies, API design, testing, and documentation. Use when mentions "R package", "pacote R", "pacote r", "devtools", "usethis", "roxygen2", "testthat", "pkgdown", "criar pacote", "create package", "desenvolver pacote", "develop package", "package development", "desenvolvimento de pacote", "DESCRIPTION", "NAMESPACE", "CRAN submission", "submissão CRAN", "submissão ao CRAN", "package documentation", "documentação de pacote", "package structure", "estrutura de pacote", "vignette", "vinheta", "vignet", "R CMD check", "check package", "verificar pacote", "build package", "construir pacote", "criar um pacote R", "make an R package", "building R packages", "construindo pacotes R", or developing R packages. ONLY R packages - do NOT activate for Python packages, npm packages, pip, setuptools, or JavaScript packages. |
| version | 1.1.0 |
| user-invocable | false |
| allowed-tools | Read, Grep, Glob |
R Package Development Decision Guide
Dependencies, API design, testing, documentation, and best practices for R packages
Dependency Strategy
When to Add Dependencies vs Base R
str_detect(x, "pattern")
length(x) > 0
parse_dates(x)
x + 1
Tidyverse Dependency Guidelines
dplyr
purrr
stringr
tidyr
lubridate
forcats
readr
ggplot2
tidyverse
shiny
Dependency Specification in DESCRIPTION
# Strong dependencies (required)
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0)
# Suggested dependencies (optional)
Suggests:
testthat (>= 3.0.0),
knitr,
rmarkdown
# Enhanced functionality (optional but loaded if available)
Enhances:
data.table
API Design Patterns
Function Design Strategy
my_summarise <- function(.data, ..., .by = NULL) {
}
my_select <- function(.data, cols) {
.data |> select({{ cols }})
}
my_mutate <- function(.data, ..., .by = NULL) {
.data |> mutate(..., .by = {{ .by }})
}
my_function <- function(.data) {
result |> tibble::as_tibble()
}
Input Validation Strategy
user_function <- function(x, threshold = 0.5) {
if (!is.numeric(x)) stop("x must be numeric")
if (!is.numeric(threshold) || length(threshold) != 1) {
stop("threshold must be a single number")
}
}
.internal_function <- function(x, threshold) {
}
safe_function <- function(x, y) {
x <- vec_cast(x, double())
y <- vec_cast(y, double())
}
Error Handling Patterns
if (length(x) == 0) {
cli::cli_abort(
"Input {.arg x} cannot be empty.",
"i" = "Provide a non-empty vector."
)
}
validate_input <- function(x, call = caller_env()) {
if (!is.numeric(x)) {
cli::cli_abort("Input must be numeric", call = call)
}
}
Error Classes
my_error <- function(message, ..., call = caller_env()) {
cli::cli_abort(
message,
...,
class = "my_package_error",
call = call
)
}
validation_error <- function(message, ..., call = caller_env()) {
cli::cli_abort(
message,
...,
class = c("validation_error", "my_package_error"),
call = call
)
}
When to Create Internal vs Exported Functions
Export Function When
process_data <- function(.data, ...) {
}
Keep Function Internal When
.validate_input <- function(x, y) {
}
.compute_metrics <- function(data) { ... }
Testing and Documentation Strategy
Testing Levels
test_that("function handles edge cases", {
expect_equal(my_func(c()), expected_empty_result)
expect_error(my_func(NULL), class = "my_error_class")
})
test_that("pipeline works end-to-end", {
result <- data |>
step1() |>
step2() |>
step3()
expect_s3_class(result, "expected_class")
})
test_that("function properties hold", {
})
Test File Organization
tests/
testthat/
test-validation.R # Input validation tests
test-processing.R # Core processing tests
test-output.R # Output format tests
test-integration.R # End-to-end tests
helper-fixtures.R # Shared test fixtures
testthat.R # Test runner
Snapshot Testing
test_that("summary output is correct", {
expect_snapshot(summary(my_object))
})
test_that("errors are informative",
expect_snapshot(my_function(bad_input), error = TRUE)
})
Documentation Priorities
roxygen2 Documentation
process_data <- function(data, vars, .by = NULL) {
}
Package Structure
Recommended Directory Layout
mypackage/
DESCRIPTION
NAMESPACE
LICENSE
README.md
R/
utils.R # Internal utilities
validation.R # Input validation
core.R # Core functionality
methods.R # S3/S7 methods
zzz.R # .onLoad, .onAttach
man/ # Generated by roxygen2
tests/
testthat/
testthat.R
vignettes/
getting-started.Rmd
inst/
extdata/ # Example data files
data/ # Package data (lazy-loaded)
data-raw/ # Scripts to create package data
DESCRIPTION Best Practices
Package: mypackage
Title: What The Package Does (One Line)
Version: 0.1.0
Authors@R:
person("First", "Last", email = "email@example.com",
role = c("aut", "cre"))
Description: A longer description that spans multiple lines.
Use four spaces for continuation lines.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Imports:
dplyr (>= 1.1.0),
rlang (>= 1.0.0)
Suggests:
testthat (>= 3.0.0)
Config/testthat/edition: 3
Release Checklist
devtools::check()
devtools::test()
devtools::document()
urlchecker::url_check()
spelling::spell_check_package()
usethis::use_version("minor")
devtools::check(remote = TRUE, manual = TRUE)
Common Package Development Mistakes
library(dplyr)
dplyr::filter(data, x > 0)
options(my_option = TRUE)
old_opts <- options(my_option = TRUE)
on.exit(options(old_opts), add = TRUE)
read.csv("/home/user/data.csv")
system.file("extdata", "data.csv", package = "mypackage")