Skip to Content
Course content

250: Building an Internal Company R Package

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've sat through countless code reviews where a junior analyst says, "I've already written the helper functions for this project; I'll just email you the utils.R script and you can source() it at the top of your file."

The "source() script is good enough" fallacy

It feels efficient in the moment. You have a few functions that calculate Company-Specific Adjusted Revenue or clean up the weird naming conventions in your SQL database, and you want your teammates to use them. But source() is a trap.

Imagine you've shared company_utils.R with five people. Three months later, you realize there's a bug in the calculate_clv() function. You fix it in your local version. Now, you have to track down every single person who downloaded that script, tell them to delete the old one, and send them the new one. Worse, someone has probably already modified the script locally to "make it work" for their specific project, and now you have five different versions of the "truth" floating around the department.

# The "Old Way" (and why it fails)
source("C:/Users/jane/Documents/scripts/company_utils.R") 
# What happens if Jane leaves the company? 
# What happens if the file path is different on your machine?
# What happens if you don't know what arguments calculate_clv() takes?

Treating your internal tools like a first-class product

The moment a piece of code is used by more than one person, it stops being a "script" and starts being a "product." The correct way to handle this is by building an internal R package. I know, "package" sounds like something only Tidyverse developers do, but an internal package is really just a standardized way of bundling code, documentation, and dependencies.

When you wrap your logic in a package, you gain three immediate superpowers: Versioning, Dependency Management, and Discoverability. Instead of hunting through a folder for a script, your colleagues just type library(corpdata). If they forget how a function works, they type ?calculate_clv and see a professional help page instead of scrolling through 500 lines of code to find your comment.

Turning utility functions into a documented library

You'll want to use devtools and roxygen2. I strongly suggest you don't try to write the .Rd help files by hand—that's a waste of your time. Use roxygen comments directly above your functions.

Take this example. Instead of a loose function in a script, we put it in R/clv_metrics.R within our package structure:

#' Calculate Customer Lifetime Value
#'
#' This function applies the company-standard discount rate to 
#' projected annual revenue.
#'
#' @param annual_rev Numeric vector of annual revenue.
#' @param churn_rate Numeric value between 0 and 1.
#' @return The calculated CLV.
#' @export
calculate_clv <- function(annual_rev, churn_rate) {
  if (churn_rate <= 0) stop("Churn rate must be positive.")
  return(annual_rev / churn_rate)
}

The @export tag is the magic part; it tells R that this function should be available to the user when they load the package, rather than being a hidden helper function used internally by other functions.

Solving the distribution nightmare

The biggest question I get is: "Where do I actually put the package so others can install it?" You don't need to submit it to CRAN. Most companies use an internal Git repository (GitLab, GitHub Enterprise, or Bitbucket).

Once your package is pushed to an internal repo, your colleagues can install it with a single line of code using the remotes package. This ensures everyone is using the exact same version of the logic. If you push a fix to the main branch, everyone just runs the install command again and they're up to date. No more emailing utils_v2_final_FIXED.R.

# The "Professional Way"
# install.packages("remotes")
remotes::install_git("https://git.company.com/analytics/corpdata.git")
library(corpdata)
calculate_clv(1000, 0.2)



📋 Practical Task

Build the 'corpfinance' Internal Utility Package

You have been tasked with creating the first official internal package for the Finance department. They are tired of copying and pasting a specific EBITDA (Earnings Before Interest, Taxes, Depreciation, and Amortization) calculation across ten different spreadsheets and scripts.

Your requirements:

  • Create a package structure using devtools::create("corpfinance").
  • Create a function called calc_ebitda that takes revenue, expenses, interest, taxes, depreciation, and amortization as arguments.
  • The function should return the sum of (Revenue - Expenses) + Interest + Taxes + Depreciation + Amortization.
  • Use roxygen2 tags to:
    • Provide a clear title and description of the function.
    • Document all six parameters.
    • Export the function so it is available to the user.
  • Add dplyr as a dependency in the DESCRIPTION file (even if the function doesn't strictly need it yet, the finance team wants it available for the data frames they'll be passing in).
  • Run devtools::document() to generate the help files.

Verification: To complete the task, ensure that when you run devtools::load_all(), you can call ?calc_ebitda and see the help page you authored.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.