Có nên cập nhật mã cũ hơn để sử dụng các cấu trúc ngôn ngữ mới hơn, hoặc các cấu trúc lỗi thời có nên bị mắc kẹt không?


15

Tôi muốn thực hiện một số cải tiến trong một số mã chức năng vẫn được viết từ lâu, trước khi ngôn ngữ lập trình được viết bằng các tính năng phát triển. Về lý thuyết, toàn bộ dự án sử dụng phiên bản cập nhật của ngôn ngữ; tuy nhiên, mô-đun cụ thể này (và trên thực tế, nhiều mô-đun khác) vẫn được viết theo phương ngữ cũ.

Tôi có nên:

  • Không chạm vào các phần của mã mà tôi không phải chạm vào, nhưng hãy viết bản vá của mình bằng cách sử dụng các tính năng ngôn ngữ mới giúp viết bản vá dễ dàng hơn nhưng không được sử dụng trong các tình huống tương tự ở bất kỳ nơi nào khác trong mô-đun? (Đây là giải pháp tôi trực giác chọn.)
  • Bỏ qua thực tế là nhiều năm đã trôi qua, và phản ánh phong cách được sử dụng trong phần còn lại của mã trong khi viết bản vá của tôi, hành xử có hiệu quả như thể tôi đang làm nhiệm vụ tương tự nhiều năm trước? (Giải pháp này theo trực giác tôi cho là ngớ ngẩn, nhưng với số lượng ồn ào mà mọi người nói về mã nguồn tốt, điều này làm cho việc giữ sự nhất quán bằng mọi giá, có lẽ đây là điều tôi nên làm.)
  • Cập nhật toàn bộ mô-đun để sử dụng các cấu trúc và quy ước ngôn ngữ mới hơn? (Đây có lẽ là giải pháp tốt nhất, nhưng có thể cần nhiều thời gian và năng lượng có thể tốt hơn dành cho một nhiệm vụ khác.)

1
@JerryCoffin Tôi sẽ không tiếp tục tranh luận trong các bình luận: Tôi đã đăng trên meta nơi chúng ta có thể có một cuộc thảo luận thích hợp.

Câu trả lời:


10

Không thể đưa ra một câu trả lời dứt khoát cho câu hỏi này, bởi vì nó phụ thuộc quá nhiều vào các chi tiết của tình huống.

Tính nhất quán trong phong cách của codebase rất quan trọng vì nó giúp làm cho mã dễ hiểu hơn, đây là một trong những khía cạnh quan trọng nhất của khả năng bảo trì.
Bạn sẽ không phải là lập trình viên đầu tiên bị nguyền rủa xuống địa ngục vì làm cho mã khó hiểu bằng cách trộn các phong cách khác nhau. Nó thậm chí có thể là chính bạn mà nguyền rủa trong một năm.

Mặt khác, sử dụng các cấu trúc ngôn ngữ mới không tồn tại khi mã được viết lần đầu tiên không tự động ngụ ý rằng bạn đang giảm khả năng bảo trì hoặc thậm chí là bạn đang phá vỡ phong cách của mã. Tất cả phụ thuộc vào các tính năng ngôn ngữ cụ thể mà bạn muốn sử dụng và mức độ quen thuộc của nhóm với cả kiểu mã cũ và các tính năng mới.

Ví dụ, thông thường không nên bắt đầu giới thiệu các khái niệm lập trình chức năng như ánh xạ / rút gọn cho một cơ sở mã hoàn toàn theo kiểu OO, nhưng có thể hoạt động để thêm chức năng lambda ở đây và ở đó cho một chương trình không sử dụng bất cứ điều gì như vậy trước đây.


1
Tôi không đồng ý một phần. Chúng ta nên tuân theo nguyên tắc đóng mở. Vì vậy, chúng ta nên cố gắng cô lập mã mới càng nhiều càng tốt, đồng thời viết mã mới với cấu trúc ngôn ngữ mới nhất có thể.
Anand Vaidya

3
@AnandVaidya: đó là một kỳ vọng phi thực tế, vì nó giả sử mã cũ tuân theo OCP hoặc có thể dễ dàng thay đổi để tuân theo OCP, điều này hiếm khi xảy ra hoặc đơn giản là không đáng để nỗ lực.
Doc Brown

1
Khi tôi nói ocp, tôi không có nghĩa là oops ocp, nhưng nói chung là ocp. Về cơ bản, đó là cố gắng không sửa đổi mã hiện có càng nhiều càng tốt và có mã riêng của bạn. Điều đó thậm chí có thể với mã thủ tục cũ. Tôi hiểu rằng mã spaghetti không thể được sửa đổi theo cách đó, nhưng đối với mã được thiết kế tốt trong bất kỳ mô hình nào, mở đóng nên dễ dàng áp dụng. Có thể là oops hoặc thủ tục hoặc chức năng.
Anand Vaidya

2
@AnandVaidya: khi bạn không có nghĩa là OCP, bạn không nên gọi nó theo cách đó. Tôi đoán những gì bạn thực sự muốn nói là những gì Feather gọi là viết mã mới trong một phương thức nảy mầm , vì vậy người ta có thể hạn chế kiểu mã mới thành một phương thức mới, tách biệt. Khi điều này là có thể và hợp lý, bạn đã đúng, điều đó tốt. Thật không may, cách tiếp cận này không phải lúc nào cũng có thể áp dụng, ít nhất là không nếu bạn muốn giữ mã cũ DRY.
Doc Brown

@DocBrown: Tôi không nghĩ rằng nguyên tắc mở / đóng bị hạn chế trong lập trình hướng đối tượng. Bạn có thể có các mô-đun nơi bạn thêm các quy trình mới nhưng không sửa đổi các quy trình hiện có, tức là bạn giữ nguyên ngữ nghĩa của mã hiện có và nếu bạn cần chức năng mới, bạn hãy viết mã mới.
Giorgio

5

Ở một mức độ lớn, mã và cách nó trông không liên quan . Những gì mã làm là những gì quan trọng.

Nếu bạn có thể đảm bảo rằng việc thay đổi / viết lại mã sẽ không thay đổi những gì mã làm, thì bạn có thể tiếp tục và cấu trúc lại mã theo nội dung trái tim của bạn. "Đảm bảo" đó là một tập hợp đầy đủ các bài kiểm tra đơn vị mà bạn có thể chạy trước và sau khi thay đổi, không có sự khác biệt rõ ràng.

Nếu bạn không thể đảm bảo sự ổn định đó (bạn chưa có các thử nghiệm đó), thì hãy để nó yên.

Không ai sẽ cảm ơn bạn vì đã "phá vỡ" một phần mềm quan trọng trong kinh doanh, ngay cả khi bạn đang cố gắng làm cho nó "tốt hơn". "Làm việc" hơn "tốt hơn" mỗi lần.

Tất nhiên, không có gì ngăn bạn tạo ra một loạt các bài kiểm tra như vậy để sẵn sàng cho một bài tập như vậy ...


4

Hầu như bất cứ điều gì có thể được phân tích về chi phí và lợi ích, và tôi nghĩ điều này áp dụng ở đây.

Những lợi ích rõ ràng của tùy chọn đầu tiên là nó giảm thiểu công việc trong thời gian ngắn và giảm thiểu cơ hội phá vỡ một cái gì đó bằng cách viết lại mã làm việc. Chi phí rõ ràng là nó giới thiệu sự không nhất quán vào cơ sở mã. Khi bạn đang thực hiện một số thao tác X, nó đã thực hiện một cách trong một số phần của mã và một cách khác trong một phần khác của mã.

Đối với cách tiếp cận thứ hai, bạn đã lưu ý lợi ích rõ ràng: tính nhất quán. Chi phí rõ ràng là bạn phải uốn cong đầu óc để làm việc theo cách mà bạn có thể đã từ bỏ nhiều năm trước, và mã vẫn không thể đọc được.

Đối với cách tiếp cận thứ ba, chi phí rõ ràng chỉ đơn giản là phải làm nhiều công việc hơn. Một chi phí ít rõ ràng hơn là bạn có thể phá vỡ những thứ đang làm việc. Điều này đặc biệt có khả năng nếu (như thường lệ) mã cũ có các kiểm tra không đầy đủ để đảm bảo rằng nó tiếp tục hoạt động chính xác. Lợi ích rõ ràng là (giả sử bạn thực hiện thành công) bạn có mã mới đẹp, sáng bóng. Cùng với việc sử dụng các cấu trúc ngôn ngữ mới, bạn có cơ hội cấu trúc lại cơ sở mã, nó sẽ luôn luôn cải thiện chính nó, ngay cả khi bạn vẫn sử dụng ngôn ngữ chính xác như khi nó được viết - thêm vào các cấu trúc mới tạo ra công việc dễ dàng hơn, và nó cũng có thể là một chiến thắng lớn.

Một điểm quan trọng khác: ngay bây giờ, có vẻ như mô-đun này đã được bảo trì tối thiểu trong một thời gian dài (mặc dù toàn bộ dự án đang được duy trì). Điều đó có xu hướng chỉ ra rằng nó được viết khá tốt và tương đối không có lỗi - nếu không, nó có thể đã được bảo trì nhiều hơn trong thời gian tạm thời.

Điều đó dẫn đến một câu hỏi khác: nguồn gốc của sự thay đổi mà bạn đang thực hiện là gì? Nếu bạn đang sửa một lỗi nhỏ trong mô-đun mà vẫn đáp ứng tốt các yêu cầu của nó, điều đó có xu hướng cho thấy rằng thời gian và nỗ lực tái cấu trúc toàn bộ mô-đun có thể sẽ bị lãng phí phần lớn - đến lúc ai đó cần phải xử lý một lần nữa, họ có thể ở cùng một vị trí như bạn hiện tại, duy trì mã không đáp ứng mong đợi "hiện đại".

Tuy nhiên, cũng có thể các yêu cầu đó đã thay đổi và bạn đang làm việc với mã để đáp ứng các yêu cầu mới đó. Trong trường hợp này, rất có thể những nỗ lực đầu tiên của bạn sẽ không thực sự đáp ứng các yêu cầu hiện tại. Ngoài ra còn có một cơ hội lớn hơn đáng kể rằng các yêu cầu sẽ trải qua một vài vòng sửa đổi trước khi chúng ổn định trở lại. Điều đó có nghĩa là bạn có nhiều khả năng thực hiện công việc quan trọng trong mô-đun này trong thời gian gần (tương đối) và có nhiều khả năng thực hiện thay đổi trong suốt phần còn lại của mô-đun, không chỉ trong một lĩnh vực bạn biết về quyền hiện nay. Trong trường hợp này, tái cấu trúc toàn bộ mô-đun có nhiều khả năng có lợi ích rõ ràng, ngắn hạn, biện minh cho công việc làm thêm.

Nếu các yêu cầu đã thay đổi, bạn cũng có thể cần xem xét loại thay đổi nào có liên quan và điều gì thúc đẩy sự thay đổi đó. Ví dụ: giả sử bạn đã sửa đổi Git để thay thế việc sử dụng SHA-1 bằng SHA-256. Đây là một thay đổi trong yêu cầu, nhưng phạm vi được xác định rõ ràng và khá hẹp. Khi bạn đã lưu trữ và sử dụng SHA-256 một cách chính xác, bạn sẽ không gặp phải các thay đổi khác ảnh hưởng đến phần còn lại của cơ sở mã.

Theo một hướng khác, ngay cả khi nó bắt đầu nhỏ và rời rạc, một thay đổi đối với giao diện người dùng có xu hướng bóng, do đó, bắt đầu như "thêm một hộp kiểm mới vào màn hình này" kết thúc giống như: "thay đổi giao diện người dùng cố định này để hỗ trợ các mẫu do người dùng xác định, các trường tùy chỉnh, bảng màu tùy chỉnh, v.v. "

Đối với ví dụ trước, có lẽ có ý nghĩa nhất để giảm thiểu các thay đổi và sai sót về mặt nhất quán. Đối với sau này, tái cấu trúc hoàn chỉnh có nhiều khả năng trả hết.


1

Tôi sẽ đi cho bạn lựa chọn đầu tiên. Khi tôi viết mã, tôi tuân theo quy tắc để nó ở trạng thái tốt hơn.

Vì vậy, mã mới tuân theo các thực tiễn tốt nhất, nhưng tôi sẽ không chạm vào mã không liên quan đến các vấn đề tôi đang làm việc. Nếu bạn làm tốt điều này, mã mới của bạn sẽ dễ đọc và dễ bảo trì hơn mã cũ. Tôi thấy rằng tổng khả năng bảo trì sẽ tốt hơn, bởi vì nếu bạn tiếp tục, mã bạn cần chạm vào cho công việc của bạn thường xuyên hơn là mã "tốt hơn". Điều này dẫn đến việc giải quyết vấn đề nhanh hơn.


1

Câu trả lời, giống như rất nhiều thứ khác, là nó phụ thuộc . Nếu có những lợi thế lớn cho việc cập nhật các cấu trúc cũ hơn, chẳng hạn như khả năng bảo trì dài hạn được cải thiện rất nhiều (ví dụ như tránh địa ngục gọi lại) hơn là đi tìm nó. Nếu không có lợi thế lớn, tính nhất quán có lẽ là bạn của bạn

Hơn nữa, bạn cũng muốn tránh nhúng hai kiểu trong cùng một chức năng nhiều hơn bạn muốn tránh nó trong hai chức năng riêng biệt.

Tóm lại: quyết định cuối cùng của bạn nên dựa trên phân tích 'chi phí' / 'lợi ích' của trường hợp cụ thể của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.