Skip to content

Anti-Patterns & Common Mistakes

When to Use

Avoid these anti-patterns when creating recipes to prevent brittle, untestable, or unmaintainable code.

Decision

Anti-Pattern Why It's Wrong Do This Instead
Monolithic recipes Hard to test, impossible to reuse components, all-or-nothing application Compose granular recipes with clear single responsibilities
Duplicating config across recipes Config drift, maintenance burden, inconsistency Reference shared recipes or use base recipes with overrides
Strict mode everywhere Can't apply to existing sites, brittle on config changes Use strict: false by default, strict: true only for schema (fields)
Missing dependencies Runtime errors, incomplete setup, user confusion Declare all module dependencies in install: and recipe dependencies in recipes:
Hardcoded values Not reusable across environments, brittle Use input system for site-specific values
Untested recipes Breaks on clean installs, missing dependencies, config errors Test every recipe on vanilla Drupal before publishing
Actions before entity exists Config action fails if entity doesn't exist Use createIfNotExists before entity-specific actions
Using install profiles Install profiles deprecated in favor of recipes Migrate to recipe-based approach
Custom code in recipes Recipes are declarative YAML; no PHP, no hooks, no dynamic logic Put custom code in a module, require it via install:
Expecting upgrade paths Recipes have no update mechanism; no way to track or reapply changes Version recipes semantically; document breaking changes; use config management for ongoing updates
Dynamic/conditional logic No if/else, no loops, no runtime decisions in recipe.yml Use modules for dynamic behavior; recipes are static declarations

Pattern

What NOT to do (monolithic recipe):

name: 'Everything Recipe'
install:
  - node
  - media
  # ... 30 more modules
config:
  strict: true  # BAD: Prevents applying to existing sites
  # ... 100 config imports and 50 mixed actions

What to do (composed recipes):

# article_content_type recipe
name: 'Article Content Type'
install:
  - node
  - image
config:
  strict:
    - field.storage.node.field_image  # Only critical schema

# site recipe
name: 'My Site'
recipes:
  - article_content_type
  - page_content_type
  - administrator_role

Why strict mode is dangerous:

# BAD: Fails if role already exists with different permissions
config:
  strict: true
  actions:
    user.role.authenticated:
      grantPermission: 'access content'

# GOOD: Adds permission to existing role
config:
  strict: false
  actions:
    user.role.authenticated:
      grantPermission: 'access content'

Why hardcoded values break:

# BAD: Only works for this specific site
config:
  actions:
    system.site:
      simpleConfigUpdate:
        name: 'Acme Corp Website'

# GOOD: Uses inputs
input:
  site_name:
    data_type: string
    default: { source: config, config: [system.site, name] }
config:
  actions:
    system.site:
      simpleConfigUpdate:
        name: ${site_name}

Common Mistakes

  • Wrong: Treating recipes as update mechanisms → Right: Recipes are apply-once; config is permanent; use config management for updates
  • Wrong: Not handling existing config → Right: Test on both clean installs AND existing sites with similar config
  • Wrong: Forgetting recipes are public once published → Right: Don't commit secrets, API keys, or sensitive data
  • Wrong: Assuming action order doesn't matter → Right: YAML preserves order; actions run sequentially
  • Wrong: Not documenting breaking changes → Right: Version bumps with new required inputs or changed config structure need migration path

See Also