Note
The majority of this codebase was generated by AI. While it lacks the manual scrutiny of hand-crafted code, we still rely on it daily.
loadfast is an R package for interactive development on large R packages. It is intended as a drop-in replacement for devtools::load_all() when reload time is a bottleneck.
For a given package path, loadfast performs a full load on the first call, then uses MD5-based change detection to re-source only changed R/ files on subsequent calls.
loadfast is intended for the edit-reload-test loop, not as a general replacement for devtools::load_all(). The main tradeoff is described in Important limitation: incremental reload does not clean up stale symbols.
At runtime, loadfast does not depend on pkgload or devtools. Its main runtime dependency is rlang.
Install from GitHub with either pak or remotes:
pak::pkg_install("finccam/loadfast")
# or
remotes::install_github("finccam/loadfast")Call load_fast() from a package root or from any path inside a package:
loadfast::load_fast()
# or
loadfast::load_fast("path/to/your/package")load_fast() reads the package name from DESCRIPTION, builds a namespace, processes NAMESPACE imports, sources R/ files, attaches the package to the search path, and optionally sources testthat helpers.
It supports packages that rely on standard R namespace behavior, including imports, S3 methods (registered via S3method()), S4 classes, and R6 classes. Exports declared with export(), exportPattern(), exportClasses(), and exportMethods() are all honored, so a load_fast()-loaded package can be depended on by another package in the same session.
load_fast() supports the following arguments:
helpers = TRUEto sourcehelper*.Rfiles fromtests/testthat/when testthat is availableattach_testthat = NULLto auto-detect whethertestthatshould be attachedfull = TRUEto force a complete teardown and rebuildverbose = TRUEto emit per-phase timing logs
If runtime code needs a specific file to be re-sourced on the next load, call load_fast_register_reload() to register that file for reload. This is useful for runtime patching and temporary method overrides.
If renv.lock changes between incremental loads for the same package path, load_fast() warns. Dependency changes may require restarting R or reinstalling packages.
Reloading the loadfast package resets the in-memory loader state, so the next load_fast() call for a package path performs a full load.
For performance, the incremental path does not track which symbols came from which file and does not remove stale objects when files are deleted or functions are removed.
This is a conscious tradeoff, not a claim that cleanup is impossible in principle. We have not found a performant enough implementation yet for the large-project use case loadfast targets. Approaches based on per-file symbol diffing and repeated enumeration of large namespaces were too slow.
After an incremental reload:
- deleted files can leave old objects behind
- removed variables or functions can remain available in the loaded package until you force a full rebuild
Use full = TRUE after deleting files, removing functions, or whenever you need a clean namespace state.
load_fast() can load several interdependent packages in the same session: load
the dependency first, then the packages that import from it. Because each
namespace is built with the same metadata a real installed package has
(including the lazydata field, S3 method tables, and full export
processing), cross-package access works — whether through importFrom(),
import(), pkg::name, S3 dispatch, or importClassesFrom() /
importMethodsFrom().
As with devtools::load_all(), a package imports its dependencies' symbols as a
snapshot taken at its own load time. After editing a dependency, reload the
dependency and the packages that import from it so the importers pick up the
change.
Earlier versions failed here with
Error: object 'lazydata' not foundwhen a loaded package was accessed with::. That is fixed.
After installation, the addin is available from the Addins menu as LOADFAST > Load Fast.
To bind it to a keyboard shortcut:
- Open
Tools > Modify Keyboard Shortcuts - Filter for
Load fast - Assign the shortcut you want
You can bind loadfast::load_fast() directly in keybindings.json:
{
"key": "ctrl+shift+l",
"command": "workbench.action.terminal.sendSequence",
"args": {
"text": "loadfast::load_fast()\n"
},
"when": "editorTextFocus && editorLangId == 'r' || terminalFocus"
}You can bind loadfast::load_fast() in the Zed keymap:
"ctrl-shift-l": [
"workspace::SendKeystrokes",
# note: you also need to add a command to focus the terminal first
"l o a d f a s t : : l o a d _ f a s t ( )"
]This repository uses a custom test harness.
Run the full test suite with:
Rscript test_loadfast.RRun only matching checks by setting LOADFAST_TEST_FILTER. The value is treated as a regular expression matched against each check description.
Example:
LOADFAST_TEST_FILTER="inter-pkg|dep-order" Rscript test_loadfast.R