Skills lifecycle
git clone https://github.com/posit-dev/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/posit-dev/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/r-lib/lifecycle" ~/.claude/skills/posit-dev-skills-lifecycle && rm -rf "$T"
r-lib/lifecycle/SKILL.mdR 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:
usethis::use_lifecycle()
This:
- Adds lifecycle to
in DESCRIPTIONImports - Adds
to the package documentation file@importFrom lifecycle deprecated - Copies badge SVGs to
man/figures/
Lifecycle Badges
Insert badges in roxygen2 documentation:
#' @description #' `r lifecycle::badge("experimental")` #' `r lifecycle::badge("deprecated")` #' `r lifecycle::badge("superseded")`
For arguments:
#' @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
- Add badge and explanation to
:@description
#' Do something #' #' @description #' `r lifecycle::badge("deprecated")` #' #' `old_fun()` was deprecated in mypkg 1.0.0. Use [new_fun()] instead. #' @keywords internal
- Add
as first line of function body:deprecate_warn()
old_fun <- function(x) { lifecycle::deprecate_warn("1.0.0", "old_fun()", "new_fun()") new_fun(x) }
- Show migration in examples:
#' @examples #' old_fun(x) #' # -> #' new_fun(x)
Deprecation Functions
| Function | When to Use |
|---|---|
| First stage; warns only direct users and during tests |
| Standard deprecation; warns once per 8 hours |
| Final stage before removal; errors with helpful message |
Deprecation workflow for major releases:
- Search
- consider removing function entirelydeprecate_stop() - Replace
withdeprecate_warn()deprecate_stop() - Replace
withdeprecate_soft()deprecate_warn()
Renaming a Function
Move implementation to new name, call from old name with deprecation:
#' @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:
#' @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
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:
#' 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
#' @description #' `r lifecycle::badge("experimental")` cool_function <- function() { lifecycle::signal_stage("experimental", "cool_function()") # ... }
Testing Deprecations
Test that deprecated functions work and warn appropriately:
test_that("old_fun is deprecated", { expect_snapshot({ x <- old_fun(1) expect_equal(x, expected_value) }) })
Suppress warnings in existing tests:
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:
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:
my_function <- function(..., verbose = deprecated()) { warn_for_verbose(verbose) # ... }
Custom Deprecation Messages
For non-standard deprecations, use
I() to wrap custom text:
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.