hotpatchR Overview

David Munoz Tord

2026-04-15

Introduction

This vignette explains the hotpatchR design and why runtime namespace patching is the right tool for legacy hotfix workflows.

The legacy package lockdown problem

A loaded R package lives in a locked namespace. Internal functions are resolved from inside the package bubble, so calling a hidden helper from the global environment does not change the package’s internal behavior.

This is the namespace trap that makes legacy hotfixing difficult: a visible exported function may still invoke a broken internal helper even after you have sourced a fixed version elsewhere.

Why the global workaround fails

The usual workaround is:

That workflow is brittle because a bug in one hidden helper can force you to patch many callers, even when only one implementation needs to change.

hotpatchR philosophy

Instead of pulling functions out into the global environment, hotpatchR performs surgical edits inside the package namespace. That means:

Basic workflow

Before using the package, install it from CRAN or GitHub:

install.packages("hotpatchR")
# or, for the development version:
# remotes::install_github("munoztd0/hotpatchR")

The package includes a real example of this pattern with an exported parent function and an internal child helper.

library(hotpatchR)

baseline <- dummy_parent_func("test")
print(baseline)
#> [1] "Parent output -> I am the BROKEN child. Input: test"
#> "Parent output -> I am the BROKEN child. Input: test"

inject_patch(
  pkg = "hotpatchR",
  patch_list = list(
    dummy_child_func = function(x) {
      paste("I am the FIXED child! Input:", x)
    }
  )
)

patched_result <- dummy_parent_func("test")
print(patched_result)
#> [1] "Parent output -> I am the FIXED child! Input: test"
#> "Parent output -> I am the FIXED child! Input: test"

How inject_patch works

inject_patch()

  1. identifies the target namespace or environment
  2. unlocks the binding for the named object
  3. assigns the replacement function into that environment
  4. re-locks the binding

Because the replacement function can be defined with the package namespace as its parent, it still has access to the package’s internal helpers.

Rolling back a patch

If you need to restore the original binding, undo_patch() reverses the previous change.

undo_patch(pkg = "hotpatchR", names = "dummy_child_func")
restored_result <- dummy_parent_func("test")
print(restored_result)
#> [1] "Parent output -> I am the BROKEN child. Input: test"
#> "Parent output -> I am the BROKEN child. Input: test"

Hotfix scripts

apply_hotfix_file() is a convenience wrapper for scripted hotfix application. A compatible hotfix file should define:

Example hotfix file for this package:

#
pkg <- "hotpatchR"
patch_list <- list(
  dummy_child_func = function(x) {
    paste("I am the FIXED child! Input:", x)
  }
)

Then apply it with:

apply_hotfix_file("dev/hotpatchR_hotfix.R")

Next steps

The current package is focused on the core runtime patching path. Future enhancements may include patch comparison, dependency scanning, and CI-friendly test wrappers that explicitly preserve the patched namespace during test execution.

mirror server hosted at Truenetwork, Russian Federation.