Strangler refactor
Skill manman1414/frontend-refactor-skills/skills/strangler-refactor
React/Vue/TS 前端重构 Agent Skills(行为不变重构、拆文件、绞杀者迁移)
npx -y skills add manman1414/frontend-refactor-skills --skill strangler-refactorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Replaces legacy frontend modules incrementally using the strangler fig pattern — Vue 2 to Vue 3, Options API to Composition API, class components to hooks, old design system to new — with feature flags, parallel routes, and rollback paths. Use when migrating frameworks, replacing large legacy components, or rewriting modules without a big-bang release.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.2 KB, as published. Nobody here has run it
Strangler Refactor (Frontend)
<!-- Author: Administrator | Date: 2026-07-05 -->Replace old implementation with new in small, shippable slices. Each slice is independently mergeable and reversible.
<HARD-GATE> Every slice must: (1) ship behind a flag or route seam, (2) leave old path working until cutover, (3) include rollback steps before deleting legacy code. </HARD-GATE>When to use
- Vue 2 → Vue 3 migration (per route or per widget)
- Options API →
<script setup>+ composables - React class components → function components + hooks
- Legacy UI kit → new design system (Button, Form, Table…)
- jQuery/vanilla widget → React/Vue component
- Old state layer (Vuex module) → Pinia / Zustand slice
Migration patterns by stack
| From → To | Seam strategy |
|---|---|
| Vue 2 → Vue 3 | @vue/compat, per-route lazy mount, micro-frontend shell |
| Options → Composition | Wrapper SFC delegates to new composable; shrink data/methods each slice |
| React class → hooks | Render-props wrapper or HOC forwarding to new FC |
| Vuex → Pinia | Dual-read store adapter; migrate actions one module |
| Old DS → New DS | Feature flag per page section; adapter maps old props → new API |
Workflow
Phase 0 — Inventory
- [ ] List all entry points (routes, embed points, Storybook)
- [ ] Mark dependencies: Vuex/Pinia, i18n, router, auth, API clients
- [ ] Identify blockers: incompatible libs, `$listeners`, filters, global mixins
- [ ] Choose seam: route | component | feature flag | build target
- [ ] Define "done": old code deleted, flag removed, tests migrated
Phase 1 — Introduce seam
Pick ONE:
A. Feature flag (recommended for same route)
// React
import { useFeatureFlag } from "@/features/flags";
const NewOrderPage = lazy(() => import("./OrderPage.v2"));
export function OrderPageRouter() {
const v2 = useFeatureFlag("order-page-v2");
return v2 ? <NewOrderPage /> : <OrderPageLegacy />;
}
<!-- Vue 3 -->
<script setup lang="ts">
import { useFeatureFlag } from "@/composables/useFeatureFlag";
import OrderPageLegacy from "./OrderPage.legacy.vue";
import OrderPageV2 from "./OrderPage.v2.vue";
const v2 = useFeatureFlag("order-page-v2");
</script>
<template>
<OrderPageV2 v-if="v2" />
<OrderPageLegacy v-else />
</template>
B. Route-level (Vue Router / React Router)
/orders → legacy
/orders-v2 → new (internal QA)
/orders → new (after cutover, redirect legacy)
C. Adapter wrapper (same public props)
/** Maps legacy OrderTable props to new DataGrid API */
export function OrderTable(props: LegacyOrderTableProps) {
const v2 = useFeatureFlag("order-table-v2");
if (v2) return <OrderDataGrid {...mapToDataGrid(props)} />;
return <OrderTableLegacy {...props} />;
}
Document rollback: flip flag off, or revert route alias.
Phase 2 — Build new path in parallel
Rules:
- Do not edit legacy file except bugfixes — new code lives in sibling (
*.v2.vue,*.next.tsx) - Match contract first — same props, emits, slots, CSS layout footprint
- Port tests — copy legacy test cases; new path must pass same assertions
- Track parity checklist per slice (see template below)
Phase 3 — Shadow / QA validation
Before cutover:
- Side-by-side in staging with flag ON for test accounts
- Compare network calls (same endpoints, payloads)
- Accessibility spot-check (focus trap, aria labels)
- Performance: no regression on LCP for route-level migrations
Phase 4 — Gradual traffic shift
1. Internal users (flag ON by user id)
2. 5% → 25% → 100% (if platform supports percentage flags)
3. Monitor errors (Sentry) and key metrics
Rollback trigger: error rate spike, critical flow broken → disable flag immediately.
Phase 5 — Delete legacy
Only when ALL true:
- Flag at 100% for ≥ one release cycle (team policy)
- No imports of legacy module (grep clean)
- Tests no longer reference legacy
- Remove flag, adapter, and legacy files in one focused PR
Parity checklist template
Copy per migrated widget:
Widget: _______________
Legacy file: _______________
New file: _______________
Flag key: _______________
Functional parity:
- [ ] Default render
- [ ] Empty state
- [ ] Loading state
- [ ] Error state
- [ ] All user interactions (list each)
- [ ] i18n keys unchanged or migrated
- [ ] Permissions / auth gates
Technical parity:
- [ ] Props / emits / slots contract
- [ ] Router query sync (if any)
- [ ] Vuex/Pinia actions dispatched equivalently
- [ ] No new console errors/warnings
Stack-specific guides
Vue 2 → Vue 3
- Enable
@vue/compatin build if doing incremental app migration - Replace per widget:
filters→ computed;$listeners→v-bind="$attrs" Vue.set/this.$set→ reactive assignment- Event bus → mitt or provide/inject
- Global mixin audit — migrate to composable before cutover
Vue Options → Composition (<script setup>)
Slice order:
- Extract
setup()with returned refs mirroringdata - Move
methodsto composable functions - Replace
computedoptions withcomputed() - Switch to
<script setup>whensetup()is complete - Delete Options block
React class → hooks
Slice order:
- Extract state logic to custom hook (class still renders)
- Create function component using hook
- Wrapper: class renders FC internally OR route flag switches
- Delete class when parity proven
Design system swap
// Adapter preserves call sites during migration
import { Button as ButtonLegacy } from "@/legacy-ui";
import { Button as ButtonNew } from "@/design-system";
export function Button(props: ButtonLegacyProps) {
if (useDesignSystemV2()) {
return <ButtonNew variant={mapVariant(props.type)} {...props} />;
}
return <ButtonLegacy {...props} />;
}
Remove adapter last — after all call sites use new DS or mapper is universal.
Output format (中文)
## 当前切片
- 范围 / flag / 回滚方式
## 并行实现
- legacy 路径 vs 新路径文件
## parity 状态
- checklist 完成项
## 下一步切片
- 建议的下一个可合并步骤
Anti-patterns
- ❌ Big-bang PR replacing entire feature folder
- ❌ Delete legacy before flag reaches production validation
- ❌ Change API contract during migration (behavior change = separate task)
- ❌ Vue 3 migration without addressing incompatible deps first
- ❌ No rollback documented
Related skills
- Structure-only changes →
behavior-preserving-refactor - File splits without replacement →
extract-and-move - Ship / merge decision →
finishing-a-development-branch