# Chapter 14 Package evolution - changing stuff in your package

This chapter presents our guidance for changing stuff in your package: changing parameter names, changing function names, deprecating functions.

This chapter was initially contributed as a tech note on rOpenSci website by Scott Chamberlain; you can read the original version here.

## 14.1 Philosophy of changes

Everyone’s free to have their own opinion about how freely parameters/functions/etc. are changed in a library - rules about package changes are not enforced by CRAN or otherwise. Generally, as a library gets more mature, changes to user facing methods (i.e., exported functions in an R package) should become very rare. Libraries that are dependencies of many other libraries are likely to be more careful about changes, and should be.

## 14.2 Parameters: changing parameter names

Sometimes parameter names must be changed for clarity, or some other reason.

A possible approach is check if deprecated arguments are not missing, and stop providing a meaningful message.

foo_bar <- function(x, y) {
if (!missing(x)) {
}
y^2
}

foo_bar(x = 5)
#> Error in foo_bar(x = 5) : use 'y' instead of 'x' 

If you want to be more helpful, you could emit a warning but automatically take the necessary action:

foo_bar <- function(x, y) {
if (!missing(x)) {
y <- x
}
y^2
}

foo_bar(x = 5)
#> 25

Be aware of the parameter .... If your function has ..., and you have already removed a parameter (lets call it z), a user may have older code that uses z. When they pass in z, it’s not a parameter in the function definition, and will likely be silently ignored – not what you want. Instead, leave the argument around, throwing an error if it used.

## 14.3 Functions: changing function names

If you must change a function name, do it gradually, as with any other change in your package.

Let’s say you have a function foo.

foo <- function(x) x + 1

However, you want to change the function name to bar.

Instead of simply changing the function name and foo no longer existing straight away, in the first version of the package where bar appears, make an alias like:

#' foo - add 1 to an input
#' @export
foo <- function(x) x + 1

#' @export
#' @rdname foo
bar <- foo

With the above solution, the user can use either foo() or bar() – either will do the same thing, as they are the same function.

It’s also useful to have a message but then you’ll only want to throw that message when they use the old function, e.g.,

#' foo - add 1 to an input
#' @export
foo <- function(x) {
bar(x)
}

#' @export
#' @rdname foo
bar <- function(x) x + 1

After users have used the package version for a while (with both foo and bar), in the next version you can remove the old function name (foo), and only have bar.

#' bar - add 1 to an input
#' @export
bar <- function(x) x + 1

## 14.4 Functions: deprecate & defunct

To remove a function from a package (let’s say your package name is helloworld), you can use the following protocol:

• Mark the function as deprecated in package version x (e.g., v0.2.0)

In the function itself, use .Deprecated() to point to the replacement function:

foo <- function() {
.Deprecated("bar")
}

There’s options in .Deprecated for specifying a new function name, as well as a new package name, which makes sense when moving functions into different packages.

The message that’s given by .Deprecated is a warning, so can be suppressed by users with suppressWarnings() if desired.

Make a man page for deprecated functions like:

#' Deprecated functions in helloworld
#'
#' These functions still work but will be removed (defunct) in the next version.
#'
#' \itemize{
#'  \item \code{\link{foo}}: This function is deprecated, and will
#'  be removed in the next version of this package.
#' }
#'
#' @name helloworld-deprecated
NULL

This creates a man page that users can access like ?helloworld-deprecated and they’ll see in the documentation index. Add any functions to this page as needed, and take away as a function moves to defunct (see below).

• In the next version (v0.3.0) you can make the function defunct (that is, completely gone from the package, except for a man page with a note about it).

In the function itself, use .Defunct() like:

foo <- function() {
.Defunct("bar")
}

Note that the message in .Defunct is an error so that the function stops whereas .Deprecated uses a warning that let the function proceed.

In addition, it’s good to add ... to all defunct functions so that if users pass in any parameters they’ll get the same defunct message instead of a unused argument message, so like:

foo <- function(...) {
.Defunct("bar")
}

Without ... gives:

foo(x = 5)
#> Error in foo(x = 5) : unused argument (x = 5)

And with ... gives:

foo(x = 5)
#> Error: 'foo' has been removed from this package

Make a man page for defunct functions like:

#' Defunct functions in helloworld
#'
#' These functions are gone, no longer available.
#'
#' \itemize{
#'  \item \code{\link{foo}}: This function is defunct.
#' }
#'
#' @name helloworld-defunct
NULL

This creates a man page that users can access like ?helloworld-defunct and they’ll see in the documentation index. Add any functions to this page as needed. You’ll likely want to keep this man page indefinitely.