4 điểm bởi GN⁺ 2023-12-09 | 1 bình luận | Chia sẻ qua WhatsApp
  • Thông qua trải nghiệm viết lại rồi phải hoàn tác phần mã resize của trình biên tập đồ họa với trọng tâm là loại bỏ trùng lặp, bài viết cho thấy việc ám ảnh với clean code có thể làm tổn hại đến khả năng thay đổi trong thực tế
  • Triển khai cũ lặp lại các công thức theo từng handle của Rectangle, Oval, Header, TextBlock; triển khai mới cố gắng loại bỏ trùng lặp bằng cách tách hướng và hình dạng để tổ hợp lại
  • Ngay sau khi refactor, kích thước mã giảm một nửa và các điểm chỉnh sửa cũng tập trung vào một chỗ, nhưng khi về sau cần các hành vi đặc thù theo từng hình dạng và từng handle, chính phần trừu tượng hóa lại có thể trở thành vật cản
  • Vấn đề lớn hơn cả phán đoán kỹ thuật là việc check-in thẳng lên master mà không trao đổi với đồng nghiệp đã viết đoạn mã gốc, điều này có thể làm tổn hại đến việc xây dựng niềm tin trong nhóm
  • “Sự sạch sẽ” không phải mục tiêu mà chỉ là công cụ để xử lý các hệ thống phức tạp; quan trọng hơn hình thức của mã là cả nhóm có thể cùng ứng phó với thay đổi hay không

Sự lặp lại trong mã resize của trình biên tập đồ họa

  • Trên canvas của trình biên tập đồ họa có đoạn mã dùng các handle nhỏ ở mép để resize những hình như hình chữ nhật và hình elip
  • Cách triển khai hoạt động được, nhưng các công thức tính vị trí và kích thước bị lặp lại theo từng hình và từng hướng của handle
    • Rectangle có các phương thức như resizeTopLeft, resizeTopRight, resizeBottomLeft, resizeBottomRight
    • Oval có các phương thức như resizeLeft, resizeRight, resizeTop, resizeBottom
    • HeaderTextBlock cũng có các phương thức resize riêng
  • Khi người dùng giữ Shift, còn phải xử lý giữ nguyên tỷ lệ trong lúc resize, nên số lượng công thức lại càng nhiều hơn

Trừu tượng hóa để loại bỏ trùng lặp

  • Sự lặp lại dường như xuất hiện trên hai trục
    • Lặp giữa các handle cùng hướng: Oval.resizeLeft()Header.resizeLeft() đều là thao tác kéo handle bên trái
    • Lặp giữa các phương thức trong cùng một hình: nhiều phương thức của Oval đều cùng xử lý hình elip
  • Cấu trúc mới cố gắng chia mã thành DirectionsShapes để tổ hợp hành vi theo hướng với hành vi theo hình dạng
    • Trong Directionstop, left, bottom, right
    • Trong ShapesOval, Rectangle
    • Dùng createHandle, createBox để tổ hợp handle và hình dạng
  • Theo cách này, tạo ra các cấu hình handle như fourCorners, fourSides, twoSides, rồi dùng chúng để tạo Rectangle, Oval, Header, TextBlock

Ảo giác ngay sau khi refactor

  • Sau khi refactor, tổng kích thước mã giảm một nửa và trông như thể đã hết trùng lặp
  • Tác giả tin rằng khi muốn thay đổi hành vi của một hướng hoặc một hình dạng cụ thể thì chỉ cần sửa ở một chỗ thay vì nhiều phương thức
  • Đã check-in thay đổi lên master vào đêm khuya, và nghĩ rằng mình đã gỡ rối gọn gàng phần mã lộn xộn của đồng nghiệp

Vấn đề lộ ra vào ngày hôm sau

  • Ngày hôm sau, sếp yêu cầu hoàn tác thay đổi; lúc đó tác giả tin rằng mã mới sạch hơn mã cũ nên rất khó chấp nhận
  • Sau này nhìn lại, quyết định đó là đúng vì hai lý do
    • Đã viết lại và check-in mã mà không hề trao đổi với người đã viết phần mã gốc
    • Đổi lấy việc giảm trùng lặp bằng cách hy sinh khả năng thích ứng với thay đổi yêu cầu
  • Một đội ngũ kỹ thuật lành mạnh cần liên tục xây dựng niềm tin, và việc viết lại quy mô lớn mã của đồng nghiệp có thể giáng một đòn mạnh vào hợp tác

Cái giá của trừu tượng hóa sai

  • Về sau, cần rất nhiều trường hợp đặc biệt và hành vi đặc thù cho nhiều handle của nhiều hình khác nhau
  • Với cấu trúc trừu tượng mới, để đáp ứng các yêu cầu như vậy thì mã có thể trở nên phức tạp hơn gấp nhiều lần
  • Ngược lại, cấu trúc “lộn xộn” ban đầu lại có các phương thức tách biệt nên tương đối dễ áp dụng các thay đổi kiểu này
  • Loại bỏ trùng lặp trong mã không phải là miễn phí; khi có được một khả năng nào đó, bạn cũng có thể mất đi một khả năng khác

Nghĩ lại về “sự sạch sẽ”

  • Giai đoạn ám ảnh với “clean code” và việc loại bỏ trùng lặp là điều nhiều lập trình viên có thể trải qua
  • Khi thiếu tự tin với đoạn mã mình viết, rất dễ đặt niềm tự hào vào những thứ đo được như quy tắc lint nghiêm ngặt, quy tắc đặt tên, cấu trúc thư mục, hay không có trùng lặp
  • Khi đã biết cách tạo trừu tượng hóa, bạn có thể bắt đầu tin rằng trừu tượng hóa là một đức tính và muốn áp dụng nó mỗi khi nhìn thấy mã lặp
  • Cần suy nghĩ sâu hơn về việc cảm giác “sạch” hay “bẩn” thực sự gắn với kết quả kỹ thuật nào
  • Không chỉ hình thức của mã, mà cách mã thay đổi và được chỉnh sửa cùng với những người trong nhóm cũng rất quan trọng

Khi cần buông bỏ clean code

  • Clean code không phải mục tiêu, mà là một nỗ lực để hiểu các hệ thống phức tạp
  • Khi bạn không chắc một thay đổi trong codebase sẽ ảnh hưởng thế nào, nó có thể trở thành một cơ chế phòng vệ giúp định hướng
  • Ban đầu, trải nghiệm đơn giản hóa mã phức tạp bằng cách trích xuất hàm hoặc refactor class có thể rất thú vị
  • Nhưng không nên dừng ở đó để rồi trở thành một “tín đồ clean code”
  • Hãy để clean code làm người dẫn đường, nhưng đến một thời điểm nào đó bạn phải buông nó ra

1 bình luận

 
GN⁺ 2023-12-09
Các ý kiến trên Hacker News
  • Trùng lặp mã đôi khi là chấp nhận được, nhưng điều đó không chứng minh rằng clean code là xấu
    Có vẻ việc refactor đã hơi quá tay; nếu chỉ tách “10 dòng toán học lặp lại” thành một hàm thì có lẽ đã gọn gàng hơn
    Đồng đội không biến 10 dòng đó thành hàm cũng không hẳn là làm tốt, nên nếu là tôi thì tôi đã từ chối PR, nhưng sẽ không tự viết lại
    Viết lại PR của người khác là một sự xúc phạm nhanh chóng, đóng lại mọi khả năng thảo luận, và nhiều khi ta cũng không biết bối cảnh vì sao mã được viết như vậy
    Nếu không có chuẩn coding, PR nên được review và từ chối; nếu cứ để commit theo kiểu “clean code không quan trọng” thì chỉ vài tháng sau codebase sẽ thành nơi không ai muốn làm việc
    Điều cần học ở đây không phải là clean code, mà là giao tiếp và sự tôn trọng

    • Tự tay viết lại mã đang chạy được của người khác không chỉ xúc phạm tác giả, mà còn là hành vi thiếu chuyên nghiệp vì lãng phí thời gian thay đổi phần mã vốn có người bảo trì riêng
      Nó cũng lấy mất cơ hội để tác giả ban đầu học được mã chất lượng thấp thực sự lãng phí thời gian như thế nào
      Dù cách tiếp cận clean code có tốt hơn, điểm cốt lõi ở đây không phải là mã, mà là việc người đó đã lãng phí thời gian của mình mà không đưa ra phản hồi hữu ích
      Cách tốt nhất lẽ ra là để tác giả ban đầu nâng tiêu chuẩn, còn bản thân thì làm những việc khác thực sự cần sửa. Nói cách khác, câu trả lời đúng là code review
    • Bài học gần hơn với “đừng áp dụng quy tắc quá mức mà không cân nhắc thỏa hiệp theo bối cảnh”, và đây là bài học cần thời gian để lĩnh hội
    • Sau hơn 20 năm, tôi thấy suy nghĩ “không tách 10 dòng lặp lại thành hàm là tệ hại” thường thuộc về những người hay tập trung nhầm chỗ trong codebase
      Những người như vậy thường tạo thêm cả vấn đề tương lai trong lúc giải những thứ vốn không phải là vấn đề, thay vì giải quyết vấn đề thật
      Ở lần triển khai đầu tiên, tốt hơn là không trừu tượng hóa một đoạn mã rất nhỏ cỡ 10 dòng như vậy. Mã chỉ làm đúng việc đó thì sau này dễ sửa, còn trừu tượng hóa 10 dòng vì một lợi ích chưa được kiểm chứng thì không mấy ý nghĩa
      Clean Code hoàn toàn không quan trọng, và phần lớn các quy tắc hay “nguyên tắc” tương tự cũng vậy. Trái lại, đó là những thứ mà những người muốn biện minh cho việc dần dần dựng lên trong codebase một cấu trúc như tổ ong thường bám vào
      Kết quả là rất dễ có mã bị trừu tượng hóa quá mức, khó thay đổi, chậm và khó hiểu
      Nếu cần một nguyên tắc, chỉ cần xem việc chương trình thực sự làm — biến đổi dữ liệu — có được biểu đạt rõ ràng và dễ thay đổi hay không
      Phần lớn các nguyên tắc SOLID không giúp, thậm chí còn cản trở, việc nhìn ra biến đổi dữ liệu là gì và diễn ra như thế nào, nên nhìn chung ít hữu ích để hiểu chương trình thực tế
    • Tôi cho rằng cách diễn đạt của bài gốc không hay
      Nhìn vào bản refactor được đề xuất, 10 dòng đó thực ra không giống hệt nhau mà chỉ tương tự nhau
      Công thức để thay đổi kích thước góc trên bên trái của hình chữ nhật và công thức để thay đổi góc dưới bên phải của hình elip là khác nhau. Bề ngoài chúng tương tự, nhưng có chỗ dùng - thay vì +
      Trong trường hợp này, độ phức tạp nằm ở bản thân hình học, và dù “trừu tượng hóa” nó thì cũng chỉ chuyển sự lộn xộn sang chỗ khác chứ không làm nó biến mất tận gốc
      Có thể có một trừu tượng toán học thông minh nào đó như một hệ số đặc biệt, nhưng nếu ngôn ngữ không tích hợp sẵn thì chi phí triển khai các phép toán đặc biệt bằng số nguyên cơ bản nhiều khả năng sẽ lớn hơn lợi ích thu được
  • “Clean code” cần được tái định vị thương hiệu
    Trong hơn 25 năm kinh nghiệm, tôi đã liên tục thấy sự phản đối không dứt đối với design pattern, trừu tượng hóa, loại bỏ trùng lặp, v.v.
    Lý do lúc nào cũng là trừu tượng hóa làm mã phức tạp hơn, không có thời gian viết clean code, tất cả chỉ là sở thích và ý kiến cá nhân
    Nhưng mục đích của clean code là làm cho mã đơn giản hơn và dễ bảo trì hơn khi yêu cầu thay đổi
    Giá trị của phần mềm nằm ở việc nó có thể thay đổi theo thời gian. Nếu không phải vậy, ta đã dùng mạch cố định, vốn dễ và rẻ hơn nhiều để triển khai và bảo trì
    Nếu việc refactor không đạt được mục tiêu này, và cấp trên có thể lập luận thuyết phục rằng nó khiến việc bảo trì khi yêu cầu tương lai thay đổi trở nên khó hơn, thì đoạn mã đó đã không “clean”
    Việc không trao đổi với tác giả ban đầu là vấn đề quy trình và phép lịch sự, nằm ngoài phạm vi clean code. Hành động thô lỗ và độc đoán không làm giá trị của clean code biến mất

    • Tôi cho rằng sự phản đối là nhắm vào những đơn thuốc quá tuyệt đối
      Cuốn sách nổi tiếng Clean Code nêu “không trùng lặp” như một điều kiện của hệ thống được thiết kế tốt, và xem trùng lặp là kẻ thù chính của công việc phát sinh, rủi ro phát sinh và độ phức tạp không cần thiết
      Theo định nghĩa này, loại bỏ trùng lặp trở thành đồng nghĩa với đơn giản hóa, nhưng điều đó là sai
      Loại bỏ trùng lặp là đưa vào một phụ thuộc. Nếu phụ thuộc đó mô hình hóa vấn đề tốt, nó có thể là một trừu tượng tốt và là sự đơn giản hóa; nếu không, nó chỉ là nén dưới lớp vỏ trừu tượng
      Nhân tiện, trích dẫn này là phần nhắc đến Simple Design của Kent Beck trong Clean Code
    • “Clean code” được dùng với hai nghĩa
      Một là cảm nhận chủ quan rằng mã nào đó tốt, hai là tuân theo cuốn sách Clean Code và các hướng dẫn liên quan
      Ngày càng nhiều người cảm thấy hai điều này khác nhau khá nhiều. Làm theo một số hướng dẫn trong sách đôi khi lại tạo ra mã hoàn toàn không sạch
      Trong một thế giới lý tưởng, ai cũng muốn viết clean code, nhưng ngày càng ít người cảm thấy Clean Code là cách tốt để đạt mục tiêu đó
      Đặc biệt, trừu tượng hóa quá mức và loại bỏ trùng lặp đã trở thành nguồn gốc của mã lộn xộn trong mắt nhiều người
    • Điều tôi học được trong ngành là ngay cả mã được viết gọn gàng nhất cũng không trụ được lâu khi gặp yêu cầu sản phẩm
      Đôi khi technical debt được mã hóa ngay trong chính yêu cầu sản phẩm, nên không thể sửa bằng refactor hay best practice, mà chỉ có thể giảm bớt đau đớn
    • Nghe giống kiểu lập luận “không phải người Scotland chân chính”
      Mục đích của clean code và kết quả thực hành thực tế khác nhau rất xa. Lý do là con người nhìn chung không dự đoán được trạng thái tương lai của mã, dù yêu cầu đang ở ngay trước mắt
      Clean code gọi ra câu hỏi kinh khủng “nếu như thì sao?”, và điều này dễ trở thành chiếc hộp Pandora khiến việc phát hành bị trì hoãn vô tận
      Hầu như lúc nào cũng tốt hơn nếu lập trình theo những gì đã biết, và để người có kinh nghiệm miền nghiệp vụ dẫn dắt kiến trúc
    • Nếu liệt kê các phương án thay thế, thì phần lớn trừu tượng mà chúng ta tạo ra là tệ, kể cả những thứ ta nghĩ là tốt cũng thường như vậy
      Nhiều trừu tượng tốt đã nằm sẵn trong thư viện. Dĩ nhiên, đáng tiếc là không phải lúc nào cũng vậy
      Trừu tượng tệ ngốn thời gian, và vì mã cũng là cách giao tiếp với những người khác trong nhóm, việc các thành viên tiếp nhận cách giao tiếp đó ra sao quan trọng hơn nhiều so với cách tôi suy nghĩ
  • Đồng nghiệp đã viết rất nhiều mã bằng cách sao chép-dán, và bạn đã refactor sau khi commit
    Đồng nghiệp phàn nàn với sếp, sếp mắng bạn, và rốt cuộc bạn kết luận lần sau sẽ để sự lộn xộn lại trong codebase
    Có bài học cần rút ra ở đây, nhưng điều đó không có nghĩa là sao chép-dán tốt hơn viết hàm
    Việc refactor đáng lẽ nên được đưa lên thành một thay đổi riêng, tag tác giả ban đầu để họ review
    Tuy nhiên, việc đồng nghiệp không nói chuyện trực tiếp mà đưa lên sếp, và sếp yêu cầu hoàn tác, là một tín hiệu khá xấu

    • Nếu là tôi, tôi sẽ nhắn riêng trên Slack cho đồng nghiệp: “Trong mỗi hàm này có 10 dòng toán học bị lặp lại, chẳng phải nên tách ra thành một hàm riêng sao?”
      Câu trả lời có thể là “Không, vì…” hoặc hiếm hơn là “Đúng nhỉ, mai tôi sẽ làm”
      Không gây ồn ào, mọi người đều tránh được vấn đề, và có thể ai đó còn học được điều gì đó
    • Nếu đó là sao chép-dán vì lười, thì dùng hàm cho phần lặp lại, tức là trừu tượng hóa, tốt hơn theo góc nhìn DRY
      Nhưng tác giả đã bổ sung một ngữ cảnh quan trọng. Họ đã đánh đổi khả năng yêu cầu thay đổi lấy việc giảm trùng lặp, và về sau mỗi handle của từng hình dạng lại cần nhiều trường hợp đặc biệt và hành vi riêng
      Điều này cho thấy khác biệt giữa DRY ngẫu nhiênDRY bản chất
      Thư viện A và B thường trông nhất quán với nhau nên mã trở nên giống nhau, nhưng thực tế đó có thể chỉ là trùng hợp
      Nếu wrapper bọc lib A và wrapper bọc lib B chứa cùng một đoạn mã nhưng lại bọc hai thứ hoàn toàn khác nhau, thì trừu tượng hóa chúng sẽ cho kết quả tệ
      “15 cái” cũng vậy. Nếu “mọi danh sách luôn phải hiển thị 15 mục” thì trừu tượng hóa số 15 là đúng, nhưng nếu đó lần lượt là “top 15 bài hát trên trang chủ” và “mặc định 15 bình luận bên dưới bài hát”, thì gom hai số 15 đó lại là lựa chọn tệ hơn
    • Dấu hiệu cảnh báo lớn nhất là đã thay đổi phần nộp của đồng nghiệp mà không trao đổi gì
      Dấu hiệu cảnh báo thứ hai là bản thân việc refactor cũng không phải một refactor tốt. Nó thiếu tính mô-đun, vẫn bị ghép nối, và chỉ giảm được chút ít trùng lặp
    • Việc tag tác giả ban đầu cũng có thể gây rắc rối
      Nó có thể nghe như “Mã cậu viết dở quá nên tôi viết lại rồi. Có ý kiến gì không?”
      Cách tốt nhất có lẽ là yêu cầu giải thích dưới dạng “review muộn”, đề xuất giải pháp, và hy vọng tác giả ban đầu không phớt lờ
    • Ở cuối bài có một đoạn quan trọng: trong những thay đổi về sau của codebase, họ phải xử lý đặc biệt các handle theo từng hình dạng; điều đó khó với cấu trúc đã refactor nhưng dễ với mã lặp lại
      Điểm cốt lõi là nhận ra mã sẽ thay đổi trong tương lai. Yêu cầu sẽ thay đổi, và mã liên quan cũng sẽ thay đổi
      Nếu tạo ra một trừu tượng hạn chế khả năng thay đổi đó, bạn sẽ làm tổn hại khả năng thích nghi codebase của chính mình trong tương lai
      Đặc biệt với những trừu tượng đã tốn nhiều thời gian và công sức, ta dễ bám víu về mặt cảm xúc, rồi lãng phí thêm thời gian để nhồi nhét thay đổi vào một giải pháp tao nhã
      KISS là đúng
  • Trường hợp này trông giống vấn đề “không ai thích người tỏ ra ta đây”
    Điều này đặc biệt hay xảy ra khi một lập trình viên junior chưa học được rằng công việc không chỉ là chuyện giữa mình và máy tính, mà là giữa mình, máy tính và những người khác
    Một phiên bản gọn gàng hơn có lẽ đã được chấp nhận nếu họ thật sự trò chuyện với tác giả ban đầu và thống nhất một cách tốt hơn
    Tiêu đề bài viết có vẻ nên là đã học cách làm việc trong đội nhóm hơn là “Tạm biệt, Clean Code”

    • Cụm “không ai thích người tỏ ra ta đây” nắm bắt khá đúng môi trường phát triển phần mềm
      Mã phức tạp hơn có thể đã bị xem là mối đe dọa, và người viết có thể bị coi là đối thủ cần bị dập xuống qua chuỗi quản lý
      Nếu là junior, cần hiểu rằng câu này phụ thuộc vào ngữ cảnh. Ở nhiều nơi làm việc, sự đơn giản quan trọng hơn tính linh hoạt, các tính năng ngôn ngữ gây rối bị tránh né, và kiểu làm việc ngồi lâu trước bàn phím cùng những ngôn ngữ như vậy được ưa chuộng
      Ngược lại, triết lý coi trọng học tập suốt đời, software craftsmanship và tự nhìn lại quy trình thì xung đột hoàn toàn với môi trường đó
      Những nỗ lực để trở nên tốt hơn không được lòng những người không phát triển
      Cả hai đều là lựa chọn sống hợp lệ. Phần mềm vừa có thể là nơi thuận tiện để làm 7 tiếng rồi về nhà, vừa có thể là thế giới khám phá mà chỉ trí tưởng tượng và trí tuệ là giới hạn
      Nếu muốn khám phá những điều mới, hãy cẩn trọng với việc dành hàng chục năm bên những người sợ cái chưa biết
      Nói ngắn hơn, nếu incentive của bạn và đồng nghiệp bị lệch nhau, hãy cân nhắc rời đi
    • Tôi nghĩ tiêu đề ổn
      Bài viết này có lý do để được yêu thích lâu dài, và đã giúp nhiều lập trình viên đang rơi vào cùng cái bẫy mở mắt
      Ngay cả khi làm việc một mình, bạn vẫn có thể thiết kế quá mức một trừu tượng gọn gàng rồi bị chính trừu tượng đó làm khó trong tương lai
    • Những vấn đề kiểu này thường do các senior say quyền lực gây ra nhiều hơn là junior
  • Vấn đề nằm ở chỗ thay đổi được commit thẳng vào nhánh main mà không qua review
    Mọi thay đổi mã nên có peer review, dù là pair programming hay PR truyền thống, để ngăn tình huống leo thang kiểu này
    Sếp là một người sếp tệ. Thay vì trách mắng, đáng lẽ phải nhận ra rằng đang thiếu một quy trình tốt
    Nếu là một đội có văn hóa code review, khi một người mới quá nhiệt tình thử làm vậy, đồng nghiệp đã có thể nói “cảm ơn, nhưng cái này không ổn” hoặc “ý hay đấy, thử làm theo cách này nhé”
    Riêng chuyện khác, tôi nghĩ không nên refactor mã nếu không có lý do chính đáng. Dù là mã xấu, nó vẫn có thể là phần gần như không bao giờ cần sửa
    Thời điểm tốt nhất để refactor là khi bạn cần thay đổi hành vi nhưng nhận ra cấu trúc hiện tại khiến việc đó quá khó
    Tiếc là ở đây có vẻ họ đã rút ra bài học sai

    • Code review sau khi merge cũng hoàn toàn ổn, và ở một số khía cạnh còn tốt hơn
  • Không hiểu sao mọi người chỉ nói về việc lẽ ra ông ấy nên làm gì thay vào đó. Đây là một bài học hoàn toàn chính đáng
    Trong tài chính, ta liên tục xử lý các sản phẩm phần nào giống nhau nhưng không hoàn toàn giống nhau. Quyền chọn cổ phiếu và quyền chọn ngoại hối đều là quyền chọn, đều có giá thực hiện và được định giá bằng một phiên bản nào đó của Black-Scholes, nhưng trên thực tế chúng khác nhau cực kỳ nhiều
    Người mới tham gia rất dễ bị cám dỗ muốn gom chung nhiều hành vi, và chúng tôi thường ở vào vị trí của sếp trong bài gốc, phải bảo các lập trình viên trẻ bình tĩnh lại và cứ tách riêng ra
    Không phải là chia tay với Clean Code, mà là phải tránh trừu tượng hóa quá mức thì mới thực sự giữ được code sạch

    • Ví dụ tài chính thì đúng, nhưng ở đây đang nói về hình học
      Những việc mà handle thông thường làm là biến đổi affine, tức là tịnh tiến, xoay, co giãn và phản xạ; nếu là co giãn đồng đều thì đó là phép biến đổi đồng dạng. Điều này được bảo đảm hoạt động với bất kỳ hình nào
      Vấn đề thật sự là không nên trừu tượng hóa những thứ mình chưa hiểu đủ rõ. Đặc biệt là khi không biết trong tương lai sẽ xuất hiện khả năng nào
      Ví dụ, nếu nghĩ oval chỉ gồm hình tròn và hình elip, bạn sẽ khá bất ngờ khi oval “thật” đầu tiên xuất hiện
    • Tôi hiểu ý chính của bài, nhưng tiêu đề thực sự rất dễ bị đọc như một cái cớ để biện minh cho code bừa bộn
      Tác giả không chủ động phân biệt code bừa bộn với code “không clean”, nên người khác mới cố phân biệt thay
    • Khá rõ là mọi người ở đây, ngoài vài dòng mã giả, chẳng có bối cảnh gì mà vẫn cảm thấy mình biết rõ hơn các thành viên trong nhóm lúc đó và tác giả
      DRY có giá trị, nhưng trừu tượng hóa cũng có chi phí. Luôn là một sự đánh đổi
  • Một trong những lý do tôi thích các ngôn ngữ như Haskell là chủ nghĩa tối đa hóa trừu tượng hóa nằm ở cấp độ ngôn ngữ, chứ không phải cấp độ coding
    Mọi người đều đồng ý Applicative nên làm gì. Thách thức được đẩy sang phía bên phải hơn của pipeline từ ý tưởng đến code, tức là tìm Applicative phù hợp với bài toán
    Hầu hết các design pattern hướng đối tượng đều có đối ứng tự nhiên và được định nghĩa rõ trong môi trường như vậy, giúp giảm các trừu tượng theo từng dự án
    Với người mới bắt đầu, chi phí ban đầu để học ngôn ngữ là lớn, và thường xuyên có chuyện mở code Haskell của người khác ra thì thấy khoảng 12 compiler extension chưa từng gặp và phải hiểu chúng
    Dù vậy, những compiler extension đó có thể mang sang mọi dự án về sau, nên tôi xem đó là lợi ích ròng
    Với chuyên gia, nhược điểm là muốn có cảm giác cats sẽ ăn bao nhiêu RAM thì cần một bằng tiến sĩ

    • Lý do ta biết Applicative và Monad làm gì là vì có các định luật toán học
      Nếu một đoạn code dùng Applicative hay Monad, nhờ các định luật đó ta có thể biết hành vi của nó
      Các design pattern hướng đối tượng không có những định luật như vậy. Chúng là các khái niệm hoặc pattern được định nghĩa lỏng lẻo, mở cho diễn giải cá nhân và tùy biến, nhưng không có những định luật cụ thể nói chính xác chúng là gì và nên kỳ vọng hành vi hay đầu ra nào
    • Là người dùng Haskell, tôi đồng ý rằng khả năng trừu tượng hóa của ngôn ngữ này rất tốt
      Tuy nhiên, với ví dụ trong bài blog, phải nói là đã có rất nhiều lần đi theo con đường clean code rồi tự bắn vào chân mình trong tương lai
      Nếu sau này phải học lại phần trừu tượng để hiểu nó, thì trừu tượng đó không hữu ích
    • Bài gốc có lẽ nên dùng nhiều nguyên tắc hướng đối tượng hơn, miễn là cả nhóm nói chung không bài xích chúng. Nếu là nhóm như vậy thì có lẽ tôi sẽ làm ở nơi khác
      Nơi hướng đối tượng khớp tự nhiên nhất là widget GUI và triển khai trình biên tập
      Quy tắc hợp nhất các quy tắc khác của hướng đối tượng là đừng hỏi, hãy ra lệnh
      Trong ví dụ này, các handle sẽ được chia sẻ theo cách đã biết, và sẽ có các điểm điều khiển theo từng hình. Khi handle của hộp thay đổi, có thể gọi updateControlPoints(changedHandle), và khi điểm điều khiển thay đổi, có thể gọi updateBoxHandles(changedControlPoint)
      Có thể có cách tách bạch gọn gàng khác, nhưng nếu là tôi thì tôi sẽ làm như vậy
    • Có thể đồng ý về việc Applicative nên làm gì
      Nhưng lại khó đạt đồng thuận về việc dùng thư viện parsing nào, chọn Data.Text.Strict hay Data.Text.Lazy, dùng phong cách point-free đến mức nào, dùng .>>> hay combinator nào khác, hoặc dùng language extension nào
      Tôi thích Haskell, nhưng nó cũng gặp các vấn đề giống những ngôn ngữ khác. Mọi người thật sự không thể thống nhất đâu là cách tốt nhất để làm việc
    • Tiến thêm một bước nữa thì cứ dùng Rust với kiểu affine là được
      Bạn có thể có hệ thống kiểu của Haskell cùng hiệu năng dự đoán được
  • Chỉ cần tách riêng phần toán học lặp lại thành một hàm riêng, rồi gọi nó trong hàm resize là được
    Bản thân các hàm resize trông có vẻ lặp lại là vì chúng tạo thành interface, nên refactoring để loại bỏ chúng là sai
    Nhưng phần toán học mà các hàm đó thực hiện thì hoàn toàn là đối tượng hợp lý để dọn dẹp code

    • Đúng. Trông như hướng đi hiển nhiên và thường thì tôi cũng làm vậy
      Kết quả là sinh ra rất nhiều hàm nhỏ và thuần, nhưng vì dễ test và refactor nên không thành vấn đề
    • Đặc biệt nếu là refactoring, khi làm việc với nhóm thì thường nên xử lý theo từng bước
      Có nhiều cách và pattern để giải cùng một vấn đề, nhưng việc xé bỏ interface hiện có mà người khác đang dùng không phải là khởi đầu tốt
      Lợi ích của trừu tượng hóa là có thể thay đổi phần bên trong linh hoạt hơn mà không làm hỏng phần còn lại
      Bước đầu tốt là, như đã nói, tạo các hàm toán học linh hoạt hơn ở tầng dưới và giữ nguyên phần còn lại
      Sau đó có thể trao đổi sâu hơn với nhóm về cách giao tiếp để interface cung cấp một facade rõ ràng, còn phần triển khai thì được trừu tượng hóa
  • Điều được mô tả ở đây có cảm giác giống như trừu tượng hóa tệ
    Vấn đề thật sự nằm ở chỗ mọi người chỉ nhìn hai ba ví dụ rồi thiết kế, mà không kiểm tra xem nó có đủ tổng quát để đáp ứng các cách dùng trong tương lai hay không

    • Một cách tiếp cận đặc biệt tốt là chỉ trừu tượng hóa phần chung thành những hàm thật đơn giản
      Nên trừu tượng hóa từ dưới lên, không phải từ trên xuống
      Không cần tạo một điểm duy nhất nơi mọi thứ diễn ra và xử lý các tổ hợp 2^n
      Dù vậy vẫn có thể giảm lặp, thay đổi ở một điểm, đồng thời giữ được khả năng tùy chỉnh khi cần
    • Vấn đề ngoài thực tế là tại thời điểm viết, không ai biết mình đang dùng trừu tượng hóa tệ. Nếu biết thì họ đã không dùng
      Vì vậy chỉ nói “hãy tránh trừu tượng hóa tệ” hay “hãy làm sao để đáp ứng được cách dùng trong tương lai” là chưa đủ
      Nói chung không ai có thể dự đoán tương lai
      Trừu tượng hóa phải được dùng cực kỳ thận trọng ngay cả khi nó có vẻ đúng. Tốt hơn nên trở thành một người hoài nghi cực đoan
    • Vấn đề của phiên bản “clean” là mọi thứ bị ghép chặt hoàn toàn với nhau
      Trong mã toán học, chạm vào một thứ là ảnh hưởng đến tất cả
      Đến một lúc nào đó, sẽ dễ hơn nếu để lại một phần mã trùng lặp, dùng manh mối trong cấu trúc mã hoặc chú thích để chỉ ra sự tương đồng, và theo thời gian nhấn mạnh các khác biệt cốt lõi bằng chú thích
      Cách tiếp cận clean code với loại mã này không xử lý tốt các yêu cầu thay đổi
      Cuối cùng nó dễ trở thành một đống hàm helper với nhiều đường đi mã khác nhau tùy theo vị trí gọi
      Đôi khi có thể trừu tượng hóa điều này; như một số người có lẽ đã nhận ra, câu trước đang mô tả tính đa hình trong lập trình hướng đối tượng
      Nhưng làm sao biết chính xác yêu cầu từ trước được? Cố làm “clean code” ở giai đoạn sớm như vậy chỉ kết thúc bằng trừu tượng hóa tệ hoặc thiết kế quá mức
      Cách tiếp cận đúng là không làm gì cả, ghi lại các vấn đề có thể xảy ra, rồi chờ đến khi hiểu thêm về miền bài toán
    • Tôi ngày càng thích cách phát hiện pattern thông qua thay đổi. Tức là tìm pattern trong tính năng mới và sửa lỗi
      Tôi không quá lo về đoạn mã hơi trùng lặp nhưng dễ hiểu như ví dụ gốc
      Nhưng nếu phải sửa cùng một issue ở ba chỗ, hoặc phải thêm mã cho tính năng mới ở ba chỗ, thì tôi bắt đầu lo
      Khi đó tôi bắt đầu nghĩ xem liệu có cách nào đẩy phần mã cần sửa hoặc thêm vào một vị trí chung hay không
    • Đúng vậy
      Mọi người đọc về design pattern hoặc DRY, vì đang trong quá trình học nên thường áp dụng sai, rồi lại đổ trách nhiệm cho nguyên bản
  • Rob Pike từng nói một trong những lý do Go trở thành như hiện nay là “sao chép nhỏ tốt hơn phụ thuộc nhỏ”
    Càng lập trình nhiều, tôi càng hiểu ý đó
    Bản thân sự lặp lại không phải là vấn đề. Điều quan trọng là nắm được đơn vị mà bạn đang đọc và có thể sẽ sửa
    DRY thường đồng nghĩa với việc thêm một trừu tượng hóa khác, và nếu trừu tượng hóa đó không đủ trực giao, thì giờ bạn phải cân nhắc hai đơn vị thay vì một, khiến tình hình tệ hơn