- vừa được xem lúc

Blame Driven Development

0 0 73

Người đăng: Huy Tran

Theo The Full Snack

Blame Driven Development

Nằm ở giữa trong một team có 3 thế hệ engineers (junior, senior, principal), mình phải làm công việc review code cho cả 2 thế hệ còn lại, vì lý do đơn giản, đám junior không đủ dũng cảm để review code các level cao hơn trong team, còn lão principal (từ khúc này sẽ gọi là bác P) thì có vẻ như chả bao giờ có ý định review code của tụi nhỏ. Nhưng cũng nhờ thé mà mình trở thành người quan sát được toàn bộ mọi sự thay đổi trong dự án. Ai, thay đổi những gì, ở đâu, vào lúc nào thì dù không muốn mình vẫn nhớ hết.

Tưởng đâu đó là lợi thế, nhưng ai ngờ lợi bất cập hại, cái hại lớn nhất đó là nghĩ rằng mình biết quá nhiều, và tạo thành tính chủ quan khi tiếp cận vấn đề.

Vào một ngày thứ hai đẹp trời, bác P gửi lên một pull request để replace các lệnh xử lý tọa độ khi animation từ JavaScript sang dùng CSS transform, code của lão này mình đọc thì cũng chỉ biết approve chứ chả tranh cãi được cái vẹo gì.

Thế rồi hôm sau có liền một bug liên quan tới animation và chỉ xảy ra trên IE: Animation bị lỗi, như hình sau:

  • Lên phương án hành động: Vậy chỉ cần không dùng transform-origin(left, top) nữa, và chịu khó tính toán thêm một chút để thay thế cho việc set origin, không khó lắm, miễn là đảm bảo fix xong nó vẫn chạy đẹp trên Chrome.
  • Và không cần suy nghĩ, anh siêu nhân comment ngay vào ticket, bằng một giọng điệu "nghĩa hiệp":

    • "Bug này xảy ra vì anh P vừa update code, nhưng giải pháp của anh ấy không được tốt cho lắm, mà không sao, tôi sẽ fix nó dùm ảnh."

    ...3 tiếng sau...

    • "Quái, sao đéo chạy được..."

    Thế là lại bắt đầu từ con số 0. Google thêm một lúc, thấy có cái lạ lạ. Trên MDN không hề đề cập gì tới sự khác biệt trong cách xử lý transform-origin, bản thân cái animation nó cũng rất khác, vì nếu origin không đúng, cái hình cũng sẽ không đến mức bị rơi ra bên ngoài của màn hình như thế này.

    Thôi thử in tọa độ của nó lúc animate ra coi sao:

    const target = document.querySelector("#the-fucking-image");
    const watcher = () => { let targetStyle = window.getComputedStyle(target); console.log("->", targetStyle.top, targetStyle.left); window.requestAnimationFrame(watcher);
    };
    watcher();
    

    Output in ra lúc bắt đầu animate:

    // Chrome, Firefox, Safari -> 600px 960px
    -> 600px 960.001px
    -> 600px 960.012px
    -> 600px 960.025px
    ...
    
    // IE
    -> undefined undefined
    -> 0.122px 0.423px
    -> 0.922px 1.423px
    -> 2.91px 5.93px
    ...
    

    Ô cái nồi gì vậy nè Sao khác nhau thế? lại còn undefined nữa?

    Hóa ra ngay từ đầu tọa độ lúc khởi tạo của #the-fucking-image đã bị sai. Lần mò thêm một hồi thì lỗi sai đúng là nằm ở phần code khởi tạo:

    const animateTarget = $(targetSelector).getBoundingClientRect();
    const img = $("<img/>") .attr('src', imgSrc) .attr('id', 'the-fucking-image') .css({ width: animateTarget.width, height: animateTarget.height, top: animateTarget.y, left: animateTarget.x });
    

    Hai giá trị animateTarget.xanimateTarget.y không tồn tại dẫn đến giá trị topleft ban đầu của #the-fucking-image bị undefined. Nhưng mà trên các trình duyệt không phải IE thì nó lại tồn tại

    Thì ra, trên IE, hàm getBoundingClientRect() được mô tả như thế này:

    Returns a ClientRectList with ClientRect objects (which do not contain x and y properties) instead of DOMRect objects.

    Vậy là đã rõ vấn đề, quả nhiên, trên đời có những thứ chỉ tồn tại với mục đích làm cho cuộc đời kẻ khác trở nên khó khăn hơn, IE là một trong số chúng. Chúng ta phải tìm cách khác, không dùng getBoundingClientRect() nữa, để lấy tọa độ x, y, có thể dùng offsetTopoffsetLeft, nhưng hai hàm này không được hỗ trợ trên mọi trình duyệt.

    Nếu xem kĩ document, thì ta vẫn thấy thuộc tính left and top có trong kết quả trả về của getBoundingClientRect(), nếu dùng luôn hai thuộc tính này thì sẽ ít gây ra thay đổi lớn trên phần code đã có, khá là thích hợp. Thậm chí trên MDN cũng có ghi:

    Due to compatibility problems (see below), it is safest to rely on only properties left, top, right, and bottom.

    Vậy là, thằng nào đó trong team, lúc implement cái hàm khởi tạo trên đã không chịu đọc kĩ tài liệu, thay vì chọn hai thuộc tính mà trình duyệt nào cũng có trả về là top/left, thì lại đi chọn hai thuộc tính x/y để khi chạy trên IE thì lỗi banh xác. Hoàn toàn không phải lỗi của lão P. Và mình quá "thông thạo" tình hình dự án đến mức áp đặt ngay cái kết luận là bug xảy ra do thay đổi của lão P, chỉ vì lão ấy vô tình update trúng vào một chỗ hơi liên quan. Và cái suy nghĩ áp đặt này đã khiến cho mình đi lạc hướng lúc phân tích vấn đề.

    Bản fix khá là đơn giản:

    const img = $("<img/>") .attr('src', imgSrc) .attr('id', 'the-fucking-image') .css({ width: animateTarget.width, height: animateTarget.height,
    - top: animateTarget.y,
    - left: animateTarget.x
    + top: animateTarget.top,
    + left: animateTarget.left });
    

    Kinh nghiệm được rút ra ở đây là gì?

    Đó là khi tiếp cận và giải quyết vấn đề, luôn gạt bỏ tất cả mọi sự đánh giá chủ quan ra một bên, mọi sự khẳng định thì đều phải đi kèm với phân tích và chứng minh, ở câu chuyện trên, mình kết luận bug xảy ra do code của lão P mà không hề kiểm chứng từ đầu. Hậu quả là bị định kiến và đi lệch hướng, gạt bỏ luôn nguyên nhân gốc rễ của vấn đề ngay từ đầu.

    Kinh nghiệm thứ hai, đó là luôn brainstorming để đưa ra càng nhiều giả thiết hoặc giải pháp càng tốt cho một vấn đề, và lựa chọn giải pháp hữu hiệu nhất. Như ở trên, sau khi xác định được đúng nguyên nhân, mình đã đưa ra nhiều hướng giải pháp khác nhau và cân nhắc lợi hại của từng giải pháp, cuối cùng chọn cách ít làm thay đổi chương trình sẵn có, giảm thiểu tối đa cơ hội gây ra bug mới khi đang fix bug cũ

    Kinh nghiệm thứ ba, luôn rút kinh nghiệm và phân tích sai lầm của bản thân hoặc đồng đội trong quá trình gây ra bug nhầm, fix bug. Trên tinh thần đó, mình chạy git blame để tìm xem ai đã commit đoạn code bị lỗi lên, để còn giáo huấn.

    $ git blame animation.js 8a2038 (Huy 2018-10-08 11:44:04 -0700 451) .css({
    8a2038 (Huy 2018-10-08 11:44:04 -0700 452) width: animateTarget.width,
    8a2038 (Huy 2018-10-08 11:44:04 -0700 453) height: animateTarget.height,
    8a2038 (Huy 2018-10-08 11:44:04 -0700 454) top: animateTarget.y,
    8a2038 (Huy 2018-10-08 11:44:04 -0700 455) left: animateTarget.x
    8a2038 (Huy 2018-10-08 11:44:04 -0700 456) });
    

    Ồ thì ra...

    Bình luận

    Bài viết tương tự

    - vừa được xem lúc

    Javascript - Math.random() có thực sự là random

    Khi bạn làm việc với Javascript, và bạn cần 1 số ngẫu nhiên, thì ngay lập tức bạn sẽ nghĩ đến là Math.random(). Một trong những lý do chính cho việc sử dụng hàm Math.random() để tạo số ngẫu nhiên là tính dễ sử dụng của nó.

    0 0 430

    - vừa được xem lúc

    Chuyện không đầu không đít (phần 4)

    Chuyện không đầu không đít (phần 4). Lâu lắm rồi mới lại nghe một album mới của Bức Tường, bài cuối cùng mình nghe là Cơn mưa tháng 5, một bài hát nhiều ý nghĩa, thực sự không có ý chê bai nhưng chỉ c

    0 0 33

    - vừa được xem lúc

    Một phím một chuột và 2 máy tính

    Một phím một chuột và 2 máy tính. Khác với các công ty startup, ở các công ty lớn hơn, thì đa phần vì policy của công ty nên máy tính do công ty cấp đều cài sẵn các phần mềm theo dõi hoặc kiểm soát in

    0 0 21

    - vừa được xem lúc

    Vùng lùng bùng

    Vùng lùng bùng. Người ta nói nhiều về cái gọi là vùng an.

    0 0 17

    - vừa được xem lúc

    Be an Amateur

    Be an Amateur. OK, đóng cửa blog là một quyết định đúng đắn, giờ thì mình có thể viết và thoải mái publish mà không sợ ai thấy nữa.

    0 0 20

    - vừa được xem lúc

    Chuyện không đầu không đít (Phần 3)

    Chuyện không đầu không đít (Phần 3). Hẳn là có nhiều bạn từng nói chuyện với mình bên Slack đều biết là mấy tháng nay mình đang build một cái app tên là Pomoday.

    0 0 27