Viết lại phần mềm từ đầu - Nên hay Không?

Chúng ta là những người làm sản phẩm, những product-manager, coder/developer, trong sâu thẳm, chúng ta là những kiến trúc sư, những creator/builder, luôn muốn sáng tạo và đổi mới.

Bất kỳ sản phẩm nào cũng có vấn đề, sản phẩm của bạn càng được sử dụng rộng rãi bởi nhiều ngưòi thì bạn càng nhận được nhiều phản hồi về lỗi, tính năng, tốc độ, độ ổn định,… Phản ứng đầu tiên của bạn là fix lỗi, cải thiện tốc độ, v.v… nhưng làm những việc này càng nhiều, trong lòng bạn càng dấy lên một suy nghĩ “mình phải viết lại phần mềm này, nó có quá nhiều vấn đề, bất cập, sai lầm, ngay từ lúc đầu”. Khi sản phẩm bắt đầu đi vào qũy đạo ổn định, các vấn đề đã được xử lý tương đối ổn thỏa (những bạn vẫn nhìn thấy những bất cập, những rủi ro tiềm tàng về lâu dài), team dev có nhiều thời gian rảnh hơn, thì suy nghĩ này trong bạn càng lớn dần.

Ở một trường hợp khác, bạn là một người mới nhận nhiệm vụ tiếp quản sản phẩm, một đống code/system hổ lốn, đang có rất nhiều vấn đề. Suy nghĩ của bạn sau khi tìm hiểu hệ thống sẽ là gì? - “We should rebuild it from scratch!”. Có rất nhiều lý do dẫn đến hành động này:

  1. Trong đầu mỗi người luôn tồn tại một số mô hình kiến trúc mà bạn cho là đẹp, tối ưu, phù hợp với tình huống,v.v… - bạn đã hình thành định kiến sau một thời gian dài thành công với các mô hình đó. Và bạn sẽ có đủ lý luận để bảo vệ nó với… chính mình.
  2. Xét trên phương diện lập trình, với mỗi loại vấn đề (sorting, matching, formatting, communication,…) bạn đã hình thành cho mình những pattern xử lý mà bạn thấy comfortable. Đọc code của một lập trình viên khác không cùng style và quan điểm thẩm mỹ với bạn thật là một trải nghiệm chẳng thích thú gì - “Tôi không thể chịu đựng gã ngốc này thêm được nữa, thà đập hết đi viết lại từ đầu còn hơn!!”
  3. Improve một sản phẩm, phần mềm có sẵn để nó tốt dần lên theo thời gian [dài] là một trải nghiệm không mấy dễ chịu. Nhất là trong trường hợp bạn phải đảm bảo một đống các ràng buộc đã tồn tại trong phần mềm (với khách hàng hay với các sản phẩm tích hợp khác). Đập đi xây lại một cái mới thì dễ dàng, thích thú và ấn tượng hơn nhiều. Nhắc lại: evolve một sản phẩm, nhất là sản phẩm online, trong khi vẫn đảm bảo các khách hàng vẫn sử dụng được bình thường, hệ thống vẫn vận hành thông suốt, là rất không đơn giản. (Tôi sẽ viết về chủ đề này trong một bài khác)

Các lý do trên tồn tại bất kể bản thân bạn có biết đến hoặc có công nhận hay không!

Khi bạn muốn viết lại phần mềm, đặc biệt khi tuyển được những tài năng mới, bạn rất có thể đang vứt đi những tri thức quý báu ẩn trong code mà bạn đã phải trả giá rất nhiều để có được. Một flow điển hình là: khách hàng báo một lỗi rất dị –> đội Test/QA mất vài ngày để reproduce được lỗi này –> đội Dev mất từ vài giờ đến vài ngày để fix lỗi. Mỗi một lỗi được fix bạn đã phải trả giá bằng rất nhiều thời gian và tiền bạc (trả lương), thậm chí là mất đi một vài khách hàng, ít thì cũng giảm độ tin tưởng, yêu quý của họ đối với sản phẩm của bạn.

Khi sản phẩm của bạn đã đạt đến độ mature qua vài năm, việc viết lại từ đầu hầu hết sẽ trở thành thảm họa. Netscape là một ví dụ điển hình. Được dẫn dắt bởi 2 huyền thoại làng công nghệ Marc Andreessen và Ben Horowitz, Netscape đã đi đến một quyết định sai lầm là viết lại hoàn toàn bộ phần mềm Netscape 4.0 (Netscape Navigator & Netscape Enterprise Server). Điều này đã khiến cho phiên bản phần mềm tiếp theo của họ mất 3 năm để phát hành. Trong 3 năm đó thế giới Internet đã thay đổi như vũ bão, sản phẩm của họ từ dẫn đầu thị trường đã rơi xuống đáy. (đấy, huyền thoại còn sai lầm chứ cứ gì bạn đâu :D nói vậy thôi chứ lý do sụp đổ của Netscape còn vài yếu tố khác)

Ebay cũng đã rewrite lại toàn bộ vào năm 1999 và đi gần tới chỗ sụp đổ (rất ít người biết chuyện này). May thay công ty này đã gượng lại được vào đúng thời điểm.

Nhưng không phải mọi sự viết lại đều đưa sản phẩm đến chỗ sụp đổ. Twitter là một ví dụ điển hình, họ đã viết lại nhiều phần trong hệ thống từ Ruby sang Java/Scala và có một sản phẩm scale/performance tốt hơn rất nhiều. Vậy sự khác nhau ở đây là gì? Twitter viết lại từng thành phần chứ ko viết lại toàn bộ các thành phần cùng một lúc. Một lý do nữa là Twitter có engineering team lớn hơn Netscape rất nhiều - điều này giúp họ dễ dàng chia lửa: vừa tiếp tục improve/dev trên codebase hiện tại, vừa viết codebase mới.

Trên trải nghiệm cá nhân, team chúng tôi đã đi qua cả thành công và thất bại khi quyết định viết lại một cái gì đó.

Trường hợp thành công: Dashboard của VCCloud đã được viết lại toàn bộ một lần, ở phiên bản mới chúng tôi đã áp dụng nhiều công nghệ mới, đã được kiến trúc lại gồm nhiều thành phần hơn, với nhiều tính năng hơn, mang đến trải nghiệm tốt hơn. Tại sao? Đó là vị đội ngũ lập trình của chúng tôi vẫn là những người cũ, và vẫn viết bằng ngôn ngữ lập trình cũ (Ruby), framework cũ (Rails). Chúng tôi đã đi qua, nhận biết, và sửa lỗi các vấn đề của phần mềm trước đó; chúng tôi đã trưởng thành theo thời gian và thuần thục Ruby/Rails hơn rất nhiều. Ngoài ra chúng tôi tương tác với một Backend API đã ổn định, không thay đổi.

Trường hợp không thực sự thành công: viết lại UniGate - một hệ thống Gateway làm chức năng quản lý routing, load-balancing, firewalling, v.v.. Từ chỗ viết chủ yếu bằng Bash thêm vào một chút Perl/Python, tôi quyết định viết lại toàn bộ bằng Python, với thiết kế kiến trúc mới hoành tráng, hỗ trợ hàng tá tính năng với các config cực kỳ flexible, và đặc biệt là bằng những Python dev mới. Ở phiên bản trước tôi là người thiết kế hệ thống cũng đồng thời code/debug/fix toàn bộ; ở phiên bản mới, tôi làm chỉ làm thiết kế hệ thống, tính năng, cách thức implement. Điều này dẫn đến việc các dev mới code như một cái máy để implement từng function/module trong khi không hiểu tại sao lại phải làm như vậy + không hiểu về networking ở bên dưới. Kết quả là phần mềm mới nhiều tính năng hơn, linh hoạt hơn, nhưng rất nhiều lỗi và chúng tôi đã phải mất thêm rất rất nhiều thời gian để tìm và sửa lỗi.

Câu hỏi đặt ra là: vậy khi nào thì tôi nên viết lại?

Bạn chỉ nên viết lại khi hội đủ hầu hết các yếu tố sau:
  1. Những dev/tester nòng cốt của sản phẩm cũ vẫn tham gia phát triển phiên bản kế tiếp
  2. Sản phẩm của bạn đã stable và cần ít người để bảo trì
  3. Bạn có một product-manager thực sự hiểu sản phẩm viết lại của bạn cần focus vào điều gì và theo sát quá trình làm sản phẩm
  4. Sản phẩm của bạn đang dần trở nên mất kiểm soát hoặc phụ thuộc vào những yếu tố bạn không kiểm soát được
  5. Nhu cầu khách hàng của bạn không thay đổi đến mức chóng mặt, thời gian cần đưa sản-phẩm hoặc tính-năng-mới của bạn ra thị trường không quá gấp gáp.

Nếu bạn mới tuyển được các “ngôi sao” mới và nôn nóng muốn viết lại ngay thì sao? Hãy bình tĩnh và cho họ thời gian để làm quen với codebase/logic, product, quy trình, đòi hỏi của khách hàng,…

Lời cuối

Sau lần “chết hụt” do viết lại, team product tại Ebay đã áp dụng một chiến lược: vừa viết lại phiên bản mới, vừa duy trì và phát triển phiên bản hiện tại một cách đồng thời. Và thực sự họ đã viết lại toàn bộ hệ thống tối thiểu thêm 2 lần nữa, bằng một ngôn ngữ lập trình và kiến trúc hoàn toàn khác. Hẳn bạn có thể tưởng tượng được khối lượng công việc mà đội ngũ kỹ sư của Ebay đã thực hiện - hàng triệu dòng code đã được viết lại ròng rã trong hàng năm trời đồng thời với nó là việc vẫn đảm bảo phát triển các tính năng mới và duy trì hiệu năng đủ tốt cho phiên bản hiện tại; và điều quan trọng nhất là không được để ảnh hưởng tới user base hiện tại. (Nếu như bạn nghĩ kỹ hơn về việc migrate dữ liệu giữa hệ thống cũ và mới, trong khi vẫn đảm bảo hàng trăm ngàn giao dịch vẫn đang diễn ra đồng thời, bạn sẽ thấy mức độ kinh khủng của nó).

Chiến lược tốt nhất để đối phó với tình huống này chính là… đừng để bạn bị rơi vào tính huống này. Hãy dành ra tối thiểu 20% tài nguyên nhân-lực/thời-gian để làm việc rewrite, re-architect, refactor các phần có vấn đề trong codebase, thay đổi database phù hợp hơn,… ngay từ những giai đoạn đầu của dự án - Đây cũng là một lời khuyên của product-guru Marty Cagan.

~ End ~

Tôi xin list thêm một số bài viết liên quan, phòng khi bạn muốn tìm hiểu kỹ hơn về phương diện kỹ thuật của việc rewrite code, cũng như các ý kiến đa chiều khác:

http://www.joelonsoftware.com/articles/fog0000000069.html

http://programmingisterrible.com/post/73023853878/getting-away-with-rewriting-code-from-scratch

http://www.webnodes.com/a-total-rewrite-costly-time-consuming-but-worth-it

http://onstartups.com/tabid/3339/bid/97052/How-To-Survive-a-Ground-Up-Rewrite-Without-Losing-Your-Sanity.aspx

http://steveblank.com/2011/01/25/startup-suicide-%E2%80%93-rewriting-the-code/

http://jeffmagnusson.com/rewrite-and-die/