lifecycle
The lifecycle skill provides guidance for managing R package function and argument lifecycle stages using tidyverse conventions and the lifecycle package. Use it when setting up lifecycle infrastructure, deprecating or renaming functions and arguments, superseding functions, marking functions as experimental, understanding lifecycle stages (stable, experimental, deprecated, superseded), or writing deprecation helpers for complex scenarios.
git clone --depth 1 https://github.com/posit-dev/skills /tmp/lifecycle && cp -r /tmp/lifecycle/r-lib/lifecycle ~/.claude/skills/lifecycleSKILL.md
# R Package Lifecycle Management
Manage function and argument lifecycle using tidyverse conventions and the lifecycle package.
## Setup
Check if lifecycle is configured by looking for `lifecycle-*.svg` files in `man/figures/`.
If not configured, run:
```r
usethis::use_lifecycle()
```
This:
- Adds lifecycle to `Imports` in DESCRIPTION
- Adds `@importFrom lifecycle deprecated` to the package documentation file
- Copies badge SVGs to `man/figures/`
## Lifecycle Badges
Insert badges in roxygen2 documentation:
```r
#' @description
#' `r lifecycle::badge("experimental")`
#' `r lifecycle::badge("deprecated")`
#' `r lifecycle::badge("superseded")`
```
For arguments:
```r
#' @param old_arg `r lifecycle::badge("deprecated")` Use `new_arg` instead.
```
Only badge functions/arguments whose stage differs from the package's overall stage.
## Deprecating a Function
1. Add badge and explanation to `@description`:
```r
#' Do something
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `old_fun()` was deprecated in mypkg 1.0.0. Use [new_fun()] instead.
#' @keywords internal
```
2. Add `deprecate_warn()` as first line of function body:
```r
old_fun <- function(x) {
lifecycle::deprecate_warn("1.0.0", "old_fun()", "new_fun()")
new_fun(x)
}
```
3. Show migration in examples:
```r
#' @examples
#' old_fun(x)
#' # ->
#' new_fun(x)
```
## Deprecation Functions
| Function | When to Use |
| ------------------ | ------------------------------------------------------- |
| `deprecate_soft()` | First stage; warns only direct users and during tests |
| `deprecate_warn()` | Standard deprecation; warns once per 8 hours |
| `deprecate_stop()` | Final stage before removal; errors with helpful message |
**Deprecation workflow for major releases:**
1. Search `deprecate_stop()` - consider removing function entirely
2. Replace `deprecate_warn()` with `deprecate_stop()`
3. Replace `deprecate_soft()` with `deprecate_warn()`
## Renaming a Function
Move implementation to new name, call from old name with deprecation:
```r
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' `add_two()` was renamed to `number_add()` for API consistency.
#' @keywords internal
#' @export
add_two <- function(x, y) {
lifecycle::deprecate_warn("1.0.0", "add_two()", "number_add()")
number_add(x, y)
}
#' Add two numbers
#' @export
number_add <- function(x, y) {
x + y
}
```
## Deprecating an Argument
Use `deprecated()` as default value with `is_present()` check:
```r
#' @param path `r lifecycle::badge("deprecated")` Use `file` instead.
write_file <- function(x, file, path = deprecated()) {
if (lifecycle::is_present(path)) {
lifecycle::deprecate_warn("1.4.0", "write_file(path)", "write_file(file)")
file <- path
}
# ... rest of function
}
```
## Renaming an Argument
```r
add_two <- function(x, y, na_rm = TRUE, na.rm = deprecated()) {
if (lifecycle::is_present(na.rm)) {
lifecycle::deprecate_warn("1.0.0", "add_two(na.rm)", "add_two(na_rm)")
na_rm <- na.rm
}
sum(x, y, na.rm = na_rm)
}
```
## Superseding a Function
For functions with better alternatives that shouldn't be removed:
```r
#' Gather columns into key-value pairs
#'
#' @description
#' `r lifecycle::badge("superseded")`
#'
#' Development on `gather()` is complete. For new code, use [pivot_longer()].
#'
#' `df %>% gather("key", "value", x, y, z)` is equivalent to
#' `df %>% pivot_longer(c(x, y, z), names_to = "key", values_to = "value")`.
```
No warning needed - just document the preferred alternative.
## Marking as Experimental
```r
#' @description
#' `r lifecycle::badge("experimental")`
cool_function <- function() {
lifecycle::signal_stage("experimental", "cool_function()")
# ...
}
```
## Testing Deprecations
Test that deprecated functions work and warn appropriately:
```r
test_that("old_fun is deprecated", {
expect_snapshot({
x <- old_fun(1)
expect_equal(x, expected_value)
})
})
```
Suppress warnings in existing tests:
```r
test_that("old_fun returns correct value", {
withr::local_options(lifecycle_verbosity = "quiet")
expect_equal(old_fun(1), expected_value)
})
```
## Deprecation Helpers
For deprecations affecting many functions (e.g., removing a common argument), create an internal helper:
```r
warn_for_verbose <- function(
verbose = TRUE,
env = rlang::caller_env(),
user_env = rlang::caller_env(2)
) {
if (!lifecycle::is_present(verbose) || isTRUE(verbose)) {
return(invisible())
}
lifecycle::deprecate_warn(
when = "2.0.0",
what = I("The `verbose` argument"),
details = c(
"Set `options(mypkg_quiet = TRUE)` to suppress messages.",
"The `verbose` argument will be removed in a future release."
),
user_env = user_env
)
invisible()
}
```
Then use in affected functions:
```r
my_function <- function(..., verbose = deprecated()) {
warn_for_verbose(verbose)
# ...
}
```
## Custom Deprecation Messages
For non-standard deprecations, use `I()` to wrap custom text:
```r
lifecycle::deprecate_warn(
when = "1.0.0",
what = I('Setting option "pkg.opt" to "foo"'),
with = I('"pkg.new_opt"')
)
```
The `what` fragment must work with "was deprecated in..." appended.
## Reference
See `references/lifecycle-stages.md` for detailed stage definitions and transitions.>
Create and use brand.yml files for consistent branding across Shiny apps and Quarto documents. Covers: (1) Creating new _brand.yml files, (2) Applying to Shiny (R and Python), (3) Using in Quarto, (4) Modifying existing files, and (5) Troubleshooting. Includes complete specifications and integration guides.
Write ggsql queries — a grammar of graphics for SQL. Use when the user wants to create, modify, or understand a ggsql visualization query.
Creates a pull request from current changes, monitors GitHub CI, and debugs any failures until CI passes. Activate when the user says "create pr", "make a pr", "open pull request", "submit pr", "pr for these changes", or wants to get their current work into a reviewable PR. Assumes the project uses git, is hosted on GitHub, and has GitHub Actions CI with automated checks (lint, build, tests, etc.). Does NOT merge - stops when CI passes and provides the PR link.
Address PR review feedback by systematically working through every unresolved PR review thread on the current branch's PR - analyze each comment, make the requested code changes (with tests where useful), commit, and optionally reply and resolve.
Bulk resolve unresolved PR review threads on the current branch’s PR — typically after threads have been addressed manually or via /pr-threads-address
>
Guide for drafting issue closure and decline responses as an open-source package maintainer. Use when helping compose a reply that says \"no\" to a feature request, closes an issue as won't-fix, redirects a user to a different package, explains why a design choice is intentional, or otherwise declines or closes a community contribution. Also use when the maintainer needs to explain a deprecation, point out a user misunderstanding, or communicate an effort/scope tradeoff to a contributor.