r-lib / tidy-deprecate-function

Guide for deprecating R functions/arguments. Use when a user asks to deprecate a function or parameter, including adding lifecycle warnings, updating documentation, adding NEWS entries, and updating tests.

0 views
0 installs

Skill Content

---
name: tidy-deprecate-function
description: Guide for deprecating R functions/arguments. Use when a user asks to deprecate a function or parameter, including adding lifecycle warnings, updating documentation, adding NEWS entries, and updating tests.
---

# Deprecate functions and function arguments

Use this skill when deprecating functions or function parameters in this package.

## Overview

This skill guides you through the complete process of deprecating a function or parameter, ensuring all necessary changes are made consistently:

1. Add deprecation warning using `lifecycle::deprecate_warn()`.
2. Silence deprecation warnings in existing tests.
3. Add lifecycle badge to documentation.
4. Add bullet point to NEWS.md.
5. Create test for deprecation warning.

## Workflow

### Step 1: Determine deprecation version

Read the current version from DESCRIPTION and calculate the deprecation version:

- Current version format: `MAJOR.MINOR.PATCH.9000` (development).
- Deprecation version: Next minor release `MAJOR.(MINOR+1).0`.
- Example: If current version is `2.5.1.9000`, deprecation version is `2.6.0`.

### Step 2: Add `lifecycle::deprecate_warn()` call

Add the deprecation warning to the function:

```r
# For a deprecated function:
function_name <- function(...) {
  lifecycle::deprecate_warn("X.Y.0", "function_name()", "replacement_function()")
  # rest of function
}

# For a deprecated parameter:
function_name <- function(param1, deprecated_param = deprecated()) {
  if (lifecycle::is_present(deprecated_param)) {
    lifecycle::deprecate_warn("X.Y.0", "function_name(deprecated_param)")
  }
  # rest of function
}
```

Key points:

- First argument is the deprecation version string (e.g., "2.6.0").
- Second argument describes what is deprecated (e.g., "function_name(param)").
- Optional third argument suggests replacement.
- Use `lifecycle::is_present()` to check if a deprecated parameter was supplied.

### Step 3: Update tests

Find all existing tests that use the deprecated function or parameter and silence lifecycle warnings. Add at the beginning of test blocks that use the deprecated feature:

```r
test_that("existing test with deprecated feature", {
  withr::local_options(lifecycle_verbosity = "quiet")

  # existing test code
})
```

Then add a new test to verify the deprecation message in the appropriate test file (usually `tests/testthat/test-{name}.R`):

```r
test_that("function_name(deprecated_param) is deprecated", {
  expect_snapshot(. <- function_name(deprecated_param = value))
})
```

You'll need to supply any additional arguments to create a valid call.

Then run the tests and verify they pass.

### Step 4: Update documentation

For function deprecation, add to the description section:

```r
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' This function is deprecated. Please use [replacement_function()] instead.
```

If the documentation does not already contain `@description`, you will need to add it.

For argument deprecation, add to the appropriate `@param` tag:

```r
#' @param deprecated_param `r lifecycle::badge("deprecated")`
```

When deprecating a function or parameter in favor of a replacement, add old/new examples to the `@examples` section to help users migrate. These should relace all existing examples.

```r
#' @examples
#' # Old:
#' old_function(arg1, arg2)
#' # New:
#' replacement_function(arg1, arg2)
#'
#' # Old:
#' x <- "value"
#' old_function("prefix", x, "suffix")
#' # New:
#' replacement_function("prefix {x} suffix")
```

Key points:

- Use "# Old:" and "# New:" comments to clearly show the transition.
- Include 2-3 practical examples covering common use cases.
- Make examples runnable and self-contained.
- Show how the new syntax differs from the old.

Then re-document the package.

### Step 5: Add NEWS entry

Add a bullet point to the top of the "# packagename (development version)" section in NEWS.md:

```markdown
# packagename (development version)

* `function_name(parameter)` is deprecated and will be removed in a future
  version.
* `function_name()` is deprecated. Use `replacement_function()` instead.
```

Place the entry:

- In the lifecycle subsection if it exists, otherwise at the top level under development version.
- Include the replacement if known.
- Keep entries concise and actionable.

## Implementation checklist

When deprecating a function or parameter, ensure you:

- [ ] Read DESCRIPTION to determine deprecation version.
- [ ] Add `lifecycle::deprecate_warn()` call in the function.
- [ ] Add `withr::local_options(lifecycle_verbosity = "quiet")` to existing tests.
- [ ] Create new test for deprecation warning using `expect_snapshot()`.
- [ ] Run tests to verify everything works.
- [ ] Add lifecycle badge to roxygen documentation.
- [ ] Add migration examples to `@examples` section (for function deprecation).
- [ ] Run `devtools::document()` to update documentation.
- [ ] Add bullet point to NEWS.md.
- [ ] Run `air format .` to format code.