{"product_id":"refactoring-codebase-lớn-với-claude-code-từ-legacy-dến-modern-architecture","title":"Refactoring codebase lớn với Claude Code — Từ legacy đến modern architecture","description":"\n\u003cp\u003eMọi codebase đủ tuổi đều tích lũy technical debt. Những quyết định hợp lý ở thời điểm startup trở thành gánh nặng khi team mở rộng và sản phẩm scale. Refactoring codebase lớn luôn là công việc đáng sợ nhất với developer: rủi ro break production cao, khó estimate thời gian, và thường bị deprioritize cho đến khi quá muộn. Claude Code thay đổi phương trình này bằng khả năng đọc hiểu codebase rộng, lên chiến lược refactoring có cấu trúc, và thực hiện thay đổi an toàn với test coverage.\u003c\/p\u003e\n\n\u003ch2\u003eKhi nào cần refactor?\u003c\/h2\u003e\n\u003cp\u003eKhông phải mọi code cũ đều cần refactor. Nhận diện các tín hiệu sau để quyết định khi nào refactoring là cần thiết:\u003c\/p\u003e\n\n\u003ch3\u003eCode smells -- dấu hiệu code cần refactor\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eGod objects\/files:\u003c\/strong\u003e File trên 500 dòng, class làm quá nhiều thứ. Ví dụ: \u003ccode\u003eUserController.js\u003c\/code\u003e vừa xử lý authentication, vừa quản lý profile, vừa handle payments.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eDuplicate code:\u003c\/strong\u003e Copy-paste logic xuất hiện ở 3+ nơi. Mỗi lần sửa bug phải sửa ở nhiều chỗ.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTight coupling:\u003c\/strong\u003e Thay đổi ở module A bắt buộc phải sửa module B, C, D. Deploy một feature đơn giản cần test toàn bộ hệ thống.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eOutdated patterns:\u003c\/strong\u003e Callback hell, var thay vì const\/let, class components thay vì hooks, string SQL queries thay vì ORM.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMissing abstractions:\u003c\/strong\u003e Business logic nằm trực tiếp trong route handlers, database queries rải rác khắp nơi thay vì tập trung trong repository layer.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTest difficulty:\u003c\/strong\u003e Không thể viết unit test cho một function vì nó phụ thuộc trực tiếp vào database, file system, hoặc external API.\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eTechnical debt signals\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Yêu cầu Claude Code đánh giá technical debt\nĐọc toàn bộ codebase trong thư mục \/src và phân tích:\n\n1. Files nào có LOC cao nhất? (potential god objects)\n2. Có patterns nào bị duplicate nhiều lần?\n3. Dependency graph: modules nào coupled chặt nhất?\n4. Có outdated patterns nào cần modernize?\n5. Test coverage hiện tại? Files nào thiếu tests?\n6. Có circular dependencies không?\n\nTạo báo cáo technical debt với severity levels\n(Critical \/ High \/ Medium \/ Low) và effort estimate.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eClaude Code Plan Mode cho chiến lược refactoring\u003c\/h2\u003e\n\u003cp\u003eTrước khi sửa bất kỳ dòng code nào, dùng Plan Mode để lên chiến lược:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Trong Claude Code, bật Plan Mode\nTôi muốn refactor codebase Express.js hiện tại sang NestJS.\n\nCodebase hiện tại:\n- 45 route files trong \/routes\n- 30 middleware files\n- 15 database models (Mongoose)\n- 0% test coverage\n- Authentication dùng passport.js\n- File upload dùng multer\n- Caching dùng node-cache (in-memory)\n\nYêu cầu:\n- Không downtime trong quá trình migration\n- Cả Express và NestJS cần chạy song song trong transition period\n- Migration phải incremental, không big-bang\n- Mỗi phase phải có rollback plan\n\nLên kế hoạch refactoring chi tiết với phases,\ntimeline estimate, và risk assessment.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eClaude Code Plan Mode sẽ phân tích codebase và đưa ra kế hoạch nhiều phase:\u003c\/p\u003e\n\n\u003ch3\u003ePhase 1: Foundation (Tuần 1-2)\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eSetup NestJS project cạnh Express project\u003c\/li\u003e\n  \u003cli\u003eCấu hình shared database connections\u003c\/li\u003e\n  \u003cli\u003eSetup proxy để route traffic giữa Express và NestJS\u003c\/li\u003e\n  \u003cli\u003eViết integration tests cho critical paths hiện tại (trước khi thay đổi code)\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003ePhase 2: Migrate theo feature (Tuần 3-8)\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eMigrate từng nhóm routes: auth -\u0026gt; users -\u0026gt; products -\u0026gt; orders\u003c\/li\u003e\n  \u003cli\u003eMỗi nhóm: viết NestJS module -\u0026gt; test -\u0026gt; switch proxy -\u0026gt; verify -\u0026gt; remove Express code\u003c\/li\u003e\n  \u003cli\u003eRollback: switch proxy ngược lại nếu có issue\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003ePhase 3: Modernize patterns (Tuần 9-10)\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eThay node-cache bằng Redis\u003c\/li\u003e\n  \u003cli\u003eThêm validation pipes\u003c\/li\u003e\n  \u003cli\u003eImplement proper error handling\u003c\/li\u003e\n  \u003cli\u003eAdd comprehensive test coverage\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003ePhase 4: Cleanup (Tuần 11-12)\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eRemove Express code\u003c\/li\u003e\n  \u003cli\u003eRemove proxy layer\u003c\/li\u003e\n  \u003cli\u003eFinal testing\u003c\/li\u003e\n  \u003cli\u003eDocumentation update\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eFile-by-file vs Big-bang approach\u003c\/h2\u003e\n\n\u003ch3\u003eFile-by-file (Strangler Fig Pattern)\u003c\/h3\u003e\n\u003cp\u003eRefactor từng file\/module một, giữ hệ thống chạy liên tục. Ưu tiên cho production systems.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho file-by-file refactoring\nRefactor file \/src\/routes\/users.js theo kế hoạch:\n\n1. Đọc file hiện tại và liệt kê tất cả responsibilities\n2. Tách thành:\n   - \/src\/modules\/users\/users.controller.ts (route handlers)\n   - \/src\/modules\/users\/users.service.ts (business logic)\n   - \/src\/modules\/users\/users.repository.ts (database queries)\n   - \/src\/modules\/users\/dto\/create-user.dto.ts (validation)\n3. Viết tests cho users.service.ts\n4. Đảm bảo tất cả existing API endpoints vẫn hoạt động\n\nLàm từng bước, chạy tests sau mỗi bước.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBig-bang (chỉ khi không có production traffic)\u003c\/h3\u003e\n\u003cp\u003eViết lại toàn bộ rồi switch. Chỉ phù hợp cho internal tools hoặc pre-launch projects.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho big-bang refactoring\nViết lại toàn bộ \/src\/api theo clean architecture:\n\nCấu trúc mới:\n\/src\n  \/domain (entities, value objects, interfaces)\n  \/application (use cases, DTOs)\n  \/infrastructure (database, external APIs)\n  \/presentation (controllers, middleware)\n\nMapping từ code cũ:\n- Mỗi route handler -\u0026gt; controller + use case\n- Database queries -\u0026gt; repository implementations\n- Business rules -\u0026gt; domain entities\n\nViết tất cả tests trước, rồi implement.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eMaintaining tests during refactoring\u003c\/h2\u003e\n\u003cp\u003eTests là safety net quan trọng nhất khi refactoring. Có hai chiến lược:\u003c\/p\u003e\n\n\u003ch3\u003eChiến lược 1: Viết characterization tests trước khi refactor\u003c\/h3\u003e\n\u003cp\u003eCharacterization tests ghi lại behavior hiện tại, kể cả bugs. Mục đích là đảm bảo refactoring không thay đổi behavior, chỉ thay đổi structure.\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt viết characterization tests\nĐọc file \/src\/routes\/orders.js và viết characterization tests:\n\n1. Với mỗi route handler, viết test cho:\n   - Happy path (request hợp lệ, response đúng format)\n   - Error cases (validation errors, not found, unauthorized)\n   - Edge cases đặc biệt trong code hiện tại\n\n2. Dùng supertest cho API testing\n3. Mock database calls\n4. Mục đích: capture behavior HIỆN TẠI, kể cả quirks\n\nKHÔNG sửa code. KHÔNG fix bugs. Chỉ viết tests mô tả\nchính xác code hiện tại hoạt động như thế nào.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eChiến lược 2: Adapter pattern -- giữ interface, đổi implementation\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho adapter pattern\nTôi đang chuyển từ Mongoose sang Prisma.\nViết adapter để giữ interface không đổi:\n\n1. Tạo interface IUserRepository với methods hiện tại\n2. Implement MongooseUserRepository (code hiện tại)\n3. Implement PrismaUserRepository (code mới)\n4. Dùng dependency injection để switch giữa hai implementations\n5. Chạy cùng tests với cả hai implementations\n\nKhi PrismaUserRepository pass tất cả tests,\nswitch default implementation sang Prisma.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eDatabase migration trong refactoring\u003c\/h2\u003e\n\u003cp\u003eDatabase migration là phần rủi ro cao nhất của refactoring. Claude Code giúp lên kế hoạch migration an toàn:\u003c\/p\u003e\n\n\u003ch3\u003eSchema migration\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho database migration\nTôi cần migrate database schema:\n\nSchema hiện tại (MongoDB):\nusers: { name, email, address: String, phone: String }\norders: { userId, items: Array, total: Number, status: String }\n\nSchema mới (PostgreSQL):\nusers: { id, name, email }\nuser_profiles: { user_id, address, phone }\norders: { id, user_id, status, total }\norder_items: { id, order_id, product_id, quantity, price }\n\nLên kế hoạch migration:\n1. Migration script đọc từ MongoDB, ghi vào PostgreSQL\n2. Xử lý data transformation (embedded -\u0026gt; normalized)\n3. Validation: so sánh data sau migration\n4. Dual-write period: ghi cả hai DB\n5. Cutover plan: switch reads sang PostgreSQL\n6. Rollback plan nếu có issues\n\nViết migration script với error handling và progress logging.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eZero-downtime database migration\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho zero-downtime migration\nTôi cần thêm cột \"status\" vào bảng orders (PostgreSQL,\n5 triệu rows). Yêu cầu zero-downtime.\n\nKế hoạch 4 bước:\n1. ALTER TABLE ADD COLUMN với DEFAULT value (nhanh, không lock)\n2. Backfill data cho existing rows (batch processing)\n3. Deploy code mới sử dụng cột \"status\"\n4. Cleanup: remove old column nếu cần\n\nViết migration scripts cho mỗi bước.\nEstimate thời gian cho bước 2 với batch size 10,000.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eAPI versioning trong refactoring\u003c\/h2\u003e\n\u003cp\u003eKhi refactoring API, cần đảm bảo clients hiện tại không bị break:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho API versioning\nTôi đang refactor API và cần thay đổi response format:\n\nHiện tại (v1):\nGET \/api\/users\/123\nResponse: { \"id\": 123, \"fullName\": \"Nguyen Van A\", \"addr\": \"...\" }\n\nMới (v2):\nGET \/api\/v2\/users\/123\nResponse: {\n  \"data\": {\n    \"id\": 123,\n    \"name\": { \"first\": \"Van A\", \"last\": \"Nguyen\" },\n    \"address\": { \"street\": \"...\", \"city\": \"...\", \"country\": \"...\" }\n  },\n  \"meta\": { \"version\": \"2.0\" }\n}\n\nYêu cầu:\n1. Cả v1 và v2 cùng chạy trong transition period (3 tháng)\n2. v1 response được tạo từ cùng data source với v2\n3. Deprecation headers cho v1\n4. Migration guide cho clients\n5. Monitoring: track usage v1 vs v2\n\nImplement NestJS versioning với cả hai versions.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eVí dụ thực tế: Migration Express sang NestJS\u003c\/h2\u003e\n\u003cp\u003eDưới đây là walkthrough chi tiết cho một module cụ thể:\u003c\/p\u003e\n\n\u003ch3\u003eBước 1: Đọc và phân tích Express code hiện tại\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eĐọc file \/src\/routes\/products.js và phân tích:\n1. Liệt kê tất cả endpoints (method, path, handler)\n2. Dependencies (database, external services, middleware)\n3. Business logic embedded trong route handlers\n4. Error handling patterns\n5. Authentication\/authorization checks\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 2: Viết tests cho Express code\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eViết integration tests cho \/src\/routes\/products.js:\n- Cover tất cả endpoints\n- Test authentication (authorized vs unauthorized)\n- Test validation (valid vs invalid input)\n- Test error responses\n- Dùng supertest, mock database\n\nMục đích: tests này sẽ được reuse cho NestJS version.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 3: Scaffold NestJS module\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eTạo NestJS products module:\n- products.module.ts\n- products.controller.ts (exact same endpoints as Express)\n- products.service.ts (business logic extracted from handlers)\n- products.repository.ts (database queries)\n- dto\/create-product.dto.ts\n- dto\/update-product.dto.ts\n\nImplement đầy đủ dựa trên logic trong Express code.\nEndpoints phải trả về exact same response format.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 4: Chạy tests trên NestJS\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eAdapt integration tests để chạy trên NestJS TestingModule.\nTất cả tests phải pass với NestJS implementation.\nFix bất kỳ discrepancy nào giữa Express và NestJS behavior.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 5: Setup proxy routing\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eCấu hình nginx\/API gateway để route:\n- \/api\/products\/* -\u0026gt; NestJS (new)\n- \/api\/* -\u0026gt; Express (old, remaining routes)\n\nImplement health check cho cả hai services.\nSetup monitoring để so sánh response times và error rates.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 6: Deploy và verify\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eTriển khai theo quy trình:\n1. Deploy NestJS service (không nhận traffic)\n2. Run smoke tests trực tiếp vào NestJS\n3. Switch 10% traffic sang NestJS (canary)\n4. Monitor 24h: error rate, latency, response correctness\n5. Nếu OK, switch 100% traffic\n6. Keep Express code 1 tuần cho rollback nếu cần\n7. Remove Express products routes\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eRefactoring patterns phổ biến\u003c\/h2\u003e\n\n\u003ch3\u003eExtract Service pattern\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt\nFile \/src\/controllers\/orderController.js có 800 dòng.\nExtract business logic thành OrderService:\n\n1. Identify tất cả business logic (validation, calculation,\n   state transitions) trong controller\n2. Tạo OrderService class với methods cho mỗi business operation\n3. Controller chỉ còn: parse request -\u0026gt; call service -\u0026gt; format response\n4. Service nhận plain objects, không biết về req\/res\n5. Viết unit tests cho service (không cần HTTP)\n6. Chạy existing integration tests để verify\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eReplace Callbacks with Async\/Await\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt\nRefactor file \/src\/services\/paymentService.js:\nChuyển tất cả callback patterns sang async\/await.\n\nVí dụ hiện tại:\nfunction processPayment(orderId, callback) {\n  db.findOrder(orderId, function(err, order) {\n    if (err) return callback(err);\n    gateway.charge(order.total, function(err, result) {\n      if (err) return callback(err);\n      db.updateOrder(orderId, { paid: true }, function(err) {\n        callback(err, result);\n      });\n    });\n  });\n}\n\nChuyển thành async\/await, thêm proper error handling\nvới try\/catch, và đảm bảo tất cả callers cũng được update.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eDependency Injection\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt\nHiện tại code import trực tiếp dependencies:\n\nconst db = require(\".\/database\");\nconst emailService = require(\".\/emailService\");\nconst logger = require(\".\/logger\");\n\nfunction createUser(data) {\n  const user = db.insert(\"users\", data);\n  emailService.send(user.email, \"Welcome!\");\n  logger.info(\"User created\", user.id);\n  return user;\n}\n\nRefactor sang dependency injection:\n1. Function nhận dependencies qua parameters hoặc constructor\n2. Tạo composition root nơi wire dependencies\n3. Viết unit tests với mock dependencies\n4. Existing behavior không đổi\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eMonitoring refactoring progress\u003c\/h2\u003e\n\u003cpre\u003e\u003ccode\u003e# Prompt cho tracking progress\nTạo refactoring scorecard cho codebase:\n\nMetrics cần track:\n1. Files đã migrate: X\/45 routes\n2. Test coverage: trước vs sau\n3. Lines of code: trước vs sau\n4. Cyclomatic complexity: trước vs sau\n5. Dependencies: circular deps count\n6. Build time: trước vs sau\n7. API response time: trước vs sau\n\nTạo script chạy hàng tuần, output ra markdown table.\nSo sánh với baseline (trước refactoring).\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003ePitfalls cần tránh khi refactoring\u003c\/h2\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRefactor quá nhiều cùng lúc:\u003c\/strong\u003e Mỗi PR nên chỉ refactor một concern. Review dễ hơn, rollback dễ hơn, risk thấp hơn.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRefactor mà không có tests:\u003c\/strong\u003e Viết characterization tests trước. Không có tests nghĩa là không có safety net.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eThay đổi behavior khi refactoring:\u003c\/strong\u003e Refactoring chỉ thay đổi structure, không thay đổi behavior. Nếu muốn fix bug hoặc thêm feature, làm trong PR riêng.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eKhông communicate với team:\u003c\/strong\u003e Refactoring ảnh hưởng đến mọi người. Thông báo trước, coordinate timing, và cập nhật documentation.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBỏ qua database migration risks:\u003c\/strong\u003e Schema changes cần planning kỹ nhất. Luôn có rollback plan và test trên copy of production data.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eBig-bang rewrite cho production system:\u003c\/strong\u003e Trừ khi system rất nhỏ hoặc không có users, luôn dùng incremental approach. Lịch sử phát triển phần mềm đầy rẫy big-bang rewrites thất bại.\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eClaude Code workflow cho refactoring hàng ngày\u003c\/h2\u003e\n\u003cpre\u003e\u003ccode\u003e# CLAUDE.md section cho refactoring workflow\n\n## Refactoring Guidelines\n- Trước khi refactor: chạy tests, commit code hiện tại\n- Mỗi refactoring step: một commit riêng với message rõ ràng\n- Sau mỗi step: chạy tests, verify không break\n- Nếu tests fail: revert về commit trước, phân tích lại\n- Code review: yêu cầu Claude Code review refactored code\n  so với original, xác nhận behavior preserved\n- Documentation: update comments và docs cho code mới\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTổng kết\u003c\/h2\u003e\n\u003cp\u003eRefactoring codebase lớn là marathon, không phải sprint. Claude Code làm mỗi bước nhanh hơn và an toàn hơn, nhưng chiến lược tổng thể vẫn phụ thuộc vào quyết định của bạn: refactor cái gì trước, đến mức nào, và khi nào dừng. Sử dụng Plan Mode để lên chiến lược, file-by-file approach để giảm risk, characterization tests để đảm bảo safety, và incremental deployment để kiểm soát impact. Codebase hoàn hảo không tồn tại, nhưng codebase có thể maintain, test, và mở rộng là mục tiêu hoàn toàn khả thi với Claude Code.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47730151162068,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/refactoring-codebase-l_n-v_i-claude-code-t_-legacy-d_n-modern-architecture.jpg?v=1774715563","url":"https:\/\/claude.vn\/products\/refactoring-codebase-l%e1%bb%9bn-v%e1%bb%9bi-claude-code-t%e1%bb%ab-legacy-d%e1%ba%bfn-modern-architecture","provider":"CLAUDE.VN","version":"1.0","type":"link"}