# R Development

> Expert guidance for R package development following best practices for devtools, testthat, roxygen2, and R ecosystem tools

- Skill: `seabbs-skills/r-development` (Agent Skill)
- Install (CLI): `npx skillmds add seabbs-skills/r-development`
- Raw SKILL.md: https://api.skillmd.com/api/skills/seabbs-skills/r-development/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: seabbs (https://skillmd.com/u/seabbs-skills)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/seabbs-skills/r-development

---


# R Package Development

Use this skill when working with R packages to ensure proper development workflows, testing patterns, and documentation standards.

## Development Workflow

### R Environment

- Use radian with R as an alias so call R scripts using Rscript
- Do not run devtools::test with grep; read the whole output

### Package Building and Management

```r
# Load package for interactive development
devtools::load_all()

# Update documentation (REQUIRED before committing R changes)
devtools::document()

# Run all tests
devtools::test()

# Run specific test file
testthat::test_file("tests/testthat/test-filename.R")

# Run tests matching a pattern
devtools::test(filter = "pattern")
testthat::test_local(filter = "pattern")

# Check package (R CMD check)
devtools::check()

# Build package
devtools::build()

# Install package locally
devtools::install()

# Check with vignettes built
devtools::check(build_args = c("--compact-vignettes=both"))
```

### Code Quality and Style

```r
# Lint package (configuration in .lintr)
lintr::lint_package()

# Lint specific file
lintr::lint("R/filename.R")

# Style code (pre-commit hook typically uses tidyverse style)
styler::style_pkg()

# Check test coverage
covr::package_coverage()
```

### Documentation Building

```r
# Build vignettes
devtools::build_vignettes()

# Build specific vignette
rmarkdown::render("vignettes/name.Rmd")

# Build pkgdown site locally
pkgdown::build_site()
```

## Testing Best Practices

### testthat Patterns

**Preferred expectations:**

- `expect_identical()` > `expect_equal()` (when exact match expected)
- Multiple `expect_true()` calls > stacking conditions with `&&`
- `expect_s3_class()` > `expect_true(inherits(...))`
- Use specific `expect_*` functions:
  - `expect_lt()`, `expect_gt()`, `expect_lte()`, `expect_gte()`
  - `expect_length()`
  - `expect_named()`
  - `expect_type()`

**Examples:**

```r
# Good
expect_identical(result, expected)
expect_s3_class(obj, "data.frame")
expect_lt(value, 10)
expect_true(condition1)
expect_true(condition2)

# Avoid
expect_equal(result, expected)  # when identical match is needed
expect_true(inherits(obj, "data.frame"))
expect_true(value < 10)
expect_true(condition1 && condition2)
```

### Test Organisation

- Use testthat edition 3
- Test files named `test-{component}.R`
- Helper files in `tests/testthat/helper-{name}.R`
- Setup files in `tests/testthat/setup.R` for shared fixtures
- Custom expectations in `tests/testthat/helper-expectations.R`

### Conditional Testing

```r
# Skip tests on CRAN
testthat::skip_on_cran()

# Skip if not on CI
testthat::skip_if_not(on_ci())

# Skip if package not available
testthat::skip_if_not_installed("package")
```

## Documentation Standards

### roxygen2 Best Practices

**Avoid duplication with `@inheritParams`:**

```r
#' @param x Input data
#' @param ... Additional arguments
my_function <- function(x, ...) {}

#' @inheritParams my_function
#' @param y Another parameter
wrapper_function <- function(x, y, ...) {}
```

**Documentation structure:**

- One sentence per line in descriptions
- Max 80 characters per line
- Use `@family` tags for related functions
- Use `@examples` or `@examplesIf` for examples
- UK English spelling

**Example documentation:**

```r
#' Process input data
#'
#' This function processes the input data according to specified parameters.
#' It returns a processed data frame with additional columns.
#'
#' @param data A data.frame containing the input data
#' @param method Character string specifying the processing method
#'
#' @return A data.frame with processed results
#'
#' @family preprocessing
#'
#' @examples
#' \dontrun{
#' result <- process_data(my_data, method = "standard")
#' }
#'
#' @export
process_data <- function(data, method = "standard") {
  # implementation
}
```

## Code Style Guidelines

### Naming Conventions

- **Internal functions**: Prefix with `.`

  ```r
  .internal_helper <- function() {}
  ```

- **Exported functions**: Use snake_case

  ```r
  public_function <- function() {}
  ```

### Formatting

- Max 80 characters per line
- No trailing whitespace
- No spurious blank lines
- Use tidyverse style guide
- Set up pre-commit hooks for automatic formatting

### Pre-commit Hooks

Typical `.pre-commit-config.yaml` includes:

- `style-files`: Auto-format R code
- `lintr`: Lint R code
- `readme-rmd-rendered`: Ensure README.md is up-to-date
- `parsable-R`: Check R syntax
- `deps-in-desc`: Check dependencies are in DESCRIPTION

```bash
# Install pre-commit
pip install pre-commit

# Install hooks
pre-commit install

# Run manually
pre-commit run --all-files
```

## Common Data Structures

### data.table Usage

Many R packages use `data.table` for performance:

- Functions often expect/return `data.table` objects
- Use `data.table::setDT()` or custom `coerce_dt()` to ensure input is data.table
- Set keys for efficient joins: `data.table::setkey(dt, col)`
- Use `:=` for in-place modification

### S3 Classes

- Check class with `inherits()` or `expect_s3_class()`
- Document S3 methods properly
- Export constructors, not internal class definitions

## Package Dependencies

### Managing Dependencies

```r
# Use specific package functions with ::
package::function()

# Add to DESCRIPTION Imports or Suggests
usethis::use_package("package_name")
usethis::use_package("package_name", type = "Suggests")
```

### Common R Package Ecosystem Tools

- **devtools**: Development workflow
- **testthat**: Testing framework
- **roxygen2**: Documentation generation
- **usethis**: Package setup automation
- **lintr**: Code linting
- **styler**: Code formatting
- **covr**: Test coverage
- **pkgdown**: Website generation

## When to Use This Skill

Activate this skill when:

- Developing R packages
- Writing R tests
- Documenting R functions
- Setting up R package infrastructure
- Running R package checks
- Working with devtools, testthat, or roxygen2

This skill provides R-specific development patterns.
Project-specific architecture and domain knowledge should remain in project CLAUDE.md files.

