workflows update_recipe() in R: Swap a Workflow's Recipe
The workflows update_recipe() function in R replaces the recipes preprocessor inside a tidymodels workflow with a new recipe, while leaving the model spec untouched. Use it to A/B preprocessing strategies, iterate on feature engineering, or rebuild a workflow after recipe changes without reassembling the pipeline by hand.
update_recipe(wf, new_rec) # swap the recipe in place wf |> update_recipe(rec_v2) # pipe-friendly swap update_recipe(wf, new_rec, blueprint = bp) # swap with custom blueprint wf |> update_recipe(rec) |> fit(data = df) # swap then refit update_recipe(wf_fit, new_rec) # resets workflow to unfitted update_model(wf, new_spec) # sibling verb for the model remove_recipe(wf) |> add_recipe(new_rec) # equivalent two-step path
Need explanation? Read on for examples and pitfalls.
What update_recipe() does
update_recipe() replaces the recipe slot of a workflow with a new recipe. It returns a new workflow object with the same model spec but a different preprocessor. The original workflow is not modified; the function is pure and assignment-safe.
The verb exists because add_recipe() refuses to overwrite an existing preprocessor. Once a recipe is attached, calling add_recipe() again raises an error. update_recipe() is the explicit replacement door. It is the right tool whenever the model stays the same and only the preprocessing changes, which happens often during feature-engineering iteration and when comparing recipe variants under cross-validation.
update_recipe() syntax and arguments
update_recipe() takes a workflow, a replacement recipe, and an optional blueprint. The signature mirrors add_recipe(), so swapping between the two verbs is a one-word rename.
The x argument must be a workflow() object that already has a recipe attached; calling update_recipe() on a workflow with a formula or variables preprocessor raises an error. The recipe argument must be an unprepped recipes object; passing a prepped recipe is rejected. The blueprint argument lets you override how indicators, intercepts, and NA handling work at prediction time, and is rarely needed.
update_recipe() is one of four workflow update verbs. They are symmetric: each one targets a different slot.
| Verb | Replaces | When to reach for it |
|---|---|---|
update_recipe() |
The recipes preprocessor | Iterate on feature engineering with the same model |
update_model() |
The parsnip model spec | Compare engines or hyperparameters with the same preprocessing |
update_formula() |
A formula preprocessor | Adjust predictors without touching a recipe |
update_variables() |
A variables preprocessor | Swap outcome or predictor selectors |
Each verb is targeted: changing one slot never disturbs the others.
Swap recipes with update_recipe(): four examples
Every example uses built-in mtcars and airquality datasets so the focus stays on the swap mechanics rather than on the data.
Example 1: Swap a minimal recipe for a normalized one
The base case is two recipes and one update. Build the workflow once, then swap as needed.
The original wf still holds the minimal recipe. wf_norm is a separate object with the normalization step. Both can be fit independently to compare model behaviour with and without scaling.
Example 2: A/B compare two preprocessing strategies
Holding the model constant and varying the recipe is the canonical use of update_recipe(). It lets you isolate the effect of preprocessing on hold-out performance.
update_recipe() returns a workflow that fit_resamples() consumes directly. You never rebuild the model spec, so any difference in the metric is attributable to preprocessing alone.
Example 3: Iterate during development
Feature engineering is a tight loop. update_recipe() shortens the loop by avoiding workflow reassembly.
Each new recipe version is a one-line swap from the prior workflow. The model spec, engine choice, and any other workflow state carries forward intact.
Example 4: Update on a fitted workflow resets it
Calling update_recipe() on a fitted workflow is legal but discards the fit. This is the most common surprise.
The returned workflow has the new recipe attached but no trained parameters. Refit with fit(wf_reset, data = aq) before calling predict().
Common pitfalls
Three errors hit beginners the most. Each one has a one-line fix.
update_recipe() and remove_recipe() |> add_recipe() are equivalent. Use the single verb. It is faster to type, shorter to read, and signals intent: you are replacing, not detaching.Try it yourself
Try it: Build a workflow with a recipe of Sepal.Length predicting Petal.Length, then use update_recipe() to switch to a recipe that also includes step_dummy() on Species. Save the swapped workflow to ex_wf.
Click to reveal solution
Explanation: The first recipe attaches with add_recipe(). The second swap happens through update_recipe() in the same pipeline. The result is a workflow with the upgraded recipe and the same linear_reg() spec.
Related tidymodels functions
update_recipe() lives inside the workflows verb family. Knowing the neighbours makes recipe iteration faster.
workflow()creates the empty workflow thatupdate_recipe()later modifies.add_recipe()attaches the initial recipe to an empty workflow.remove_recipe()strips the recipe out and leaves the preprocessor slot empty.update_model()swaps the parsnip model spec while leaving the recipe in place.extract_recipe()returns the recipe currently stored in a fitted or unfitted workflow.fit()retrains the workflow after a recipe swap.
See the workflows package reference for the full verb family.
FAQ
Does update_recipe() refit the workflow?
No. update_recipe() only replaces the recipe slot. Any trained parameters from a prior fit are discarded, and the returned workflow is unfitted. Call fit(wf, data = ...) after the swap to retrain. This separation lets you queue several updates before paying the cost of a fit, which matters during interactive feature engineering and when scripting batch comparisons across recipe variants.
What is the difference between update_recipe() and remove_recipe() then add_recipe()?
They produce the same workflow. update_recipe(wf, new_rec) is one verb and one allocation; remove_recipe(wf) |> add_recipe(new_rec) is two verbs and two allocations. The single-verb form is the idiomatic choice in tidymodels code because it signals replacement intent and reads more naturally inside pipelines.
Can I use update_recipe() to add steps to an existing recipe?
Not directly. update_recipe() swaps the whole recipe object, not individual steps. To extend a recipe, rebuild it with the extra step_*() calls and pass the new object to update_recipe(). The recipes package itself provides no in-place mutation; the design philosophy is to construct recipes declaratively and swap them whole.
Does update_recipe() invalidate tuning results?
Yes. Tuning grids and resampled metrics are tied to the exact workflow used to produce them. Calling update_recipe() returns a new workflow that has not been tuned. Rerun tune_grid() or fit_resamples() on the updated workflow to get fresh results, and treat any prior select_best() output as invalid for the new recipe.
Can update_recipe() change the outcome variable?
Technically yes; the new recipe can declare any formula. In practice you should not change the outcome inside update_recipe() because doing so silently changes what the model predicts and breaks any prior metrics. Treat the outcome as fixed across recipe versions and use update_recipe() only for predictor-side and step-side changes.