Files
gh-sjungling-claude-plugins…/skills/recipe-writer/references/checklist-recipe-development.md
2025-11-30 08:57:41 +08:00

7.7 KiB

OpenRewrite Recipe Development Checklist

Use this checklist to ensure you've covered all important aspects of recipe development.

Planning Phase

Recipe Type Selection

  • Determined if recipe can be declarative (preferred)
  • Evaluated if Refaster template would work for simple replacements
  • Confirmed imperative recipe is necessary for complex logic
  • Identified which LST elements need to be visited
  • Reviewed existing recipes to avoid duplication

Requirements Gathering

  • Clearly defined what the recipe should change
  • Identified what should NOT be changed
  • Documented expected input and output
  • Listed any dependencies or external types needed
  • Determined if multi-file analysis is required (ScanningRecipe)

Implementation Phase

Recipe Class Structure

  • Used @Value and @EqualsAndHashCode(callSuper = false) for immutability
  • All fields are final (via Lombok or manual implementation)
  • Added @JsonCreator constructor with @JsonProperty annotations
  • Defined @Option fields with clear descriptions and examples
  • Implemented getDisplayName() with sentence-case name
  • Implemented getDescription() with clear, period-ending description
  • getVisitor() returns NEW instance (never cached)

Visitor Implementation

  • Chose correct visitor type (JavaIsoVisitor vs JavaVisitor)
  • Called super.visitX() in overridden visit methods
  • Checked for null before accessing type information
  • Implemented "do no harm" - return unchanged LST when unsure
  • Used .withX() methods instead of mutating LSTs
  • Used ListUtils instead of stream operations on LST collections
  • Avoided creating new lists unnecessarily

JavaTemplate Usage (if applicable)

  • Used typed substitution #{any(Type)} for LST elements
  • Used untyped substitution #{} for strings
  • Declared all imports with .imports()
  • Declared static imports with .staticImports()
  • Configured parser with .javaParser() if referencing external types
  • Added classpath dependencies or stubs as needed
  • Used context-free templates (default) when possible
  • Only used .contextSensitive() when necessary

Advanced Features (if applicable)

  • Added preconditions with Preconditions.check()
  • Used UsesType or UsesMethod to filter applicable files
  • Implemented cursor messaging for intra-visitor communication
  • For ScanningRecipe: defined accumulator with Map<JavaProject, T>
  • For ScanningRecipe: implemented getInitialValue()
  • For ScanningRecipe: implemented getScanner() (no changes, only collect)
  • For ScanningRecipe: implemented getVisitor() (uses accumulator data)
  • For ScanningRecipe: implemented generate() if creating new files

Imports and Dependencies

  • Used maybeAddImport() for new types
  • Used maybeRemoveImport() for removed types
  • Chained visitors with doAfterVisit() when needed

Testing Phase

Test Structure

  • Created test class implementing RewriteTest
  • Implemented defaults(RecipeSpec) with recipe configuration
  • Added @DocumentExample to primary test

Test Coverage

  • Test for expected changes (before → after)
  • Test for no changes when not applicable (before only)
  • Test for edge cases and boundary conditions
  • Test with multiple files
  • Test that recipe doesn't change already-correct code
  • Test with different parameter values (if applicable)
  • Test with different Java versions (if version-specific)
  • Added classpath dependencies with spec.parser()

Test Quality

  • Test names clearly describe what is being tested
  • Used meaningful package and class names in test code
  • Included comments explaining complex test scenarios
  • Verified tests pass (including multi-cycle verification)
  • Checked that recipe is idempotent (runs produce same result)

Code Quality Phase

Best Practices

  • Recipe follows "do no harm" principle
  • Recipe makes minimal, least invasive changes
  • Recipe is immutable (no mutable state)
  • Recipe is idempotent (same input → same output)
  • Recipe never mutates LSTs
  • Recipe respects existing formatting
  • Used referential equality checks (same object = no change)

Naming Conventions

  • Display name uses sentence case
  • Display name uses backticks around code elements
  • Display name ends with period (if complete sentence)
  • Description is clear and concise
  • Recipe class name follows VerbNoun pattern
  • Package name follows com.yourorg.category pattern

Performance

  • Added preconditions to skip irrelevant files
  • Used context-free JavaTemplates when possible
  • Avoided unnecessary LST allocations
  • Recipe completes work in single cycle (no causesAnotherCycle)
  • For ScanningRecipe: minimized accumulator size

Multi-Module Support

  • For ScanningRecipe: tracked data per JavaProject
  • Did not assume single project per repository
  • Retrieved JavaProject from markers correctly

Documentation Phase

Code Documentation

  • Added class-level JavaDoc describing the recipe
  • Documented all @Option fields clearly
  • Added inline comments for complex logic
  • Included usage example in JavaDoc

External Documentation

  • Created YAML file in src/main/resources/META-INF/rewrite/ (if distributing)
  • Added recipe to catalog/index (if applicable)
  • Documented any known limitations
  • Added tags for categorization

Distribution Phase

Build Configuration

  • Recipe compiles with Java 8 target (or appropriate version)
  • Added -parameters compiler flag for Jackson
  • Included rewrite-recipe-bom for dependency management
  • Tests run successfully with build tool

Publishing

  • Published to local Maven repository for testing (publishToMavenLocal)
  • Tested recipe in separate project
  • Configured publishing to artifact repository (if applicable)
  • Tagged release in version control

Final Verification

Smoke Testing

  • Ran recipe on sample project
  • Verified changes are correct
  • Verified no unwanted changes were made
  • Checked that formatting is preserved
  • Ran recipe multiple times (idempotence check)

Common Pitfalls Avoided

  • Did not mutate LSTs directly
  • Did not cache visitor instances
  • Did not use ExecutionContext for visitor state
  • Did not forget to call super.visitX()
  • Did not create unnecessary list allocations
  • Did not forget imports in JavaTemplate
  • Did not skip null checks on type information
  • Did not assume single project per repository

Recipe-Specific Checklists

For Declarative Recipes

  • Saved in src/main/resources/META-INF/rewrite/
  • Used type: specs.openrewrite.org/v1beta/recipe
  • All recipe names are fully qualified
  • All parameters are correctly indented
  • String values with special characters are quoted
  • Recipe list is in logical order
  • Tested with spec.recipeFromResources()

For Refaster Template Recipes

  • Created template class with @RecipeDescriptor
  • Implemented @BeforeTemplate methods
  • Implemented single @AfterTemplate method
  • Verified generated recipe works correctly
  • Recipe name uses generated form (e.g., RecipesName)

Notes

Remember:

  • Do no harm: If unsure, don't change
  • Minimize changes: Make only necessary modifications
  • Immutability: Recipes and LSTs must not be mutated
  • Test thoroughly: Both positive and negative cases
  • Document clearly: Future maintainers will thank you

For more information, see:

  • SKILL.md in this skill directory
  • OpenRewrite documentation in this repository
  • Examples in examples/ directory