5 min read

Before you start swapping out a design system or upgrading a framework across thousands of files, it pays to pick the right approach for each change. Some updates fit neatly into simple find-and-replace patterns, while others demand custom logic you can’t capture in a basic rule. In this post, we’ll show you when to reach for declarative codemods versus imperative ones and how to combine them in a real-world migration.

Pattern-First Changes with Declarative Rules

Declarative codemods use YAML to describe “find this, replace with that” at the AST level. You don’t write loops or visitors; you just point out the code snippet and the fix. YAML is perfect for advanced code pattern detection and simple transformations. Common tasks like renaming imports, updating prop names, or swapping API calls are perfect here. Because rules stay short and readable, you can crank them out fast, and our Studio UI offers AI suggestions and live previews to help you iterate in minutes.

When You Need Full Control: Imperative Transforms

For changes that depend on context such as: dynamic prop values, multi-step adjustments, or conditional edits, you’ll need to build and use imperative transforms. We recommend ast-grep’s NAPI for these cases. NAPI delivers top-notch speed, flexible AST access, and an active open-source community. You write JavaScript or Python to walk the AST, inspect nodes, and emit patches. You can start with YAML to easily detect advanced patterns with the help of AI and use it inside a NAPI script to handle the advanced transformations. If you prefer jscodeshift you can still use it, but for new work ast-grep NAPI is the go-to choice.

Weighing Your Options: Pros and Cons

  • Easy to author: Declarative rules are quick to author, especially with the help of Codemod AI, while imperative codemods take more time to write and test. Over time, Codemod AI keeps getting better at handling complex transformations and generating NAPI codemod scripts.
  • Performance: Declarative rules execute via compiled AST-pattern matching, so they tend to run significantly faster than imperative codemods, which must interpret and execute script logic step by step.
  • Custom-logic flexibility: Imperative transforms handle any context-dependent or multi-step change; declarative rules stop at simple pattern matches.
  • Maintenance: YAML stays concise; larger scripts require ongoing upkeep and unit tests.

Mixing Both for a Successful Migration

Imagine you’re updating a <Button> component across your app. You need to:

  1. Change:
1import { Button } from "old-ui";
2<Button primary onClick={} />

to:

1import { Button } from "new-ui";
2<Button variant="primary" onClick={} analyticsId="button-xyz" />
  1. Update spread props: <Button {...props} />

Step by step

  • Declarative first: Build a YAML rule with Codemod AI that replaces the import path and renames the primary prop to variant="primary". Dry-run it in Studio and review the output preview.
  • Spot edge cases: Some files add extra props via spread or conditionals that aren’t caught by your simple pattern.
  • Imperative fallback: Build an ast-grep NAPI codemod to traverse SgNode trees. In your visitor, detect JSX elements named Button, handle the spread attributes by merging them into explicit props, and inject analyticsId. Write a few test fixtures to ensure you catch every variation. Currently Codemod AI doesn’t support generating NAPI codemods just yet, so you can do this externally either with the help of AI or by learning ast-grep.
  • Iterate and merge: Test both codemods in Studio, verify diffs, then run them together in sequence via the CLI apply the changes to your local project. Additionally, you can publish your codemods to Codemod Registry and share them with your team.

How Codemod Supports You

  • Studio for both: Author YAML rules with AI help, or load your externally-built imperative codemods into Studio for testing and debugging.
  • Insights: Use the Insights dashboard to plan and track your migration progress. Codemod Insights surfaces metrics on rules applied and code changes that scale over time—powered by the same underlying engine that runs your transforms.
  • Campaigns: Migration Campaigns dashboard lets you find migration opportunities, sequence steps, set dependencies, and orchestrate large-scale migration campaigns.
  • Expert support packages: Delegate complicated migrations to migration experts. From initial planning through automation and tracking, we’ll handle the heavy lifting and modernize your codebase while you focus on shipping new features. Contact us now.

By leaning on declarative rules for routine refactors and using imperative codemods only when you need the extra power, you’ll knock out most of your migration in record time and handle the tricky parts with precision. Ready to give it a try? Head over to Codemod, spin up your first rule, and supercharge large-scale code migrations.

Delegate your migrations

Let Codemod agents securely handle your large-scale code migrations, delivering faster time to market, reduced costs, and exceptional quality—all without the staffing hassle.

Get started