{"id":640546,"date":"2025-09-23T15:15:39","date_gmt":"2025-09-23T14:15:39","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=webstorm&#038;p=640546"},"modified":"2026-01-16T19:17:34","modified_gmt":"2026-01-16T18:17:34","slug":"from-ai-generated-to-production-ready-code-webstorm-refactorings-for-the-modern-workflow","status":"publish","type":"webstorm","link":"https:\/\/blog.jetbrains.com\/ko\/webstorm\/2025\/09\/from-ai-generated-to-production-ready-code-webstorm-refactorings-for-the-modern-workflow","title":{"rendered":"From AI-Generated to Production-Ready Code: WebStorm Refactorings for the Modern Workflow"},"content":{"rendered":"\n<p>We&#8217;ve all been there. You ask your AI tool to generate a React component, and the initial result works perfectly in the beginning. The AI gives you a functional 250-line component that gets the job done quickly and is exactly what you need for rapid prototyping.<\/p>\n\n\n\n<p>But even though AI does indeed excel at generating working code that solves your immediate problem, it\u2019s the next step \u2013 transforming that working code into maintainable, production-ready code \u2013 where WebStorm&#8217;s refactoring tools can make a real difference. While AI focuses on getting you from zero to working (which is incredibly valuable), WebStorm&#8217;s deterministic refactorings help you get from working to production-ready code.<\/p>\n\n\n\n<p>Let me walk you through this modern workflow by taking a realistic analytics dashboard component and transforming it step by step. By the end, you&#8217;ll see how WebStorm&#8217;s refactoring tools complement AI-generated code perfectly, saving you hours while making your code exponentially better.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The starting point: A &#8220;working&#8221; analytics dashboard<\/h2>\n\n\n\n<p>Let&#8217;s say you asked an AI to create an analytics dashboard. Here&#8217;s what you might get back \u2013 a component that works great as a starting point but has room for improvement in terms of maintainability:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">import { useEffect, useState} from &#039;react&#039;;\n\nconst AnalyticsDashboard = () =&gt; {\n\n    const &#091;data, setData] = useState&lt;{\n        &quot;totalUsers&quot;: number,\n        &quot;previousTotalUsers&quot;: number,\n        &quot;revenue&quot;: number,\n        &quot;previousRevenue&quot;: number,\n        &quot;pageViews&quot;: number,\n        &quot;previousPageViews&quot;: number,\n        &quot;conversionRate&quot;: number,\n        &quot;previousConversionRate&quot;: number,\n        &quot;recentActivity&quot;: {user: string, action: string, timestamp: string}&#091;]\n    } | null&gt;(null);\n    const &#091;loading, setLoading] = useState(true);\n    const &#091;error, setError] = useState&lt;string | null&gt;(null);\n\n    useEffect(() =&gt; {\n        fetch(&#039;\/api\/analytics&#039;)\n            .then(response =&gt; response.json())\n            .then(result =&gt; {\n                setData(result);\n                setLoading(false);\n            })\n            .catch(err =&gt; {\n                setError(&#039;Failed to load analytics data&#039;);\n                setLoading(false);\n            });\n    }, &#091;]);\n\n    const formatNumber = (num: number) =&gt; {\n        if (num &gt;= 1000000) {\n            return (num \/ 1000000).toFixed(1) + &#039;M&#039;;\n        } else if (num &gt;= 1000) {\n            return (num \/ 1000).toFixed(1) + &#039;K&#039;;\n        }\n        return num.toString();\n    };\n\n    const formatDate = (dateString: string) =&gt; {\n        const date = new Date(dateString);\n        const months = &#091;&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];\n        return months&#091;date.getMonth()] + &#039; &#039; + date.getDate() + &#039;, &#039; + date.getFullYear();\n    };\n\n    const calculateGrowth = (current: number, previous: number) =&gt; {\n        if (previous === 0) return 0;\n        return ((current - previous) \/ previous) * 100;\n    };\n\n    if (loading) return &lt;div&gt;Loading analytics...&lt;\/div&gt;;\n    if (error) return &lt;div style={{color: &#039;red&#039;}}&gt;{error}&lt;\/div&gt;;\n    if (!data) return &lt;div&gt;No data available&lt;\/div&gt;;\n\n    return (\n        &lt;div style={{padding: &#039;20px&#039;, fontFamily: &#039;Arial, sans-serif&#039;}}&gt;\n            &lt;h1&gt;Analytics Dashboard&lt;\/h1&gt;\n\n            &lt;div style={{\n                display: &#039;grid&#039;,\n                gridTemplateColumns: &#039;repeat(auto-fit, minmax(250px, 1fr))&#039;,\n                gap: &#039;20px&#039;,\n                marginBottom: &#039;30px&#039;\n            }}&gt;\n                &lt;div style={{border: &#039;1px solid #ddd&#039;, padding: &#039;20px&#039;, borderRadius: &#039;8px&#039;}} className=&quot;metric-card&quot;&gt;\n                    &lt;h3&gt;Total Users&lt;\/h3&gt;\n                    &lt;div style={{fontSize: &#039;32px&#039;, fontWeight: &#039;bold&#039;, color: &#039;#2196F3&#039;}}&gt;\n                        {formatNumber(data.totalUsers)}\n                    &lt;\/div&gt;\n                    &lt;div\n                        style={{color: calculateGrowth(data.totalUsers, data.previousTotalUsers) &gt;= 0 ? &#039;green&#039; : &#039;red&#039;}}&gt;\n                        {calculateGrowth(data.totalUsers, data.previousTotalUsers) &gt;= 0 ? &#039;\u2197&#039; : &#039;\u2198&#039;}\n                        {Math.abs(calculateGrowth(data.totalUsers, data.previousTotalUsers)).toFixed(1)}% vs last month\n                    &lt;\/div&gt;\n                &lt;\/div&gt;\n\n                &lt;div style={{border: &#039;1px solid #ddd&#039;, padding: &#039;20px&#039;, borderRadius: &#039;8px&#039;}} className=&quot;metric-card&quot;&gt;\n                    &lt;h3&gt;Revenue&lt;\/h3&gt;\n                    &lt;div style={{fontSize: &#039;32px&#039;, fontWeight: &#039;bold&#039;, color: &#039;#4CAF50&#039;}}&gt;\n                        ${formatNumber(data.revenue)}\n                    &lt;\/div&gt;\n                    &lt;div style={{color: calculateGrowth(data.revenue, data.previousRevenue) &gt;= 0 ? &#039;green&#039; : &#039;red&#039;}}&gt;\n                        {calculateGrowth(data.revenue, data.previousRevenue) &gt;= 0 ? &#039;\u2197&#039; : &#039;\u2198&#039;}\n                        {Math.abs(calculateGrowth(data.revenue, data.previousRevenue)).toFixed(1)}% vs last month\n                    &lt;\/div&gt;\n                &lt;\/div&gt;\n\n                &lt;div style={{border: &#039;1px solid #ddd&#039;, padding: &#039;20px&#039;, borderRadius: &#039;8px&#039;}} className=&quot;metric-card&quot;&gt;\n                    &lt;h3&gt;Page Views&lt;\/h3&gt;\n                    &lt;div style={{fontSize: &#039;32px&#039;, fontWeight: &#039;bold&#039;, color: &#039;#FF9800&#039;}}&gt;\n                        {formatNumber(data.pageViews)}\n                    &lt;\/div&gt;\n                    &lt;div\n                        style={{color: calculateGrowth(data.pageViews, data.previousPageViews) &gt;= 0 ? &#039;green&#039; : &#039;red&#039;}}&gt;\n                        {calculateGrowth(data.pageViews, data.previousPageViews) &gt;= 0 ? &#039;\u2197&#039; : &#039;\u2198&#039;}\n                        {Math.abs(calculateGrowth(data.pageViews, data.previousPageViews)).toFixed(1)}% vs last month\n                    &lt;\/div&gt;\n                &lt;\/div&gt;\n\n                &lt;div style={{border: &#039;1px solid #ddd&#039;, padding: &#039;20px&#039;, borderRadius: &#039;8px&#039;}} className=&quot;metric-card&quot;&gt;\n                    &lt;h3&gt;Conversion Rate&lt;\/h3&gt;\n                    &lt;div style={{fontSize: &#039;32px&#039;, fontWeight: &#039;bold&#039;, color: &#039;#9C27B0&#039;}}&gt;\n                        {data.conversionRate.toFixed(2)}%\n                    &lt;\/div&gt;\n                    &lt;div\n                        style={{color: calculateGrowth(data.conversionRate, data.previousConversionRate) &gt;= 0 ? &#039;green&#039; : &#039;red&#039;}}&gt;\n                        {calculateGrowth(data.conversionRate, data.previousConversionRate) &gt;= 0 ? &#039;\u2197&#039; : &#039;\u2198&#039;}\n                        {Math.abs(calculateGrowth(data.conversionRate, data.previousConversionRate)).toFixed(1)}% vs\n                        last month\n                    &lt;\/div&gt;\n                &lt;\/div&gt;\n            &lt;\/div&gt;\n\n            &lt;div style={{marginTop: &#039;40px&#039;}}&gt;\n                &lt;h2&gt;Recent Activity&lt;\/h2&gt;\n                &lt;div style={{border: &#039;1px solid #ddd&#039;, borderRadius: &#039;8px&#039;, overflow: &#039;hidden&#039;}}&gt;\n                    {data.recentActivity &amp;&amp; data.recentActivity.map((activity: any, index: number) =&gt; (\n                        &lt;div key={index} style={{\n                            padding: &#039;15px&#039;,\n                            borderBottom: index &lt; data.recentActivity.length - 1 ? &#039;1px solid #eee&#039; : &#039;none&#039;,\n                            display: &#039;flex&#039;,\n                            justifyContent: &#039;space-between&#039;,\n                            alignItems: &#039;center&#039;\n                        }}&gt;\n                            &lt;div&gt;\n                                &lt;div style={{fontWeight: &#039;bold&#039;}}&gt;{activity.user}&lt;\/div&gt;\n                                &lt;div style={{color: &#039;#666&#039;, fontSize: &#039;14px&#039;}}&gt;{activity.action}&lt;\/div&gt;\n                            &lt;\/div&gt;\n                            &lt;div style={{color: &#039;#999&#039;, fontSize: &#039;12px&#039;}}&gt;\n                                {formatDate(activity.timestamp)}\n                            &lt;\/div&gt;\n                        &lt;\/div&gt;\n                    ))}\n                &lt;\/div&gt;\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n    );\n};\n\nexport default AnalyticsDashboard;<\/pre>\n\n\n\n<p>This component works perfectly for rapid prototyping and delivers immediate value, but it\u2019s far from polished. Now, let&#8217;s use WebStorm&#8217;s refactoring tools to evolve it into production-ready code that&#8217;s easier to maintain, test, and extend.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Follow along: Interactive repository<\/h2>\n\n\n\n<p>Want to practice these refactorings yourself? I&#8217;ve created a repository where you can follow along with each step: <a href=\"https:\/\/github.com\/niklas-wortmann\/refactoring-react-example\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/niklas-wortmann\/refactoring-react-example<\/a><\/p>\n\n\n\n<p>Each refactoring step is a separate branch, so you can:<\/p>\n\n\n\n<ul>\n<li><strong>See the exact changes:<\/strong> Use <a href=\"https:\/\/www.jetbrains.com\/guide\/tips\/compare-with-branch\/\" target=\"_blank\" rel=\"noopener\">Compare with Branch<\/a> to see what each refactoring changed.<\/li>\n\n\n\n<li><strong>Try it yourself: <\/strong>Check out any branch and practice the refactoring<\/li>\n\n\n\n<li><strong>Compare results:<\/strong> See how your refactoring compares to the example.<\/li>\n<\/ul>\n\n\n\n<p>The repository includes the original AI-generated code as the starting point, with each subsequent commit showing the result of applying one of the refactorings below. This hands-on approach will help you master these techniques much faster than just reading about them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Extract type alias for inline types<\/h2>\n\n\n\n<p><strong>The problem:<\/strong> Inline type declarations make your code harder to read and impossible to reuse. For an illustration, just look at this <code>useState<\/code> declaration with an inline object type:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">const &#091;data, setData] = useState&lt;{\n  totalUsers: number;\n  previousTotalUsers: number; \n  revenue: number;\n  previousRevenue: number;\n  pageViews: number;\n  previousPageViews: number;\n  conversionRate: number;\n  previousConversionRate: number;\n  recentActivity: {\n    user: string;\n    action: string;\n    timestamp: string;\n  }&#091;];\n} | null&gt;(null);<\/pre>\n\n\n\n<p>This approach gets you running immediately, but for production code, you&#8217;ll want proper interfaces.<\/p>\n\n\n\n<p><strong>The solution:<\/strong> WebStorm&#8217;s <em>Extract type alias<\/em> refactoring.<\/p>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Select the object type definition (everything between <code>{<\/code> and <code>}<\/code>, including the nested object types)<\/li>\n\n\n\n<li>Right-click, select <em>Refactor<\/em>, and then click on <em>Extract Type Alias<\/em>.<\/li>\n\n\n\n<li>WebStorm will automatically create proper interfaces and update all usages of this literal type within the file.<\/li>\n<\/ol>\n\n\n\n<p><strong>The result:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">interface AnalyticsData {\n  totalUsers: number;\n  previousTotalUsers: number;\n  revenue: number;\n  previousRevenue: number;\n  pageViews: number;\n  previousPageViews: number;\n  conversionRate: number;\n  previousConversionRate: number;\n  recentActivity: {\n    user: string;\n    action: string;\n    timestamp: string;\n  }&#091;];\n}\n\nconst &#091;analyticsData, setAnalyticsData] = useState&lt;AnalyticsData | null&gt;(null);<\/pre>\n\n\n\n<p>Notice that the nested object type for <code>recentActivity<\/code> is still inline. You can run the refactoring again on nested types to extract them to whatever granularity makes sense for reuse:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">\/\/ Select the nested object type and extract again\ninterface RecentActivity {\n  user: string;\n  action: string;\n  timestamp: string;\n}\n\ninterface AnalyticsData {\n  totalUsers: number;\n  previousTotalUsers: number;\n  revenue: number;\n  previousRevenue: number;\n  pageViews: number;\n  previousPageViews: number;\n  conversionRate: number;\n  previousConversionRate: number;\n  recentActivity: RecentActivity&#091;];\n}<\/pre>\n\n\n\n<p><strong>Why this matters:<\/strong> WebStorm finds and updates all instances of that exact type structure throughout your file. What would take you five or more minutes manually (hunting down every usage and likely missing a spot) happens instantly and without error.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Rename for clarity<\/h2>\n\n\n\n<p><strong>The problem: <\/strong>Variables like <code>data<\/code> and <code>result<\/code> tell us nothing. When you come back to this code in six months, you&#8217;ll waste time figuring out what they contain.<\/p>\n\n\n\n<p><strong>The solution:<\/strong> WebStorm&#8217;s smart <em>Rename<\/em> refactoring.<\/p>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Place the cursor on the variable\u2019s name.<\/li>\n\n\n\n<li>Press <em>Shift+F6<\/em> (or right-click, then <em>&#8220;Refactor\u201d<\/em>, and select <em>Rename<\/em>)<\/li>\n\n\n\n<li>Type the new name and WebStorm will update all the references automatically.<br><\/li>\n<\/ol>\n\n\n\n<p>Let&#8217;s rename:<\/p>\n\n\n\n<p>* <code>data<\/code> \u2192 <code>analyticsData<\/code><br>* <code>result<\/code> \u2192 <code>apiResponse<\/code><br>* <code>err<\/code> \u2192 <code>apiError<\/code><\/p>\n\n\n\n<p><strong>Why this matters:<\/strong> WebStorm&#8217;s <em>Rename<\/em> refactoring is scope-aware and handles edge cases that find-and-replace misses. It knows the difference between your local variable <code>data<\/code> and a property called <code>data<\/code> on some other object.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Extract reusable components<\/h2>\n\n\n\n<p><strong>The problem:<\/strong> This component is doing everything: data fetching, formatting, and rendering multiple UI patterns. It&#8217;s impossible to test individual pieces or reuse the metric cards elsewhere.<\/p>\n\n\n\n<p><strong>The solution: <\/strong>WebStorm&#8217;s <em>Extract Component<\/em> refactoring.<\/p>\n\n\n\n<p>Let&#8217;s extract that repeated metric card pattern:<\/p>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Select the JSX for one complete metric card (elements with the class <code>metric-card<\/code>)<\/li>\n\n\n\n<li>Right-click, select <em>Refactor<\/em>, and then <em>pick Extract Component<\/em> (or use the shortcut <em>Ctrl+Alt+M\/\u2325+\u2318+M<\/em>).<\/li>\n\n\n\n<li>WebStorm will:\n<ul>\n<li>Create a new component.<\/li>\n\n\n\n<li>Identify the props automatically.<\/li>\n\n\n\n<li>Replace the selected code with the component call.<br><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>The result:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">export function MetricCardProps(props: { s: string, number: number }) {\n    return &lt;div style={{border: &quot;1px solid #ddd&quot;, padding: &quot;20px&quot;, borderRadius: &quot;8px&quot;}}&gt;\n        &lt;h3&gt;Total Users&lt;\/h3&gt;\n        &lt;div style={{fontSize: &quot;32px&quot;, fontWeight: &quot;bold&quot;, color: &quot;#2196F3&quot;}}&gt;\n            {props.s}\n        &lt;\/div&gt;\n        &lt;div\n            style={{color: props.number &gt;= 0 ? &quot;green&quot; : &quot;red&quot;}}&gt;\n            {props.number &gt;= 0 ? &quot;\u2197&quot; : &quot;\u2198&quot;}\n            {Math.abs(props.number).toFixed(1)}% vs last month\n        &lt;\/div&gt;\n    &lt;\/div&gt;;\n}<\/pre>\n\n\n\n<p><strong>Why this matters:<\/strong> WebStorm automatically figured out what should be props and what should stay internal. This kind of analysis would take you several minutes manually, and you&#8217;d probably miss something.<\/p>\n\n\n\n<p><em>Note: The Extract Component refactoring works seamlessly across the major frontend frameworks (Angular, Vue, and React). We&#8217;re using React for demo purposes, but this refactoring technique applies universally.<\/em><\/p>\n\n\n\n<p><strong>Bonus:<\/strong> Once the component is extracted, you can use WebStorm&#8217;s <em>Move<\/em> refactoring (<em>F6<\/em>) to relocate the <code>MetricCard<\/code> component to its own file (e.g., <code>components\/MetricCard.tsx<\/code>). WebStorm will automatically:<\/p>\n\n\n\n<ul>\n<li>Create the new file with proper imports.<\/li>\n\n\n\n<li>Update the import statement in the original file.<\/li>\n\n\n\n<li>Handle any type dependencies between files.<\/li>\n<\/ul>\n\n\n\n<p>This transforms your extracted component from a local helper into a truly reusable component that other parts of your application can import and use. Note that WebStorm won&#8217;t automatically detect and replace similar code patterns elsewhere in your codebase. Those would need to be manually refactored to use the new component.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3.5: Rename props for clarity<\/h2>\n\n\n\n<p><strong>The problem:<\/strong> The extracted component works perfectly, but WebStorm generated prop names based on the original variable names, which might not be ideal for a reusable component.<\/p>\n\n\n\n<p><strong>The solution:<\/strong> WebStorm&#8217;s smart <em>Rename<\/em> refactoring. We already went over this, but this also works well when destructuring props.<\/p>\n\n\n\n<p>Looking at our extracted <code>MetricCard<\/code>, the props might have generic names like <code>value<\/code> and <code>previousValue<\/code>. For a reusable component, these could be more descriptive:<\/p>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Place the cursor on the prop name in the JSX part.<\/li>\n\n\n\n<li>Press <em>Shift+F6<\/em> (or right-click, pick <em>Refactor<\/em>, and then select <em>Rename<\/em>)<\/li>\n\n\n\n<li>Type the new name and WebStorm updates all the references automatically.<\/li>\n<\/ol>\n\n\n\n<p>Let&#8217;s rename:<\/p>\n\n\n\n<p>* <code>s<\/code> \u2192 <code>label<\/code><br>* <code>number<\/code> \u2192 <code>delta<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Add props for better reusability<\/h2>\n\n\n\n<p><strong>The problem:<\/strong> Our <code>MetricCard<\/code> component has hardcoded trend icons (\u2197 and \u2198). To make it more flexible, let&#8217;s make the icon configurable by the parent component.<\/p>\n\n\n\n<p><strong>The solution:<\/strong> WebStorm&#8217;s <em>Create component prop<\/em> quick-fix.<\/p>\n\n\n\n<p>First, let&#8217;s use the new prop in the parent component by adding it to one of our <code>MetricCard<\/code> calls:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">&lt;MetricCard\n  label=&quot;Total Users&quot;\n  delta={calculateGrowth(analyticsData.totalUsers, analyticsData.previousTotalUsers)}\n  icon={calculateGrowth(analyticsData.totalUsers, analyticsData.previousTotalUsers) &gt;= 0 ? &quot;\u2197&quot; : &quot;\u2198&quot;}\n\/&gt;<\/pre>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Place the cursor on the new <code>icon<\/code> prop (it will be highlighted as an error).<\/li>\n\n\n\n<li>Hit <em>Alt+Enter<\/em>\/<em>\u2325+Enter<\/em> and then select the <em>Create component prop &#8216;icon&#8217;<\/em> option.<\/li>\n\n\n\n<li>WebStorm automatically adds the prop to the <code>MetricCardProps<\/code> interface.<\/li>\n\n\n\n<li>Update the component to use the new <code>icon<\/code> prop instead of the hardcoded logic.<\/li>\n<\/ol>\n\n\n\n<p><strong>The result:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">export function MetricCard(props: { label: string, delta: number, icon?: string }) {\n    return &lt;div style={{border: &quot;1px solid #ddd&quot;, padding: &quot;20px&quot;, borderRadius: &quot;8px&quot;}}&gt;\n        &lt;h3&gt;Total Users&lt;\/h3&gt;\n        &lt;div style={{fontSize: &quot;32px&quot;, fontWeight: &quot;bold&quot;, color: &quot;#2196F3&quot;}}&gt;\n            {props.label}\n        &lt;\/div&gt;\n        &lt;div\n            style={{color: props.delta &gt;= 0 ? &quot;green&quot; : &quot;red&quot;}}&gt;\n            {props.icon ?? props.delta &gt;= 0 ? &quot;\u2197&quot; : &quot;\u2198&quot;}\n            {Math.abs(props.delta).toFixed(1)}% vs last month\n        &lt;\/div&gt;\n    &lt;\/div&gt;;\n}<\/pre>\n\n\n\n<p><strong>Note:<\/strong> By default, the newly created prop will be optional; this way, the introduced change is backwards compatible with existing usages of that component. This might be something that you want to change manually based on the semantics of the prop and the component usage.<\/p>\n\n\n\n<p><strong>Why this matters:<\/strong> WebStorm&#8217;s <em>Create component prop<\/em> quick-fix ensures perfect synchronization between prop usage and interface definitions. No more forgetting to add props to interfaces or mismatched types. This feature works similarly in Angular (for component inputs and outputs) and Vue (for component props), adapting to each framework&#8217;s conventions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Surround with error boundaries and loading states<\/h2>\n\n\n\n<p><strong>The problem:<\/strong> Our main dashboard content has no error boundary protection. If a component throws an error, the entire app crashes.<\/p>\n\n\n\n<p><strong>The solution:<\/strong> WebStorm&#8217;s <em>Surround with<\/em> refactoring to wrap our content with error handling.<\/p>\n\n\n\n<p>Let&#8217;s say we want to wrap our main dashboard grid with an error boundary. First, select the main dashboard content:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">&lt;div style={{\n  display: &#039;grid&#039;, \n  gridTemplateColumns: &#039;repeat(auto-fit, minmax(250px, 1fr))&#039;, \n  gap: &#039;20px&#039;, \n  marginBottom: &#039;30px&#039;\n}}&gt;\n  &lt;MetricCard ... \/&gt;\n  &lt;MetricCard ... \/&gt;\n  &lt;MetricCard ... \/&gt;\n  &lt;MetricCard ... \/&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Select the JSX content you want to protect.<\/li>\n\n\n\n<li>Hit <em>Ctrl+Alt+T\/\u2325+\u2318+T<\/em> (or right-click and select <em>Surround With<\/em>).<\/li>\n\n\n\n<li>Choose <em>Surround with &lt;tag&gt;&lt;\/tag&gt;<\/em> and type <code>&lt;ErrorBoundary fallback={&lt;div&gt;Something went wrong&lt;\/div&gt;}&gt;<\/code><\/li>\n\n\n\n<li>If <code>ErrorBoundary<\/code> doesn&#8217;t exist yet, hit <em>Alt+Enter<\/em> and select <em>Create component &#8216;ErrorBoundary&#8217;<\/em> to generate it automatically.<\/li>\n<\/ol>\n\n\n\n<p><strong>The result:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">&lt;ErrorBoundary fallback={&lt;div&gt;Something went wrong with the metrics.&lt;\/div&gt;}&gt;\n  &lt;div style={{\n    display: &#039;grid&#039;, \n    gridTemplateColumns: &#039;repeat(auto-fit, minmax(250px, 1fr))&#039;, \n    gap: &#039;20px&#039;, \n    marginBottom: &#039;30px&#039;\n  }}&gt;\n    &lt;MetricCard ... \/&gt;\n    &lt;MetricCard ... \/&gt;\n    &lt;MetricCard ... \/&gt;\n    &lt;MetricCard ... \/&gt;\n  &lt;\/div&gt;\n&lt;\/ErrorBoundary&gt;<\/pre>\n\n\n\n<p>You can also use <em>Surround with<\/em> to wrap async operations in <code>try...catch<\/code> blocks or conditional statements in <code>if\/else<\/code> logic.<\/p>\n\n\n\n<p><strong>Why this matters:<\/strong> The <em>Surround with<\/em> feature helps you consistently apply protective patterns across your codebase. No more forgetting to wrap risky operations in error boundaries or <code>try...catch<\/code> blocks. It&#8217;s also incredibly handy for adding structural elements like containers, sections, or wrapper divs to your HTML\/JSX without manually typing opening and closing tags.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Smart string refactoring<\/h2>\n\n\n\n<p><strong>The problem:<\/strong> We have string concatenation that&#8217;s harder to read and maintain. For example, in our <code>formatDate<\/code> function, we&#8217;re building a date string with multiple concatenations.<\/p>\n\n\n\n<p><strong>The solution:<\/strong> WebStorm&#8217;s smart string conversion.<\/p>\n\n\n\n<p>Let&#8217;s say you start with this string concatenation in your function:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">const formatDate = (dateString: string) =&gt; {\n  const date = new Date(dateString);\n  const months = &#091;&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;, &#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;];\n  return months&#091;date.getMonth()] + &#039; &#039; + date.getDate() + &#039;, &#039; + date.getFullYear();\n};<\/pre>\n\n\n\n<p>You want to convert this to a template literal for better readability:<\/p>\n\n\n\n<p><strong>Two approaches in WebStorm:<\/strong><\/p>\n\n\n\n<ul>\n<li><strong>Explicit conversion<\/strong>: Place the cursor on the string and hit <em>Alt+Enter\/\u2325+\u21b5<\/em>, then select <em>Convert to template string<\/em> (more efficient for this example with complex concatenation).<\/li>\n\n\n\n<li><strong>Natural conversion<\/strong>: Start typing <code>${<\/code> anywhere in the string and WebStorm automatically converts your code without interrupting your flow (fastest when you just want to add a variable to an existing string).<br><\/li>\n<\/ul>\n\n\n\n<p><strong>How to do it in WebStorm:<\/strong><\/p>\n\n\n\n<ol>\n<li>Place the cursor inside any of the string quotes (like <code>' '<\/code> between a month and date).<\/li>\n\n\n\n<li>Start typing <code>${<\/code> and WebStorm immediately converts the entire expression to use backticks.<\/li>\n\n\n\n<li>Continue building your template: <code>`${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`<\/code><br><\/li>\n<\/ol>\n\n\n\n<p><strong>Before:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">return months&#091;date.getMonth()] + &#039; &#039; + date.getDate() + &#039;, &#039; + date.getFullYear();<\/pre>\n\n\n\n<p><strong>After (automatic conversion):<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">return `${months&#091;date.getMonth()]}\/${date.getDate()}\/${date.getFullYear()}`;<\/pre>\n\n\n\n<p><strong>Honorable mention:<\/strong> That <code>months<\/code> array is also a perfect candidate for WebStorm&#8217;s <em>Introduce Constant<\/em> refactoring (<em>Ctrl+Alt+C\/\u2325+\u2318+C<\/em>). Select the array and extract it to a module-level constant for better reusability and performance.<\/p>\n\n\n\n<p><strong>Why this matters:<\/strong> You never have to manually convert strings to template literals. WebStorm recognizes when you need interpolation and handles the conversion seamlessly. This works for any string in JavaScript\/TypeScript, including function returns, variable assignments, object properties, etc.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The transformation: From AI-generated to production-ready<\/h2>\n\n\n\n<p>After applying these six refactorings, we&#8217;ve transformed our AI-generated component from a functional prototype into professional-grade code. Let&#8217;s look at what we accomplished:<\/p>\n\n\n\n<p><strong>What we started with:<\/strong><\/p>\n\n\n\n<ul>\n<li>A single 250-line component doing everything.<\/li>\n\n\n\n<li>Inline types scattered throughout the code.<\/li>\n\n\n\n<li>Generic variable names like <code>data<\/code> and <code>result<\/code>.<\/li>\n\n\n\n<li>Repeated UI patterns copy-pasted four times.<\/li>\n\n\n\n<li>String concatenation mixed with template literals.<\/li>\n\n\n\n<li>No error boundaries or component isolation.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>What we ended up with:<\/strong><\/p>\n\n\n\n<ul>\n<li>Clean, separated interfaces (<code>AnalyticsData<\/code>, <code>RecentActivity<\/code>).<\/li>\n\n\n\n<li>Descriptive variable names (<code>analyticsData<\/code>, <code>apiResponse<\/code>).<\/li>\n\n\n\n<li>Reusable <code>MetricCard<\/code> component with a clear API.<\/li>\n\n\n\n<li>Consistent string formatting using template literals.<\/li>\n\n\n\n<li>Error boundaries protecting critical sections.<\/li>\n\n\n\n<li>A focused main component that orchestrates rather than implements.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>Each refactoring took just seconds to apply, but collectively they&#8217;ve made the code exponentially more maintainable, testable, and professional. More importantly, these changes happened deterministically \u2013 there was no guesswork, no manual find-and-replace errors, just reliable transformations.<\/p>\n\n\n\n<p>This is the power of combining AI&#8217;s rapid prototyping with WebStorm&#8217;s precise refactorings. You get the best of both worlds: speed and quality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The AI + WebStorm workflow: Best of both worlds<\/h2>\n\n\n\n<p>Here&#8217;s an emerging trend we\u2019re seeing more and more in 2025:<\/p>\n\n\n\n<p>1. <strong>AI generates the initial working code<\/strong> (fast, functional, and gets you started immediately).<\/p>\n\n\n\n<p>2. <strong><a href=\"https:\/\/www.jetbrains.com\/webstorm\/\" data-type=\"link\" data-id=\"https:\/\/www.jetbrains.com\/webstorm\/\" target=\"_blank\" rel=\"noopener\">WebStorm<\/a> refactors it into production-quality code<\/strong> (deterministic, safe, and comprehensive).<\/p>\n\n\n\n<p>This combination gives you:<\/p>\n\n\n\n<ul>\n<li><strong>Speed<\/strong>: AI gets you from zero to working in minutes.<\/li>\n\n\n\n<li><strong>Quality<\/strong>: WebStorm gets you from working to maintainable.<\/li>\n\n\n\n<li><strong>Confidence<\/strong>: Deterministic refactorings mean no surprises.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why these refactorings matter more than ever<\/h2>\n\n\n\n<p>As AI becomes increasingly sophisticated at generating functional code, the ability to quickly and safely restructure that code becomes critical. AI excels at solving immediate problems and getting you started fast. WebStorm&#8217;s refactoring tools excel at taking that functional foundation and transforming it into code that&#8217;s maintainable, testable, and ready for long-term development.<\/p>\n\n\n\n<p>The future of frontend development isn&#8217;t about choosing between AI and traditional tools. It&#8217;s about AI and powerful refactoring tools like WebStorm&#8217;s working together seamlessly. Master this combination, and you&#8217;ll be building better code faster than ever before.<\/p>\n\n\n\n<p><strong>Pro tip:<\/strong> When you have that feeling that &#8220;something could be better organized&#8221; with your code but you&#8217;re not sure what to refactor, try <em>Ctrl+T\/\u2303+T <\/em>(<em>Refactor This<\/em>). It shows all available refactoring options for your current selection, helping you discover improvement opportunities you might not have considered.<\/p>\n\n\n\n<p><em>The WebStorm team<\/em><\/p>\n","protected":false},"author":1424,"featured_media":644176,"comment_status":"closed","ping_status":"closed","template":"","categories":[601],"tags":[659],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/webstorm\/640546"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/webstorm"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/types\/webstorm"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/users\/1424"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/comments?post=640546"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/webstorm\/640546\/revisions"}],"predecessor-version":[{"id":674823,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/webstorm\/640546\/revisions\/674823"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/media\/644176"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/media?parent=640546"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/categories?post=640546"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/tags?post=640546"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/cross-post-tag?post=640546"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}