<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mufatech</title>
    <description>The latest articles on DEV Community by Mufatech (@mufatech).</description>
    <link>https://dev.to/mufatech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4130414%2F992a7615-ed4c-49b8-92b6-69ac4731ba54.png</url>
      <title>DEV Community: Mufatech</title>
      <link>https://dev.to/mufatech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mufatech"/>
    <language>en</language>
    <item>
      <title>Plugin count is a terrible way to size a WooCommerce migration. Here's the data.</title>
      <dc:creator>Mufatech</dc:creator>
      <pubDate>Thu, 17 Sep 2026 19:51:50 +0000</pubDate>
      <link>https://dev.to/mufatech/plugin-count-is-a-terrible-way-to-size-a-woocommerce-migration-heres-the-data-4on9</link>
      <guid>https://dev.to/mufatech/plugin-count-is-a-terrible-way-to-size-a-woocommerce-migration-heres-the-data-4on9</guid>
      <description>&lt;p&gt;Plugin count is the first number anyone quotes when sizing a WooCommerce to Shopify migration. "They're running 58 plugins, this is going to be a big one." Metorik's analysis of &lt;a href="https://metorik.com/blog/woocommerce-plugin-statistics" rel="noopener noreferrer"&gt;more than 6,000 stores&lt;/a&gt; says 58 is about average, so the number is real enough.&lt;/p&gt;

&lt;p&gt;It is also close to useless, and we have the data to show why.&lt;/p&gt;

&lt;p&gt;We mapped the 88 most common WooCommerce and WordPress plugins to whatever replaces them on Shopify, with a migration risk level on each one. Here is how they land:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What replaces it on Shopify&lt;/th&gt;
&lt;th&gt;Plugins&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Built into Shopify&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;42%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Third-party app&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App made by Shopify&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not needed at all&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopify Plus, or an app&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No native equivalent&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Half the stack isn't work. It's deletion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whole categories collapse into a checkbox
&lt;/h2&gt;

&lt;p&gt;The interesting part isn't the percentage, it's which plugins vanish. They go in clusters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payments — five plugins, one setting.&lt;/strong&gt; Stripe, WooPayments, PayPal Checkout and PayPal Payments all reduce to Shopify Payments; Klarna rides on the same rails. A payments stack that took a developer a week to assemble and now needs quarterly maintenance becomes a screen in the admin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEO — four plugins, two fields.&lt;/strong&gt; Yoast, Rank Math, All in One SEO and Redirection all reduce to a native SEO title, a meta description and a redirect table. The plugin that was arguably the most important thing on the site turns into two text inputs. (Getting the &lt;em&gt;content&lt;/em&gt; of those fields across is a real job — the values live in post meta, not in the product export — but that's a data problem, not an architecture one.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page builders — three plugins, gone.&lt;/strong&gt; Elementor, Elementor Pro, WPBakery. Shopify's theme editor covers the same ground with sections and blocks. This is the one that makes people flinch, and it's also the one where migrations quietly improve the site, because the shortcode soup doesn't come along.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance, security and infrastructure — eight plugins, nothing.&lt;/strong&gt; WP Rocket, LiteSpeed Cache, W3 Total Cache, Autoptimize, Wordfence, reCAPTCHA for WooCommerce, Jetpack, Post Views Counter. Nothing replaces them, because the platform owns the layer they were patching. Eight licences, eight update cycles and eight sources of compatibility bugs simply stop existing.&lt;/p&gt;

&lt;p&gt;Gift cards, brands, cookie banners, variation swatches, predictive search, currency switching, ACF field groups: same story, each one a field or a setting rather than a purchase. The instinct that "58 plugins means 58 apps to buy" is wrong by a wide margin, and it's the assumption most migration quotes — and most merchant panic — are built on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 13 that actually cost you
&lt;/h2&gt;

&lt;p&gt;Risk splits 33 none, 22 low, 20 medium, 13 high. The high group is small, and it's always the same shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions and memberships&lt;/strong&gt; — WooCommerce Subscriptions, WooCommerce Memberships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multilingual&lt;/strong&gt; — WPML, Polylang, TranslatePress, WooCommerce Multilingual&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketplaces&lt;/strong&gt; — Dokan, WC Vendors, WCFM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B2B&lt;/strong&gt; — B2BKing, Wholesale Prices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bookings and composite products&lt;/strong&gt; — WooCommerce Bookings, Composite Products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thirteen plugins out of 88 decide almost the entire budget. A store with 60 plugins and none of these is a routine migration. A store with 12 plugins and WooCommerce Subscriptions is not. Counting gets that exactly backwards.&lt;/p&gt;

&lt;p&gt;Subscriptions is the worst of them, because the part that matters isn't in WooCommerce at all. A subscription is a contract in the database plus a payment token held by the gateway. Export the contract and you have a billing schedule that cannot charge anybody.&lt;/p&gt;

&lt;p&gt;Shopify &lt;a href="https://shopify.dev/docs/apps/build/purchase-options/subscriptions/migrate-to-subscriptions-api/migrate-customer-information" rel="noopener noreferrer"&gt;documents four processors&lt;/a&gt; whose saved payment methods can be attached to a migrated customer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gateway&lt;/th&gt;
&lt;th&gt;What transfers&lt;/th&gt;
&lt;th&gt;Identifier you need&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;td&gt;Credit cards only&lt;/td&gt;
&lt;td&gt;Customer ID (&lt;code&gt;cus_…&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Braintree&lt;/td&gt;
&lt;td&gt;Credit cards, Apple Pay&lt;/td&gt;
&lt;td&gt;Customer ID + payment method token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorize.net&lt;/td&gt;
&lt;td&gt;Customer profiles&lt;/td&gt;
&lt;td&gt;Profile ID + payment profile ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PayPal&lt;/td&gt;
&lt;td&gt;Billing agreements&lt;/td&gt;
&lt;td&gt;Agreement ID (&lt;code&gt;B-…&lt;/code&gt;), reference transactions approval required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Anything outside that table means every subscriber re-enters a card, and you will not get all of them back. On the WooCommerce side those identifiers sit in post meta — &lt;code&gt;_stripe_customer_id&lt;/code&gt;, &lt;code&gt;_paypal_subscription_id&lt;/code&gt; — as the official &lt;a href="https://github.com/woocommerce/woocommerce-subscriptions-importer-exporter" rel="noopener noreferrer"&gt;Subscriptions importer/exporter&lt;/a&gt; documents.&lt;/p&gt;

&lt;p&gt;That belongs in a quote on day one, not in a discovery call in week six.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it on the command line
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp plugin list &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;name | npx woocommerce-shopify-plugin-map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HIGH    WooCommerce Subscriptions → Shopify Subscriptions (free, by Shopify) or Recharge
        Active subscriptions depend on stored payment tokens. They move only if your
        payment gateway supports token migration; plan this before anything else.
HIGH    WPML → Shopify Markets with Translate &amp;amp; Adapt
        Translations are imported per language; translated URLs and hreflang change structure.
MEDIUM  Product Bundles → Shopify Bundles (free, by Shopify)
        Fixed bundles map cleanly, mix-and-match needs an app.
LOW     Yoast SEO → Native SEO title and description fields
NONE    WP Rocket → Not needed: Shopify CDN and caching
UNKNOWN my-custom-plugin

Ignored: woocommerce
5 of 6 plugins mapped · highest risk: HIGH
2 high and 1 medium risk plugins decide most of the migration effort.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Input is deliberately forgiving, because plugin lists arrive in whatever shape the site gives you. Slugs, folder paths and asset URLs all resolve to the same entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wordpress-seo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/var/www/wp-content/plugins/wordpress-seo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/wp-content/plugins/wordpress-seo/js/dist/x.js?ver=22.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// all → Yoast SEO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That third form is the one that earns its keep. When you have a URL and no server access, the plugins a storefront will admit to are the ones whose assets it loads — so a crawl of asset paths becomes a plugin list, and a plugin list becomes a plan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;wp-content/plugins | npx woocommerce-shopify-plugin-map
npx woocommerce-shopify-plugin-map &lt;span class="nt"&gt;--file&lt;/span&gt; plugins.txt &lt;span class="nt"&gt;--markdown&lt;/span&gt;
npx woocommerce-shopify-plugin-map &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="s1"&gt;'.by_risk'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--markdown&lt;/code&gt; writes a table you can paste into a proposal. The command exits &lt;code&gt;2&lt;/code&gt; when anything high risk is present, so it can gate a checklist or a build step. WooCommerce itself and the WordPress defaults are ignored rather than reported as unknown, which keeps &lt;code&gt;wp plugin list&lt;/code&gt; output clean.&lt;/p&gt;

&lt;p&gt;There's a library too — TypeScript types, no dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;assess&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;woocommerce-shopify-plugin-map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;assess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;installedSlugs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highestRisk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'high'&lt;/span&gt;
&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byRisk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// { none: 1, low: 0, medium: 0, high: 2 }&lt;/span&gt;
&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// slugs we have no entry for&lt;/span&gt;
&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;known&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// entries, highest risk first&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The data is the actual artifact
&lt;/h2&gt;

&lt;p&gt;The CLI is a thin wrapper around a JSON file, and the JSON file is the point. It ships as &lt;code&gt;plugins.json&lt;/code&gt;, &lt;code&gt;plugins.csv&lt;/code&gt; and a JSON Schema under &lt;strong&gt;CC BY 4.0&lt;/strong&gt;. Each entry carries the Shopify equivalent, an equivalent type, a risk level, notes on what to plan for, and a link to a longer write-up where one exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://raw.githubusercontent.com/mufa-tech/woocommerce-shopify-plugin-map/main/data/plugins.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it in your own tooling, your own estimates, your own checklists. The only condition is attribution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Corrections are the contribution we want most.&lt;/strong&gt; Shopify ships features constantly, and every release makes some row here a little less true — a plugin that needed a paid app last year may be a checkbox today. That decay is the real maintenance cost of a dataset like this one, and it's why it's public rather than sitting in our internal notes. If you find a stale row, or a plugin you keep running into that's missing, open an issue or a PR.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/woocommerce-shopify-plugin-map" rel="noopener noreferrer"&gt;woocommerce-shopify-plugin-map&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/mufa-tech/woocommerce-shopify-plugin-map" rel="noopener noreferrer"&gt;mufa-tech/woocommerce-shopify-plugin-map&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We maintain it at &lt;a href="https://studio.mufatech.com" rel="noopener noreferrer"&gt;Mufatech Studio&lt;/a&gt;, where we build Shopify themes and move stores off WooCommerce. If you'd rather start from a URL than a plugin list, our &lt;a href="https://studio.mufatech.com/migrate/" rel="noopener noreferrer"&gt;readiness scanner&lt;/a&gt; reads a storefront's public data with no login, and the &lt;a href="https://studio.mufatech.com/woocommerce-to-shopify/" rel="noopener noreferrer"&gt;migration guides&lt;/a&gt; go deeper on redirects, SEO fields, the 3-option limit and the subscription problem above.&lt;/p&gt;

&lt;p&gt;If you're sizing a migration right now and something in your stack isn't in the table, put it in the comments — that's usually where the interesting ones come from.&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>wordpress</category>
      <category>opensource</category>
      <category>node</category>
    </item>
  </channel>
</rss>
