Refactoring với Copilot
1. Refactoring là gì?
Refactoring = Cải thiện code structure mà không thay đổi behavior
Tại sao cần Refactoring?
| Before | After |
|---|---|
| Code khó đọc | Clear, readable |
| Duplicate code | DRY (Don't Repeat Yourself) |
| Long functions | Small, focused functions |
| Complex conditions | Simple, clear logic |
| Hard to test | Easy to test |
| Hard to extend | Easy to extend |
Khi nào nên Refactor?
Refactoring Triggers
2. Code Smells và Cách Fix
Common Code Smells
Code Smells
Bloaters
Long Method
Large Class
Long Parameter List
OO Abuse
Switch Statements
Refused Bequest
Change Preventers
Divergent Change
Shotgun Surgery
Dispensables
Dead Code
Duplicate Code
3. Copilot Refactoring Commands
3.1 Quick Refactors
| Command | Mô tả |
|---|---|
/simplify | Đơn giản hóa code phức tạp |
/fix | Sửa issues và improve |
| Inline Chat | "Refactor this to..." |
3.2 Using /simplify
Python
1# Original - Complex nested conditions2def get_discount(user, cart):3 if user.is_premium:4 if cart.total > 100:5 if cart.has_coupon:6 return 0.257 else:8 return 0.159 else:10 if cart.has_coupon:11 return 0.1012 else:13 return 0.0514 else:15 if cart.total > 100:16 if cart.has_coupon:17 return 0.1018 else:19 return 0.0520 else:21 if cart.has_coupon:22 return 0.0523 else:24 return 02526# Select và dùng /simplifyCopilot Output:
Python
1def get_discount(user, cart):2 base_discount = 0.05 if cart.total > 100 else 03 premium_bonus = 0.10 if user.is_premium else 04 coupon_bonus = 0.05 if cart.has_coupon else 05 6 return base_discount + premium_bonus + coupon_bonus4. Extract Method Refactoring
When to Extract
- Function > 20 lines
- Code block có specific purpose
- Duplicate code blocks
Example
Before:
JavaScript
1async function processOrder(order) {2 // Validate order3 if (!order.items || order.items.length === 0) {4 throw new Error('Empty order');5 }6 if (!order.customer || !order.customer.email) {7 throw new Error('Invalid customer');8 }9 if (!order.shippingAddress || !order.shippingAddress.city) {10 throw new Error('Invalid shipping address');11 }12 13 // Calculate totals14 let subtotal = 0;15 for (const item of order.items) {16 subtotal += item.price * item.quantity;17 }18 const tax = subtotal * 0.1;19 const shipping = subtotal > 100 ? 0 : 10;20 const total = subtotal + tax + shipping;21 22 // Save to database23 const savedOrder = await db.orders.create({24 ...order,25 subtotal,26 tax,27 shipping,28 total,29 status: 'pending'30 });31 32 // Send confirmation email33 await emailService.send({34 to: order.customer.email,35 subject: 'Order Confirmation',36 body: `Your order #${savedOrder.id} has been placed.`37 });38 39 return savedOrder;40}Prompt:
Text
1Refactor this function by extracting separate methods for:2- Validation3- Calculating totals4- Saving to database5- Sending email6Keep the main function as an orchestrator.After:
JavaScript
1function validateOrder(order) {2 if (!order.items?.length) {3 throw new Error('Empty order');4 }5 if (!order.customer?.email) {6 throw new Error('Invalid customer');7 }8 if (!order.shippingAddress?.city) {9 throw new Error('Invalid shipping address');10 }11}1213function calculateOrderTotals(items) {14 const subtotal = items.reduce(15 (sum, item) => sum + item.price * item.quantity, 16 017 );18 const tax = subtotal * TAX_RATE;19 const shipping = subtotal > FREE_SHIPPING_THRESHOLD ? 0 : SHIPPING_COST;20 21 return { subtotal, tax, shipping, total: subtotal + tax + shipping };22}2324async function saveOrder(order, totals) {25 return db.orders.create({26 ...order,27 ...totals,28 status: 'pending'29 });30}3132async function sendOrderConfirmation(email, orderId) {33 await emailService.send({34 to: email,35 subject: 'Order Confirmation',36 body: `Your order #${orderId} has been placed.`37 });38}3940// Main orchestrator - clean and readable41async function processOrder(order) {42 validateOrder(order);43 const totals = calculateOrderTotals(order.items);44 const savedOrder = await saveOrder(order, totals);45 await sendOrderConfirmation(order.customer.email, savedOrder.id);46 return savedOrder;47}5. Replace Conditional with Polymorphism
Before - Switch Statement
Python
1def calculate_shipping(order_type, weight):2 if order_type == 'standard':3 return weight * 2.54 elif order_type == 'express':5 return weight * 5.0 + 106 elif order_type == 'overnight':7 return weight * 8.0 + 258 elif order_type == 'international':9 return weight * 15.0 + 5010 else:11 raise ValueError(f"Unknown order type: {order_type}")Prompt
Text
1Refactor this to use polymorphism with a ShippingStrategy patternAfter - Strategy Pattern
Python
1from abc import ABC, abstractmethod23class ShippingStrategy(ABC):4 @abstractmethod5 def calculate(self, weight: float) -> float:6 pass78class StandardShipping(ShippingStrategy):9 def calculate(self, weight: float) -> float:10 return weight * 2.51112class ExpressShipping(ShippingStrategy):13 def calculate(self, weight: float) -> float:14 return weight * 5.0 + 101516class OvernightShipping(ShippingStrategy):17 def calculate(self, weight: float) -> float:18 return weight * 8.0 + 251920class InternationalShipping(ShippingStrategy):21 def calculate(self, weight: float) -> float:22 return weight * 15.0 + 502324# Factory25SHIPPING_STRATEGIES = {26 'standard': StandardShipping,27 'express': ExpressShipping,28 'overnight': OvernightShipping,29 'international': InternationalShipping,30}3132def calculate_shipping(order_type: str, weight: float) -> float:33 strategy_class = SHIPPING_STRATEGIES.get(order_type)34 if not strategy_class:35 raise ValueError(f"Unknown order type: {order_type}")36 return strategy_class().calculate(weight)6. Remove Duplicate Code
Identify Duplicates
Text
1@workspace find duplicate code patterns in src/Example
Before - Duplicate validation:
Python
1# In user_service.py2def create_user(data):3 if not data.get('email'):4 raise ValueError('Email required')5 if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', data['email']):6 raise ValueError('Invalid email format')7 # ... create user89# In order_service.py 10def create_order(data):11 if not data.get('email'):12 raise ValueError('Email required')13 if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', data['email']):14 raise ValueError('Invalid email format')15 # ... create orderPrompt:
Text
1Extract duplicate email validation into a reusable utilityAfter:
Python
1# validators.py2import re34class ValidationError(Exception):5 pass67def validate_email(email: str) -> str:8 """Validate email format and return cleaned email."""9 if not email:10 raise ValidationError('Email required')11 12 email = email.strip().lower()13 if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email):14 raise ValidationError('Invalid email format')15 16 return email1718# user_service.py19from validators import validate_email2021def create_user(data):22 data['email'] = validate_email(data.get('email'))23 # ... create user2425# order_service.py26from validators import validate_email2728def create_order(data):29 data['email'] = validate_email(data.get('email'))30 # ... create order7. Modernize Code
Update Syntax
Prompt:
Text
1Modernize this code to use ES2023+ features:2- Optional chaining3- Nullish coalescing4- Array methods5- Template literalsBefore:
JavaScript
1function getUserInfo(user) {2 var name = 'Anonymous';3 if (user && user.profile && user.profile.name) {4 name = user.profile.name;5 }6 7 var roles = [];8 if (user && user.roles) {9 for (var i = 0; i < user.roles.length; i++) {10 roles.push(user.roles[i].toUpperCase());11 }12 }13 14 return 'User: ' + name + ', Roles: ' + roles.join(', ');15}After:
JavaScript
1const getUserInfo = (user) => {2 const name = user?.profile?.name ?? 'Anonymous';3 const roles = user?.roles?.map(role => role.toUpperCase()) ?? [];4 5 return `User: ${name}, Roles: ${roles.join(', ')}`;6};Convert Callbacks to Async/Await
Prompt:
Text
1Convert this callback-based code to async/awaitBefore:
JavaScript
1function loadUserData(userId, callback) {2 getUser(userId, (err, user) => {3 if (err) return callback(err);4 5 getOrders(user.id, (err, orders) => {6 if (err) return callback(err);7 8 getPayments(user.id, (err, payments) => {9 if (err) return callback(err);10 11 callback(null, { user, orders, payments });12 });13 });14 });15}After:
JavaScript
1async function loadUserData(userId) {2 const user = await getUser(userId);3 const [orders, payments] = await Promise.all([4 getOrders(user.id),5 getPayments(user.id)6 ]);7 8 return { user, orders, payments };9}8. Safe Refactoring Workflow
Step-by-Step Process
markdown
11. **Ensure Test Coverage**2 - Run existing tests3 - Add tests if missing4 - Tests = Safety net5 62. **Make Small Changes**7 - One refactor at a time8 - Commit after each9 - Easy to rollback10 113. **Run Tests After Each Change**12 - All tests must pass13 - No behavior change14 154. **Code Review**16 - Use Copilot to review17 - Check for edge casesRollback Strategy
Bash
1# If something breaks2git stash # Save current changes3git checkout . # Restore last commit4# OR5git revert HEAD # Revert last commit9. Large-Scale Refactoring
Using Agent Mode
Với refactoring lớn, sử dụng Agent mode:
Text
1Refactor the authentication system:2 3Current structure:4- auth.py (500 lines, does everything)5 6Target structure:7- auth/8 - __init__.py9 - models.py (User, Session, Token models)10 - services.py (AuthService class)11 - handlers.py (Route handlers)12 - utils.py (Helper functions)13 - exceptions.py (Custom exceptions)14 15Requirements:16- Split the monolithic file17- Keep all functionality working18- Maintain backward compatibility for imports19- Update all files that import from auth.pyMigration Pattern
Prompt cho migration:
Text
1I want to migrate from [OLD_PATTERN] to [NEW_PATTERN].2 3Current usage in codebase:4- File A uses OLD_PATTERN at lines X, Y, Z5- File B uses OLD_PATTERN at lines ...6 7Create a migration plan:81. Create NEW_PATTERN92. Update usages one by one103. Deprecate OLD_PATTERN114. Remove OLD_PATTERN10. Refactoring Exercises
Exercise 1: Long Method
Refactor this function:
Python
1def process_data(data):2 # Validate3 if not data:4 return None5 if not isinstance(data, list):6 data = [data]7 valid_items = []8 for item in data:9 if item and isinstance(item, dict) and 'value' in item:10 valid_items.append(item)11 if not valid_items:12 return None13 14 # Transform15 transformed = []16 for item in valid_items:17 new_item = {}18 new_item['id'] = item.get('id', str(uuid.uuid4()))19 new_item['value'] = float(item['value'])20 new_item['timestamp'] = item.get('timestamp', datetime.now().isoformat())21 new_item['category'] = item.get('category', 'default').lower()22 transformed.append(new_item)23 24 # Aggregate25 total = sum(item['value'] for item in transformed)26 avg = total / len(transformed)27 by_category = {}28 for item in transformed:29 cat = item['category']30 if cat not in by_category:31 by_category[cat] = []32 by_category[cat].append(item)33 34 return {35 'items': transformed,36 'total': total,37 'average': avg,38 'by_category': by_category,39 'count': len(transformed)40 }Goal: Extract into validate(), transform(), aggregate() functions.
Exercise 2: Duplicate Code
Tìm và loại bỏ duplicate:
JavaScript
1// In productService.js2async function searchProducts(query) {3 const results = await db.products.find({ name: new RegExp(query, 'i') });4 return results.map(p => ({5 id: p._id,6 name: p.name,7 price: p.price,8 image: p.images[0] || '/placeholder.jpg'9 }));10}1112// In categoryService.js13async function getProductsByCategory(categoryId) {14 const results = await db.products.find({ categoryId });15 return results.map(p => ({16 id: p._id,17 name: p.name,18 price: p.price,19 image: p.images[0] || '/placeholder.jpg'20 }));21}2223// In recommendationService.js24async function getRecommendations(userId) {25 const results = await getRecommendedProducts(userId);26 return results.map(p => ({27 id: p._id,28 name: p.name,29 price: p.price,30 image: p.images[0] || '/placeholder.jpg'31 }));32}Goal: Extract formatProduct() function.
11. Refactoring Checklist
markdown
1## Pre-Refactoring2- [ ] Understand the code fully3- [ ] Have tests covering current behavior4- [ ] Create a branch for refactoring5- [ ] Define the goal clearly6 7## During Refactoring8- [ ] Make one change at a time9- [ ] Run tests after each change10- [ ] Commit frequently11- [ ] Use meaningful commit messages12 13## Post-Refactoring14- [ ] All tests pass15- [ ] No functionality changed16- [ ] Code is cleaner/simpler17- [ ] Code review completed18- [ ] Documentation updated if neededTiếp theo
Bài tiếp theo: Testing với Copilot - generate unit tests, integration tests, và improve test coverage với AI!
