32 điểm bởi xguru 2024-09-02 | 6 bình luận | Chia sẻ qua WhatsApp
  • Refactoring mã nguồn đóng vai trò quan trọng trong việc giữ cho codebase luôn khỏe mạnh
  • Tuy nhiên, refactoring sai cách có thể khiến mã trở nên phức tạp hơn và khó bảo trì hơn
  • Cần biết cách phân biệt refactoring tốt và refactoring tệ, đồng thời hiểu cách tránh những cái bẫy của refactoring tệ

Mặt tốt, mặt xấu và mặt tệ hại của refactoring

  • Trừu tượng hóa có thể tốt hoặc xấu. Điều cốt lõi là biết khi nào và áp dụng nó như thế nào
  • Bài viết giải thích một số cạm bẫy phổ biến và cách tránh chúng (đã lược bỏ mã nguồn gốc)

1. Thay đổi quá nhiều về phong cách lập trình

  • Các lập trình viên thường mắc sai lầm là thay đổi hoàn toàn phong cách viết mã trong lúc refactoring
  • Việc đưa vào thư viện mới hoặc áp dụng một phong cách viết mã hoàn toàn khác có thể ảnh hưởng xấu đến khả năng bảo trì
  • Nên cải thiện theo hướng mã mang tính thành ngữ hơn và dễ đọc hơn, nhưng không nên đưa vào một paradigm hoàn toàn mới hay các phụ thuộc bên ngoài

2. Trừu tượng hóa không cần thiết

  • Việc thêm quá nhiều lớp trừu tượng mới mà chưa hiểu mã hiện có là một vấn đề
  • Điều đó chỉ làm tăng độ phức tạp và khiến mã khó hiểu hơn
  • Nên tách logic thành các hàm nhỏ, có thể tái sử dụng, nhưng không đưa vào độ phức tạp không cần thiết

3. Thêm sự thiếu nhất quán (Inconsistency)

  • Việc cập nhật chỉ một phần của codebase để nó hoạt động theo cách hoàn toàn khác có thể gây nhầm lẫn và bực bội
  • Nếu cần đưa vào một pattern mới, tốt nhất nên đạt được sự đồng thuận của cả nhóm trước
  • Điều quan trọng là duy trì cách tiếp cận nhất quán đối với việc lấy dữ liệu trong toàn bộ ứng dụng

4. Không hiểu mã trước khi refactoring

  • Vừa học mã vừa refactoring cùng lúc là một ý tưởng không hay
  • Nó có thể tạo ra bug, làm giảm hiệu năng và loại bỏ tính năng
  • Trước khi refactoring, điều quan trọng là phải hiểu sâu mã nguồn và cải thiện nó trong khi vẫn giữ nguyên hành vi hiện có

5. Hiểu bối cảnh kinh doanh

  • Đưa ra lựa chọn kỹ thuật mà không hiểu rõ bài toán kinh doanh có thể dẫn đến thảm họa
  • Với một trang thương mại điện tử phụ thuộc nhiều vào SEO, render phía client có thể là lựa chọn tệ
  • Các cách tiếp cận render phía server như Next.js hoặc Remix có thể là lựa chọn tốt hơn về SEO và hiệu năng

6. Hợp nhất mã quá mức

  • Việc hợp nhất mã chỉ để làm nó trông "gọn gàng" trong khi hy sinh tính linh hoạt là không tốt
  • Khi trừu tượng hóa, luôn phải cân nhắc các use case mà nó phục vụ
  • Cần bảo đảm phần trừu tượng vẫn cho phép mọi khả năng mà cách triển khai ban đầu từng hỗ trợ

Refactoring đúng cách

  • Refactoring mã là cần thiết. Nhưng phải làm theo đúng cách
  • Mã của chúng ta không hoàn hảo và cần được dọn dẹp, nhưng vẫn phải giữ tính nhất quán với codebase hiện có
  • Cần refactoring sau khi đã hiểu kỹ mã, và phải chọn lựa trừu tượng hóa một cách cẩn trọng
  • Mẹo để refactoring thành công:
    • Tiến hành từng bước: thực hiện các thay đổi nhỏ, dễ quản lý thay vì viết lại toàn bộ
    • Hiểu sâu mã trước khi thực hiện những refactoring quan trọng hoặc đưa vào lớp trừu tượng mới
    • Khớp với phong cách mã hiện có: tính nhất quán là chìa khóa để bảo trì
    • Hạn chế đưa vào quá nhiều lớp trừu tượng mới: hãy giữ mọi thứ đơn giản trừ khi độ phức tạp thực sự cần thiết
    • Đặc biệt tránh thêm thư viện mới với phong cách lập trình rất khác nếu chưa có sự đồng thuận của nhóm
    • Viết test trước khi refactoring và cập nhật test trong quá trình thực hiện. Điều này giúp xác nhận rằng chức năng ban đầu vẫn được giữ nguyên
    • Các đồng nghiệp cùng có trách nhiệm nhắc nhau tuân thủ những nguyên tắc này

Công cụ và kỹ thuật để refactoring tốt hơn

  • Có thể cân nhắc sử dụng các kỹ thuật và công cụ sau để refactoring tốt hơn:

Công cụ linting

  • Sử dụng công cụ linting để áp dụng phong cách mã nhất quán và phát hiện các vấn đề tiềm ẩn
  • Có thể tự động format theo phong cách nhất quán bằng Prettier
  • Có thể dùng Eslint với plugin tùy chỉnh để kiểm tra tính nhất quán chi tiết hơn

Code review

  • Thực hiện code review kỹ lưỡng để nhận phản hồi từ đồng nghiệp trước khi merge mã đã refactoring
  • Giúp phát hiện sớm các vấn đề tiềm ẩn và bảo đảm mã đã refactoring phù hợp với tiêu chuẩn cũng như kỳ vọng của nhóm

Testing

  • Viết và chạy test để bảo đảm mã đã refactoring không làm hỏng chức năng hiện có
  • Vitest là một test runner đặc biệt nhanh, chắc chắn và dễ dùng, gần như không cần cấu hình mặc định
  • Có thể cân nhắc dùng Storybook cho visual testing
  • React Testing Library là một bộ utility tốt để kiểm thử component React (cũng có các biến thể cho Angular, v.v.)

Công cụ AI (phù hợp)

  • Hỗ trợ nỗ lực refactoring bằng các công cụ AI có thể bám sát phong cách và quy tắc viết mã hiện có
  • Visual Copilot là công cụ đặc biệt hữu ích để duy trì tính nhất quán khi lập trình frontend
    • Giúp chuyển thiết kế thành mã trong khi vẫn khớp với phong cách mã hiện có và tận dụng đúng các component cùng token của design system

Kết luận

  • Refactoring là một phần cần thiết của phát triển phần mềm, nhưng cần được thực hiện cẩn trọng cùng với sự quan tâm đến codebase hiện có và động lực trong nhóm
  • Mục tiêu của refactoring là cải thiện cấu trúc bên trong của mã mà không thay đổi hành vi bên ngoài của nó
  • Những đợt refactoring tốt nhất thường không hiển hiện với người dùng cuối, nhưng lại giúp cuộc sống của lập trình viên dễ thở hơn rất nhiều
  • Chúng cải thiện khả năng đọc, khả năng bảo trì và hiệu quả, nhưng không khiến toàn bộ hệ thống trở nên rối rắm
  • Khi muốn vạch ra một "kế hoạch lớn" cho mã, hãy lùi lại một bước để hiểu thật kỹ mã nguồn, cân nhắc tác động của thay đổi và thực hiện các cải tiến dần dần mà cả nhóm có thể trân trọng
  • Bản thân bạn trong tương lai và các đồng nghiệp sẽ biết ơn một cách tiếp cận thấu đáo nhằm giữ cho codebase sạch sẽ và có thể bảo trì

6 bình luận

 
toaonly 2024-09-04

Cũng có không ít người bắt tay vào refactoring ngay mà không có code test.
Nhìn mà cứ như đang đi dây cực kỳ nguy hiểm nên cũng thấy hồi hộp ghê, haha

 
ahwjdekf 2024-09-03

Điểm số 4, "không hiểu code trước khi refactoring", đúng là nội dung khiến tôi rất đồng cảm.

 
bichi 2024-09-02

Đó là điều quá hiển nhiên, kiểu như kiến thức nền tảng vậy haha, nhưng được tổng hợp lại như thế này nên nhìn rất dễ chịu. Tôi nghĩ rằng khi refactoring thì 100% sẽ cho ra code tốt hơn. Nếu không phải vậy thì chẳng phải là kỹ năng của tôi hôm nay đã đi lùi so với hôm qua sao?

Thấy họ tổng hợp lại những điều hiển nhiên như vậy nên có cảm giác đây là bài viết được viết ra vì ai đó đã bày ra một mớ hỗn độn quá mức khiến tác giả thật sự bực mình haha

 
kandk 2024-09-02

Đây không hẳn là cách refactoring cho tốt, mà đúng hơn là cách code giỏi một cách thực tế trong môi trường làm việc..
(Có vẻ là bài viết được viết trong lúc bực mình vì một junior vào đòi refactoring dự án legacy, làm mọi thứ trở nên phức tạp khắp nơi và tạo ra bug...)

 
savvykang 2024-09-02

Những người không phân biệt được hoặc hay nhầm lẫn giữa đơn vị công việc và đơn vị của mã nguồn, bất kể nhiều hay ít kinh nghiệm, thường sẽ làm kiểu refactoring như vậy. Cuối cùng, mã dễ sửa và có thể bảo trì là mã mà về sau người khác vào cũng phải đọc được luồng nghiệp vụ thì mới đáp ứng được, và đôi khi tôi cũng tự hỏi liệu đây có phải là điều thật sự không thể hiểu được nếu chưa từng trải nghiệm trong môi trường làm việc thực tế hay không.

 
savvykang 2024-09-02

Tôi từng gặp một trường hợp cho thấy dấu hiệu của sự trừu tượng hóa quá mức trong React: dù component bảng đã được trừu tượng hóa sẵn bởi UI framework, chỉ vì schema của hai bảng trông có vẻ giống nhau mà lại dùng inversion of control để tạo thêm một component mới chẳng có vai trò gì thì theo tôi là điều nên tránh. Khi thấy những trường hợp lạm dụng các component rỗng chỉ khoảng mười dòng với danh nghĩa áp dụng Atomic Design, tôi thực sự thấy rất bất tiện vì phải xem quá nhiều chỗ.

Quy tắc DRY vốn có ý là đừng copy-paste những đoạn code có hình thức và vai trò hoàn toàn giống hệt nhau, nhưng có lẽ kiểu gì cũng sẽ xuất hiện những người hiểu sai điều đó. Có phải vì bối cảnh như thế này mà người ta nói đừng quá sùng bái design pattern?