Why recipes own snapshots of profiles
In BrewDesigner, water profiles, mash profiles, and fermentation profiles are reusable libraries. Recipes don’t reference them by foreign key at render time — they own a snapshot of the profile at the moment the recipe was created or last edited.
The pattern
When you attach a mash profile to a recipe, the app copies the profile’s steps, temperatures, and durations into a recipe-scoped table (recipe_mash + recipe_mash_steps). The original profile in your library is unaffected.
The recipe’s snapshot stores two pointers to its source:
source_profile_name— the human label, frozen at copy time.source_profile_id— a foreign key withON DELETE SET NULL. It’s lineage only; if you delete the source profile, the snapshot stays intact and the FK becomes null.
Why this design
We considered three alternatives:
- Hard FK to the profile. Edits to the profile would silently change every recipe that used it — unacceptable.
- Versioned profiles. Every save of a profile creates a new version; recipes pin a version. Solves the silent-edit problem but explodes the number of profile rows over time.
- Snapshots. Each recipe gets its own copy. Profiles are just templates. Edits to a recipe’s mash don’t touch the library; edits to the library don’t touch existing recipes.
We picked snapshots because:
- The data is small (a few rows of mash steps per recipe).
- It maps cleanly to how brewers think: “this batch used this mash, even if I tweak the template later.”
- It makes recipes self-contained for export, sharing, and import.
Consequences
- Editing a recipe’s mash never affects your library — there’s no “save this back to the profile” button. If you want to update the library, do it in the profiles page.
- Importing a recipe from BeerXML / BeerSmith populates the snapshot directly without needing a matching library entry.
- Deleting a profile from your library is safe; existing recipes keep working.
Last updated on