Golem Framework and Workflow: Production-Grade Shiny Development

Master the Complete Development Lifecycle from Prototype to Production

Master the Golem framework for building production-ready Shiny applications. Learn the complete development workflow from project setup through deployment, including best practices for enterprise-grade application architecture and team collaboration.

Tools
Author
Affiliation
Published

May 23, 2025

Modified

June 14, 2025

Keywords

golem framework shiny, production shiny applications, golem development workflow, shiny package development, enterprise shiny apps

Key Takeaways

Tip
  • Production-Grade Architecture: Transform Shiny applications into robust, enterprise-ready packages with automated testing, documentation, and deployment capabilities
  • Structured Development Workflow: Master the complete Golem development lifecycle from project initialization through production deployment with guided best practices
  • Package-Based Benefits: Leverage R package infrastructure for dependency management, testing frameworks, and professional distribution methods
  • Team Collaboration Framework: Implement standardized project structures and workflows that support multiple developers and complex application requirements
  • Enterprise Integration: Build applications that integrate seamlessly with corporate infrastructure, security requirements, and deployment pipelines

Introduction

The transition from prototype to production represents the most challenging phase in Shiny application development. While Shiny makes it remarkably easy to create functional applications quickly, scaling these applications to enterprise-grade systems requires architectural decisions and development practices that most data scientists haven’t encountered. The Golem framework bridges this gap, providing an opinionated yet flexible structure for building production-ready Shiny applications.



The motivation behind Golem recognizes a fundamental truth: building a proof-of-concept application is easy, but things change dramatically when applications become larger and more complex, especially when you need to send that app to production. Until recently, there was no real framework for building and deploying production-grade Shiny apps. This is where Golem comes into play, offering Shiny developers a toolkit for making stable, easy-to-maintain, and robust production web applications with R.

Golem represents more than just another R package—it’s a complete development philosophy that transforms how you think about Shiny applications. By treating your Shiny app as an R package from the beginning, Golem unlocks professional development practices including automated testing, comprehensive documentation, systematic dependency management, and standardized deployment procedures.

Understanding the Golem Framework

What Makes Golem Different

Golem is an opinionated framework for building production-ready Shiny applications. This means it provides structured approaches to common development challenges while maintaining flexibility for specific requirements. The framework emphasizes several key principles:

flowchart TD
    A[Golem Philosophy] --> B[Package-Based Architecture]
    A --> C[Structured Development]
    A --> D[Production Focus]
    A --> E[Team Collaboration]
    
    B --> B1[Dependency Management]
    B --> B2[Testing Infrastructure]
    B --> B3[Documentation Standards]
    
    C --> C1[Guided Workflow]
    C --> C2[Best Practice Enforcement]
    C --> C3[Standardized Structure]
    
    D --> D1[Deployment Ready]
    D --> D2[Configuration Management]
    D --> D3[Monitoring Integration]
    
    E --> E1[Modular Development]
    E --> E2[Version Control Integration]
    E --> E3[Code Review Processes]
    
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e8
    style D fill:#fff3e0
    style E fill:#fce4ec

Package-First Approach: A Golem application is contained inside a package. This isn’t just a technical detail—it’s a fundamental architectural decision that enables professional development practices. Everything you know about package development can be applied to Golem, making it easier to manage dependencies, write tests, create documentation, and distribute your application.

Modular by Design: Golem works better if you are working with Shiny modules. The framework encourages modular development from the beginning, making applications easier to maintain, test, and extend. Knowing how modules work is recommended but not necessary to get started.

Production-Oriented: Unlike frameworks designed for rapid prototyping, Golem focuses on building applications that will be sent to production. This means considering factors like configuration management, deployment automation, monitoring integration, and long-term maintenance from the project’s inception.

The Golemverse Ecosystem

Golem is part of the golemverse, a series of tools for building production Shiny apps. This ecosystem includes complementary packages that extend Golem’s capabilities:

  • attachment: Automatic dependency management and DESCRIPTION file maintenance
  • fakir: Fake data generation for prototyping and testing
  • shinipsum: Lorem ipsum for Shiny, enabling UI-first development
  • dockerfiler: Docker integration for containerized deployment
  • covrpage: Coverage reporting and documentation

Getting Started with Golem

Installation and Setup

Install the stable version from CRAN:

install.packages("golem")

For the latest development features:

# install.packages("remotes")
remotes::install_github("Thinkr-open/golem")

Creating Your First Golem Project

Using RStudio (Recommended):

  1. File → New Project → New Directory
  2. Choose “Package for Shiny App Using golem”
  3. Enter your project name (e.g., “myAwesomeApp”)
  4. Choose project location and create

Command Line Creation:

golem::create_golem("path/to/myAwesomeApp")

This creates a complete project structure with all necessary files and directories pre-configured for production development.

Understanding the Golem Project Structure

A new Golem project includes a comprehensive structure designed for professional development:

myAwesomeApp/
├── DESCRIPTION          # Package metadata and dependencies
├── NAMESPACE           # Package namespace (auto-generated)
├── NEWS.md             # Change log
├── README.Rmd          # Project documentation
├── dev/                # Development scripts
│   ├── 01_start.R      # Initial project setup
│   ├── 02_dev.R        # Development workflow
│   ├── 03_deploy.R     # Deployment configuration
│   └── run_dev.R       # Development server
├── R/                  # Application code
│   ├── app_config.R    # Configuration management
│   ├── app_server.R    # Main server logic
│   ├── app_ui.R        # Main UI definition
│   └── run_app.R       # Application launcher
├── inst/               # Package resources
│   ├── app/www/        # Web assets (CSS, JS, images)
│   └── golem-config.yml # Environment configuration
├── man/                # Documentation (auto-generated)
└── tests/              # Testing infrastructure
    └── testthat/       # Unit tests

The Golem Development Workflow

Phase 1: Project Initialization (dev/01_start.R)

The first development script guides you through essential project setup tasks:

## Fill the DESCRIPTION ----
golem::fill_desc(
  pkg_name = "myAwesomeApp",
  pkg_title = "My Awesome Shiny Application",
  pkg_description = "A production-ready dashboard for business analytics.",
  author_first_name = "John",
  author_last_name = "Smith",
  author_email = "john.smith@company.com",
  repo_url = "https://github.com/company/myAwesomeApp"
)

## Set golem options ----
golem::set_golem_options()

## Create Common Files ----
usethis::use_mit_license(name = "John Smith")
usethis::use_readme_rmd(open = FALSE)
usethis::use_code_of_conduct(contact = "john.smith@company.com")
usethis::use_lifecycle_badge("Experimental")
usethis::use_news_md(open = FALSE)

## Use git ----
usethis::use_git()

## Init Testing Infrastructure ----
golem::use_recommended_tests()

## Use Recommended Packages ----
golem::use_recommended_deps()

## Favicon ----
golem::use_favicon()

## Add helper functions ----
golem::use_utils_ui()
golem::use_utils_server()
## Configure Package Dependencies ----
# Add specific packages your app will use
usethis::use_package("DT")
usethis::use_package("plotly")
usethis::use_package("dplyr")

## Add Internal Datasets ----
# For built-in datasets your app needs
usethis::use_data_raw("sample_data")

## Set Up Environment Configuration ----
# Configure different environments (dev, test, prod)
golem::use_recommended_tests()

## Documentation Setup ----
# Prepare for comprehensive documentation
usethis::use_vignette("getting-started")
usethis::use_pkgdown()

This initialization phase establishes the foundation for professional development by configuring package metadata, setting up testing infrastructure, establishing documentation standards, and preparing for version control integration.

Phase 2: Development Workflow (dev/02_dev.R)

The development script provides structure for building your application:

## Add modules ----
golem::add_module(name = "data_input")
golem::add_module(name = "visualization")
golem::add_module(name = "analysis")

## Add utility functions ----
golem::add_utils("data_processing")
golem::add_fct("calculations")

## Add external resources ----
golem::add_css_file("custom")
golem::add_js_file("interactions")

## Dependencies ----
# Automatically detected and added to DESCRIPTION
attachment::att_amend_desc()

## Documentation ----
devtools::document()

Creating Modules with Golem:

# Generate a new module
golem::add_module(name = "dashboard_summary", with_test = TRUE)

This creates two files: - R/mod_dashboard_summary.R - Module implementation - tests/testthat/test-mod_dashboard_summary.R - Module tests

Module Template Structure:

# R/mod_dashboard_summary.R

#' dashboard_summary UI Function
#'
#' @description A shiny Module.
#'
#' @param id,input,output,session Internal parameters for {shiny}.
#'
#' @noRd 
#'
#' @importFrom shiny NS tagList 
mod_dashboard_summary_ui <- function(id){
  ns <- NS(id)
  tagList(
    h3("Dashboard Summary"),
    plotOutput(ns("summary_plot")),
    DT::dataTableOutput(ns("summary_table"))
  )
}
    
#' dashboard_summary Server Functions
#'
#' @noRd 
mod_dashboard_summary_server <- function(id, data){
  moduleServer(id, function(input, output, session){
    ns <- session$ns
    
    output$summary_plot <- renderPlot({
      # Plotting logic using data()
    })
    
    output$summary_table <- DT::renderDataTable({
      # Table generation using data()
    })
  })
}

Phase 3: Deployment Configuration (dev/03_deploy.R)

The deployment script prepares your application for production:

## Docker ----
golem::add_dockerfile()
golem::add_dockerfile_with_renv()

## RStudio Connect ----
golem::add_rstudioconnect_file()

## Shinyapps.io ----
golem::add_shinyappsio_file()

## Posit Connect ----
golem::add_positconnect_file()

## Other platforms ----
golem::add_dockerfile_heroku()
golem::add_dockerfile_shinyproxy()

Each deployment function creates platform-specific configuration files and deployment scripts optimized for your application’s requirements.

Advanced Golem Features

Configuration Management

Golem provides sophisticated configuration management through the golem-config.yml file:

# inst/golem-config.yml
default:
  golem_name: myAwesomeApp
  golem_version: 0.0.0.9000
  app_prod: false
  database_url: "sqlite://./dev.db"
  api_endpoint: "https://api-dev.company.com"
  
production:
  app_prod: true
  database_url: !expr Sys.getenv("DATABASE_URL")
  api_endpoint: "https://api.company.com"
  
testing:
  database_url: "sqlite://./test.db"
  api_endpoint: "https://api-test.company.com"

Accessing Configuration in Your App:

# In your application code
config <- golem::get_golem_config()
database_url <- config$database_url
api_endpoint <- config$api_endpoint

# Environment-specific behavior
if (golem::app_prod()) {
  # Production-specific logic
} else {
  # Development-specific logic
}

Business Logic Separation

Golem encourages separating business logic from Shiny-specific code:

# R/fct_calculations.R - Pure business logic
calculate_roi <- function(investment, return_value) {
  if (investment <= 0) {
    stop("Investment must be positive")
  }
  ((return_value - investment) / investment) * 100
}

calculate_portfolio_metrics <- function(data) {
  data %>%
    group_by(asset_class) %>%
    summarise(
      total_investment = sum(investment),
      total_return = sum(return_value),
      roi = calculate_roi(total_investment, total_return),
      .groups = "drop"
    )
}

# R/mod_portfolio_analysis.R - Shiny module using business logic
mod_portfolio_analysis_server <- function(id, portfolio_data) {
  moduleServer(id, function(input, output, session) {
    
    metrics <- reactive({
      req(portfolio_data())
      calculate_portfolio_metrics(portfolio_data())
    })
    
    output$metrics_table <- DT::renderDataTable({
      metrics()
    })
  })
}

Testing Infrastructure

Golem automatically sets up comprehensive testing infrastructure:

# tests/testthat/test-fct_calculations.R
test_that("ROI calculation works correctly", {
  expect_equal(calculate_roi(100, 150), 50)
  expect_equal(calculate_roi(1000, 1200), 20)
  expect_error(calculate_roi(-100, 150))
})

# tests/testthat/test-mod_portfolio_analysis.R
test_that("Portfolio module loads without error", {
  testServer(mod_portfolio_analysis_server, {
    # Module testing logic
  })
})

# tests/testthat/test-golem_utils_server.R
test_that("App launches", {
  expect_running(sleep = 5)
})


Common Issues and Solutions

Issue 1: Package Loading and Dependencies

Problem: Modules or functions not found when running the app, especially after adding new dependencies.

Solution: Use Golem’s dependency management workflow:

# 1. Add package dependencies explicitly
usethis::use_package("ggplot2")
usethis::use_package("dplyr")

# 2. Update DESCRIPTION automatically
attachment::att_amend_desc()

# 3. Document and reload
devtools::document()
devtools::load_all()

# 4. Check that everything works
golem::run_dev()

Issue 2: Configuration Not Loading

Problem: Configuration values from golem-config.yml not accessible in the application.

Solution: Ensure proper configuration setup and access:

# In app_config.R, verify config is loaded
config <- golem::get_golem_config()

# In your modules, access config properly
mod_my_module_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    # Get config at module level
    config <- golem::get_golem_config()
    
    output$debug_info <- renderText({
      paste("App version:", config$golem_version)
    })
  })
}

# Set environment variable for different configs
Sys.setenv("GOLEM_CONFIG_ACTIVE" = "production")

Issue 3: Module Communication Complexity

Problem: Difficulty passing data between modules in a Golem application.

Solution: Use reactive values and proper module communication patterns:

# In app_server.R
app_server <- function(input, output, session) {
  # Shared reactive values
  shared_data <- reactiveValues(
    current_dataset = NULL,
    filters = list(),
    analysis_results = NULL
  )
  
  # Module communication through shared state
  mod_data_input_server("data_input", shared_data)
  mod_analysis_server("analysis", shared_data)
  mod_visualization_server("viz", shared_data)
}

# In modules
mod_data_input_server <- function(id, shared_data) {
  moduleServer(id, function(input, output, session) {
    observeEvent(input$load_data, {
      # Update shared data
      shared_data$current_dataset <- load_dataset()
    })
  })
}

mod_analysis_server <- function(id, shared_data) {
  moduleServer(id, function(input, output, session) {
    # React to shared data changes
    observe({
      req(shared_data$current_dataset)
      shared_data$analysis_results <- analyze_data(shared_data$current_dataset)
    })
  })
}

Common Questions About Golem Framework

Choose Golem when you’re building applications that will be maintained over time, developed by multiple people, or deployed to production environments. If your application will have more than 1000 lines of code, require automated testing, need systematic deployment, or be used by non-technical stakeholders, Golem provides essential infrastructure. However, for simple prototypes, personal projects, or one-off analyses, the overhead of Golem’s package structure may not be justified. The decision point is often whether you need the professional development practices that Golem enables.

While not strictly required, understanding R package development significantly improves your Golem experience. Golem automates many package development tasks, but you’ll be more effective if you understand concepts like DESCRIPTION files, NAMESPACE management, and testing with testthat. If you’re new to packages, start with Golem and learn package concepts as you encounter them. The framework provides good defaults and guided workflows that teach package development practices implicitly.

Golem uses the golem-config.yml file for environment-specific configuration and the GOLEM_CONFIG_ACTIVE environment variable to switch between configurations. You can define different settings for development, testing, and production environments, then activate the appropriate configuration during deployment. The framework also generates deployment-specific files for various platforms (Docker, RStudio Connect, Heroku) with appropriate configuration handling built in.

Yes, but the migration effort depends on your application’s current structure. Well-organized applications with modular code migrate more easily. The process typically involves: creating a new Golem project, moving UI and server code into Golem’s structure, converting functions to modules where appropriate, setting up package dependencies, and configuring testing infrastructure. For large applications, plan for a gradual migration where you can test each component as you move it. The investment is usually worthwhile for applications that will continue to be developed and maintained.

Golem is more mature and opinionated, providing a complete package-based development workflow with extensive tooling for professional development. Rhino is newer and focuses more on modern web development practices with TypeScript integration. Golem is better for teams familiar with R package development and those wanting comprehensive documentation and deployment tooling. Rhino might be better for teams with strong front-end development skills or those requiring advanced JavaScript integration. Both frameworks solve similar problems but with different philosophical approaches to Shiny development.

Test Your Understanding

You’re setting up a new Golem project for a financial analytics application that will be developed by a team of 3 developers over 6 months. The application needs to connect to multiple databases, provide different interfaces for analysts and managers, and be deployed to both staging and production environments. Which Golem setup approach is most appropriate?

  1. Create separate Golem projects for each user type and environment
  2. Use a single Golem project with modules for different user types and environment configuration
  3. Start with a simple Golem setup and add complexity later
  4. Use Golem only for production and develop features in separate standard Shiny apps
  • Consider the team collaboration requirements
  • Think about configuration management for different environments
  • Remember that Golem is designed for complex, multi-developer projects
  • Consider the benefits of unified development vs. separated concerns

B) Use a single Golem project with modules for different user types and environment configuration

This approach leverages Golem’s strengths for complex, collaborative development:

Why this is correct:

  • Unified Codebase: Single project enables shared business logic, consistent testing, and unified deployment
  • Modular Architecture: Different user interfaces can be implemented as separate modules while sharing core functionality
  • Environment Management: Golem’s configuration system handles multiple deployment environments elegantly
  • Team Collaboration: Single project structure supports multiple developers working on different modules simultaneously

Implementation strategy:

# Module structure
golem::add_module("analyst_dashboard")
golem::add_module("manager_dashboard") 
golem::add_module("data_connection")
golem::add_module("user_management")

# Configuration for environments
# inst/golem-config.yml
default:
  database_analyst: "dev_analyst_db"
  database_manager: "dev_manager_db"
  
production:
  database_analyst: !expr Sys.getenv("PROD_ANALYST_DB")
  database_manager: !expr Sys.getenv("PROD_MANAGER_DB")

Why other options are less suitable:

  • Option A: Creates unnecessary complexity and code duplication
  • Option C: Underestimates the complexity requirements and team needs
  • Option D: Defeats the purpose of using Golem for collaborative development

You’re in the middle of developing a Golem application and need to add a new feature that requires a complex data processing module and integration with an external API. Complete the proper Golem workflow for adding this functionality:

# Step 1: Add the module
golem::add_module(name = "api_integration", ________)

# Step 2: Add business logic functions  
golem::add_fct("________")

# Step 3: Add required dependencies
usethis::use_package("________")
usethis::use_package("________")

# Step 4: Update package documentation
________()

# Step 5: Update dependencies automatically
________()
  • Consider what parameters the add_module function needs for testing
  • Think about what type of functions you’d need for API integration
  • Consider common packages for HTTP requests and data processing
  • Remember Golem’s automatic dependency management tools
# Step 1: Add the module with testing
golem::add_module(name = "api_integration", with_test = TRUE)

# Step 2: Add business logic functions
golem::add_fct("api_processing")

# Step 3: Add required dependencies
usethis::use_package("httr2")      # For HTTP requests
usethis::use_package("jsonlite")   # For JSON processing

# Step 4: Update package documentation
devtools::document()

# Step 5: Update dependencies automatically
attachment::att_amend_desc()

Workflow explanation:

  • with_test = TRUE: Creates both the module and its test file, following TDD practices
  • add_fct(): Creates a file for business logic functions separate from Shiny-specific code
  • HTTP/JSON packages: Essential for API integration work
  • devtools::document(): Updates package documentation based on roxygen2 comments
  • att_amend_desc(): Automatically scans code and updates DESCRIPTION file with dependencies

Additional best practices:

# After adding the module, you might also want to:
golem::add_utils("api_helpers")  # For utility functions
golem::use_recommended_tests()   # Ensure comprehensive testing setup
devtools::load_all()             # Reload package for testing
golem::run_dev()                 # Test the changes

You’re advising three different teams on whether to use Golem or standard Shiny development. Match each scenario with the most appropriate recommendation:

Scenario A: A solo data scientist building a quick exploratory dashboard for personal use to analyze quarterly sales data. Timeline: 1 week, audience: self only.

Scenario B: A 4-person analytics team building a customer insights platform that will be used by 50+ business users across multiple departments. Timeline: 6 months, needs automated testing and deployment.

Scenario C: A research team creating an interactive supplement for a journal publication. Timeline: 2 months, audience: academic peers, needs to be reproducible and publicly available.

Recommendations:

  1. Standard Shiny - simple and fast for individual use
  2. Golem - essential for enterprise-grade requirements
  3. Golem - beneficial for reproducibility and professional presentation
  • Consider complexity, timeline, and audience for each scenario
  • Think about maintenance and collaboration requirements
  • Consider the balance between development overhead and long-term benefits

Scenario A → Recommendation 1 (Standard Shiny)

  • Quick timeline and personal use case don’t justify Golem’s overhead
  • No collaboration or long-term maintenance requirements
  • Standard Shiny provides faster development for simple exploratory work

Scenario B → Recommendation 2 (Golem)

  • Multi-developer team requires structured collaboration
  • Enterprise deployment needs professional development practices
  • 50+ users demand reliability, testing, and proper deployment procedures
  • 6-month timeline justifies investment in proper architecture

Scenario C → Recommendation 3 (Golem)

  • Academic reproducibility benefits from package structure
  • Public availability requires professional presentation
  • Documentation and testing improve credibility
  • Moderate complexity and timeline make Golem investment worthwhile

Decision factors:

  • Team size: Multiple developers → Golem
  • Audience: Enterprise/public → Golem, Personal → Standard
  • Timeline: Short exploratory → Standard, Long-term → Golem
  • Requirements: Testing, deployment, documentation → Golem
  • Maintenance: One-time use → Standard, Ongoing → Golem

Conclusion

The Golem framework represents a paradigm shift in Shiny development, transforming applications from simple scripts into robust, enterprise-grade software. By embracing package-based development from the beginning, Golem provides the infrastructure necessary for building applications that not only work today but remain maintainable and extensible as requirements evolve and teams grow.

The structured workflow that Golem provides—from initial project setup through production deployment—eliminates the guesswork from professional Shiny development. The framework’s opinionated approach ensures that teams follow best practices for testing, documentation, configuration management, and deployment, while its flexibility allows for customization based on specific organizational needs.

Most importantly, Golem bridges the gap between data science and software engineering, providing data scientists with professional development tools while maintaining the rapid development cycles that make Shiny attractive. The result is applications that serve not just as analytical tools but as reliable, scalable business systems that can support organizational decision-making at scale.

Next Steps

Based on what you’ve learned about the Golem framework, here are the recommended paths for continuing your production-grade Shiny development journey:

Immediate Next Steps (Complete These First)

  • Testing and Debugging Strategies - Master the testing infrastructure that Golem provides and learn advanced debugging techniques
  • Production Deployment Overview - Leverage Golem’s deployment capabilities for professional application hosting
  • Practice Exercise: Create a new Golem project and implement a multi-module application using the complete development workflow

Building on Your Foundation (Choose Your Path)

For Advanced Architecture:

For Production Excellence:

For Team Leadership:

Long-term Goals (2-4 Weeks)

  • Build a complete Golem application with multiple modules, comprehensive testing, and production deployment
  • Establish team standards and workflows based on Golem best practices
  • Create organizational templates and guidelines for Golem-based development
  • Implement continuous integration and deployment pipelines using Golem’s deployment tools
Back to top

Reuse

Citation

BibTeX citation:
@online{kassambara2025,
  author = {Kassambara, Alboukadel},
  title = {Golem {Framework} and {Workflow:} {Production-Grade} {Shiny}
    {Development}},
  date = {2025-05-23},
  url = {https://www.datanovia.com/learn/tools/shiny-apps/best-practices/golem-framework.html},
  langid = {en}
}
For attribution, please cite this work as:
Kassambara, Alboukadel. 2025. “Golem Framework and Workflow: Production-Grade Shiny Development.” May 23, 2025. https://www.datanovia.com/learn/tools/shiny-apps/best-practices/golem-framework.html.