# r-lib/devtools: the R package development toolkit that hides the plumbing

> devtools wraps load_all(), document(), test(), check() and release() into one entry point for R package authors. It is a convenience layer over smaller r-lib packages, and the trade-off is that most of its behaviour belongs to those packages.

**r-lib/devtools** — Tools to make an R developer's life easier

- Repository: https://github.com/r-lib/devtools
- Website: https://devtools.r-lib.org
- Stars: 2,521 · Forks: 761
- Language: R
- License: NOASSERTION
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/r-lib-devtools

## The problem devtools solves for R package authors

Writing an R package means running the same sequence dozens of times a day: edit a function in R/, make it visible in a live session, regenerate man/ pages and NAMESPACE, run tests, then run R CMD check. Each of those steps has its own tool, and each tool has its own invocation. devtools exists to collapse that sequence into a handful of functions that all read from the current working directory when no path is given. The README states the aim plainly: "to make package development easier by providing R functions that simplify and expedite common tasks."

The audience is narrow and specific. This is for people who maintain R packages, not for people who consume them. If your work is installing and loading other people's packages, devtools is oversized for the job. If you are writing the package, it is the entry point that the companion book R Packages is built around, and the README points to its Whole Game and Package structure chapters as starting points.

## How load_all(), document() and test() actually connect

The mechanism is delegation, not reimplementation. devtools is a facade over a set of smaller packages that were split out over time, a process the README calls "conscious uncoupling". Each user-facing verb maps to one of them: load_all() belongs to pkgload, document() to roxygen2, test() to testthat, build() to pkgbuild, check() to rcmdcheck, and revdep_check() to revdepcheck.

The data flow for the common loop is worth spelling out. load_all() simulates installing and reloading a package: it loads R code from R/, compiled shared objects from src/ and data files from data/. The README notes that during development you usually want access to internal functions, so load_all() behaves as if every function were exported in NAMESPACE, whether or not it is. test() then reloads with load_all() before running testthat tests, which is why a failing test reflects your edited source rather than a stale installed copy. document() writes generated documentation into man/ and also updates file collation and NAMESPACE. check() updates documentation first, then builds and checks locally.

That chain is the reason the tools feel like one program. It is also the reason a bug in roxygen2 looks like a devtools bug.

## Installing devtools in R and running a first load_all()

Installation is a single call from CRAN. The README gives the CRAN route first and the development route second, using pak for the GitHub version.

```r
# Install devtools from CRAN
install.packages("devtools")

# Or the development version from GitHub:
# install.packages("pak")
pak::pak("r-lib/devtools")
```

After that, the recommended practice is to set your working directory to the package you are developing and call functions without arguments. Every devtools function accepts a path, so load_all("path/to/mypkg") works too, but the README treats the working-directory habit as the recommended one.

```r
library(devtools)
load_all()
```

The first call loads the package sources from the current directory. What you should see is your package's functions available in the session, including internal ones that are not exported. From there the loop is document(), test(), then check() before you consider a release. For installation of other packages, the install_* family covers the common hosts: install_github(), install_gitlab(), install_bitbucket(), install_url(), install_git() and install_svn() for repositories, install_local() for a file on disk, and install_version() for a specific CRAN version. update_packages() works across both CRAN installs and anything installed by those install_* functions.

## install() reloads, and reloading is not guaranteed to work

The most useful limitation in the README is attached to install(). It reinstalls the package, detaches the currently loaded version, then reloads the new version with library(). The README then states that reloading a package is not guaranteed to work and points at the documentation for unload() for the caveats.

This matters because install() sits in the same mental category as load_all(), and the two are not equivalent. load_all() is a simulation that deliberately exposes internals; install() performs a real install and a real library() call. When a package holds state in a namespace, or when a compiled component was already loaded, the detach-and-reload step is the part that can fail, and the fix is usually a fresh R session rather than another call to install().

The second boundary is scope. devtools does not build packages for you in the sense of supplying a compiler toolchain. build() produces a package file from sources, and the README attributes the underlying work, including checking whether build tools are available, to pkgbuild. If your problem is that R cannot find a compiler on your machine, devtools is the wrong layer to debug.

## devtools versus pak for installation work

The README's uncoupling list makes the alternative explicit: pak is the package that handles installing packages, and devtools exposes it as pak::pak(). The difference in approach is dependency resolution. pak is designed as an installer with its own resolution and caching behaviour, and it is the tool the devtools README itself tells you to install first if you want the GitHub version of devtools.

So the split is clean. If you maintain a package and spend your day in load_all(), document(), test() and check(), devtools gives you those verbs alongside the install_* helpers in one namespace. If you are setting up an environment, pinning versions, or installing from a lockfile-style workflow, go to pak directly. Using devtools purely as an installer means carrying the development facade to get at a function that belongs to another package.

The same logic applies to the individual split packages. roxygen2, testthat and rcmdcheck are usable on their own, and a maintainer who wants only documentation generation can depend on roxygen2 without devtools.

## Maintenance, release cadence and the licence question

The repository is not archived, and the last push was on 2026-09-24, four days before this article's date, so the source tree is being touched. Recent releases are v2.5.0 on 2026-03-16, v2.5.1 on 2026-04-16 and v2.5.2 on 2026-04-30. The README also carries a CRAN version badge, and those repository tags are the development line; a CRAN badge reading behind the newest tag is normal rather than a sign of abandonment.

Upgrade cost is mostly inherited rather than paid directly. Because the verbs delegate to pkgload, roxygen2, testthat, rcmdcheck, pkgbuild and revdepcheck, a devtools upgrade can change behaviour that is really a change in one of those dependencies. The repository includes NEWS.md and cran-comments.md at the top level, which is where a maintainer should look before bumping.

On licensing, GitHub reports NOASSERTION for this repository, meaning the platform did not match the licence to a recognised SPDX identifier. The repository does contain LICENSE and LICENSE.md files at the top level. That is the factual position; whether those files grant you the rights you need is a question for your own legal review, not something to infer from the NOASSERTION label alone.

## Conclusion

Adopt devtools if you author R packages and want one namespace for the local development loop; skip it if you only need installation, where pak::pak() is the more direct tool. Before relying on it, verify the licence file, since GitHub reports NOASSERTION rather than a recognised identifier, and confirm which version CRAN currently serves, because the repository releases run ahead of the CRAN badge. The functions themselves delegate to pkgload, roxygen2, testthat, rcmdcheck and pkgbuild, so read those packages when a behaviour surprises you.

## FAQ

### How do I install devtools in R?

The README gives install.packages("devtools") for the CRAN version. For the development version it shows installing pak first and then running pak::pak("r-lib/devtools").

### What is devtools used for in R?

It provides R functions that simplify common package development tasks, including load_all(), document(), test(), check(), build() and release(). The README describes its aim as making package development easier by simplifying and expediting those tasks.

### How do I install devtools?

From CRAN with install.packages("devtools"), or from GitHub by installing pak and running pak::pak("r-lib/devtools"). The README lists both routes in its Installation section.

## Sources

- [Issues](https://github.com/r-lib/devtools/issues)
- [Project website](https://devtools.r-lib.org)
- [README](https://github.com/r-lib/devtools/blob/main/README.md)
- [Releases](https://github.com/r-lib/devtools/releases)
- [r-lib/devtools on GitHub](https://github.com/r-lib/devtools)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/r-lib-devtools
