2 điểm bởi GN⁺ 2024-08-06 | 1 bình luận | Chia sẻ qua WhatsApp
  • Trải nghiệm viết lại trong một tháng phần cốt lõi của một chương trình đã tự dùng và sửa dần suốt 2 năm đã làm lung lay niềm tin trước đây của tôi về kiểm thử và quản lý phiên bản
  • Năm 2015, tôi từng cho rằng hơn cả các phép trừu tượng hóa tệ hại, kiểm thử và phiên bản mới là cốt lõi của phần mềm sống lâu; nhưng qua Mu và Freewheeling Apps, cách làm việc thực tế của tôi dần thay đổi
  • Tôi cho rằng những chương trình tồn tại lâu dài không nên nhắm tới quá nhiều người, mà nên được tạo ra trong phạm vi con người, ngữ cảnh và chức năng mà ta hiểu rõ, đồng thời chấp nhận các giới hạn thực tế như Dunbar's number
  • Kiểu dữ liệu, trừu tượng hóa, kiểm thử, phiên bản, máy trạng thái, tính bất biến và phân tích hình thức đều hữu ích trong những vùng lạ, nhưng nếu lạm dụng thì sẽ trở thành nợ kỹ thuật che giấu sự phức tạp không cần thiết
  • Khi hiểu biết về ngữ cảnh đã ổn định, việc vứt bỏ những phần lớn và làm lại là điều đáng giá; sau đó cần đưa các kịch bản cần thiết lên đầu óc cùng một lúc rồi cấu thành toàn bộ trong một lần

Sự thay đổi trong suy nghĩ về kiểm thử và quản lý phiên bản

  • Tôi vẫn luôn vật lộn với việc chọn và tự tạo ra những chương trình có thể phụ thuộc lâu dài, nhưng bản thân cũng không cảm thấy mình làm việc đó giỏi
  • Trong một tháng gần đây, tôi đã viết lại phần cốt lõi của một chương trình mà mình đã dùng suốt 2 năm và chỉnh sửa dần dần
    • Sau đó tôi dành vài ngày để sắp xếp lại những gì đã học được và xem bước tiếp theo nên là gì
    • Nhờ lần làm việc này, tôi bắt đầu nhìn thấy một thay đổi rộng hơn trong cả quỹ đạo cuộc đời mình
  • Năm 2015, tôi hoài nghi các phép trừu tượng hóa và coi trọng kiểm thử cùng quản lý phiên bản
    • Tôi cho rằng trong code có rất nhiều trừu tượng hóa tệ, còn kiểm thử và phiên bản là những tiến bộ cốt lõi của thập niên 2000
    • Tôi tìm nguyên nhân của vấn đề ở các động lực lệch lạc, sự trừu tượng hóa quá mức, và việc thiếu kiểm thử lẫn phiên bản
    • Mu1 là một nỗ lực thiết kế nền tảng lấy kiểm thử và layers làm các ràng buộc nền tảng
  • Năm 2017, tôi bắt đầu làm lại Mu1 thành Mu hiện tại
    • giai đoạn đầu, tôi dùng tất cả những ý tưởng mới về kiểm thử và layer
    • Theo thời gian, tôi dùng các ý tưởng đó ngày càng ít hơn
    • Hiện tại Mu có nhiều kiểm thử, nhưng đa số chỉ là kiểm thử thông thường, và tôi cũng không thể chuyển hạ tầng layer sang
  • Năm 2022, tôi bắt đầu làm Freewheeling Apps
    • Ban đầu không có kiểm thử, về sau tôi viết kiểm thử rất kỹ cho trình soạn thảo văn bản, là mảnh ghép cốt lõi
    • Tôi khó tìm ra cách kiểm thử phần còn lại, nhưng vẫn tiến triển đủ tốt ngay cả khi không có kiểm thử
  • Năm 2024, tôi xóa toàn bộ kiểm thử
    • Tôi bắt đầu làm lại lớn cho trình soạn thảo văn bản, và cách này lẽ ra có thể khiến tôi lo về xung đột khi gộp với các Freewheeling Apps khác
    • Kết quả là tôi cũng ngừng nghĩ về quản lý phiên bản
    • Sau khi từ bỏ kiểm thử và phiên bản mà lại có được chương trình tốt hơn, tôi không thể tiếp tục phớt lờ sự bất hòa nhận thức với niềm tin cũ của mình

Cách tổng kết hiện tại về phần mềm tồn tại lâu dài

  • Tôi cho rằng làm ra thứ bền lâu cho thật nhiều người là quá khó, nên tốt hơn là đừng cố làm vậy ngay từ đầu
    • Ta nên bị chi phối bởi những gì mình hiểu rõ, những người mình biết rõ, và Dunbar's number
  • Tôi cho rằng phần lớn phần mềm trên thế giới bị lây nhiễm bởi các động lực phục vụ thật nhiều người trong ngắn hạn
    • Tôi cố tập trung vào phần mềm có càng ít logo trên website càng tốt
    • Tôi thích phần mềm dễ tạo, ít phụ thuộc, và không tự động cập nhật
    • Khi lọc theo những giới hạn này, lượng phần mềm sống lâu mà nhân loại từng tạo ra hóa ra rất nhỏ
  • Chỉ một thay đổi nhỏ trong ngữ cảnh như con người, địa điểm hay chức năng mà ta muốn hỗ trợ cũng có thể làm thay đổi mạnh mức độ phù hợp của chương trình với ngữ cảnh đó
    • Trong một môi trường bị chủ nghĩa ngắn hạn chi phối, rất khó chuẩn bị cho thực tế này
  • Vì khối lượng công việc trước đây nhỏ và phạm vi áp dụng cho từng chương trình cũng thấp, nên bất kỳ chương trình mới nào quyết định làm cũng có khả năng bước vào vùng chưa biết theo cách nào đó
    • Ngay cả khi muốn thêm các “drawing lines” đặc thù vào trình soạn thảo văn bản, hàng loạt câu hỏi cũng xuất hiện
      • Con trỏ có thể nằm trên hình vẽ không
      • Khi con trỏ ở dòng khác, có thể vẽ lên một dòng không
      • Hình vẽ cao hơn dòng văn bản, vậy nó có thể chỉ hiện một phần ở mép trên màn hình không
      • Có thể vẽ lên một hình chỉ hiện một phần không
    • Câu trả lời cho những câu hỏi này đã không tối ưu trong thời gian dài, khiến các giải pháp chắp vá cứ chồng thêm lên nhau

Công cụ là cần thiết, nhưng quá tay sẽ thành nợ kỹ thuật

  • Kiểu dữ liệu, trừu tượng hóa, kiểm thử, phiên bản, máy trạng thái, tính bất biến và phân tích hình thức là những công cụ có thể dùng khi đi trên địa hình xa lạ
    • Hãy dùng vừa đủ và theo cách hợp gu của mình
  • Con người rất dễ lạm dụng những công cụ mà mình bị hấp dẫn
    • Tôi cho rằng lượng dùng lý tưởng của các công cụ này là rất nhỏ
    • Nó phải nhỏ hơn nhiều so với cảm giác ta học được trong môi trường bị chủ nghĩa ngắn hạn thống trị
  • Dùng công cụ quá mức sẽ trở thành nợ kỹ thuật
    • Nó khiến ta khó nhận ra rằng chương trình đang phức tạp một cách không cần thiết
    • Nó làm chương trình kém bền hơn trạng thái mà lẽ ra có thể đạt được
    • Khi ngữ cảnh thay đổi, nó khiến chương trình khó thay đổi hơn

Viết lại và “làm toàn bộ trong một lần”

  • Khi hiểu biết về ngữ cảnh đã ổn định, việc bỏ đi những phần lớn của chương trình và làm lại từ đầu là điều đáng giá
  • Trước khi viết lại, ta phải đưa vào đầu cùng một lúc mọi điều mình muốn ở chương trình và mọi kịch bản cần xử lý
    • Quá trình này rất khó, nhưng mục tiêu là đạt tới trạng thái có thể làm ra mọi thứ trong một lần
  • Cách cuối cùng là làm mọi thứ trong một lần
  • Trong trải nghiệm lần này, kiểm thử và phiên bản lại cản trở việc đi đến điểm cuối của sự tiến hóa đó
    • Kiểm thử khiến ta quên mất những điều đáng ra phải lo
    • Quản lý phiên bản khiến ta cứ bị níu lại với quá khứ
    • Cả hai đều phản tác dụng, và để buông chúng xuống thì cần một cú rẽ hướng lớn
  • Tôi cho rằng mọi phần mềm mình từng làm và Freewheeling Apps đều đang ở bước 6 của quỹ đạo này

Giới hạn của độ phức tạp và thiết kế hướng dữ liệu

  • Khi chương trình trở nên quá phức tạp, việc giữ trọn nó trong đầu ở bước 8 có thể là bất khả thi
    • Tôi cho rằng điều này đúng với phần lớn phần mềm từ trước tới nay, đặc biệt là phần mềm do nhiều hơn một hai người viết
    • Ngay cả một trình soạn thảo văn bản nhỏ cũng đủ gây áp lực, đến mức tôi đã dùng phần lớn thời gian trong một tháng để chuẩn bị đối mặt với nỗi sợ đó
  • Không phải mọi phần mềm đều nhất thiết phải đi tới bước 9
    • Nhiều Freewheeling Apps đủ đơn giản và tiến hóa chậm
    • Tôi cho rằng chỉ cần được một số ít người dùng cũng có thể ổn định thành trạng thái không lỗi, bất kể các lựa chọn thiết kế ban đầu
    • Đặc biệt là giờ đây tôi đã biết cách đơn giản hóa một mảnh phức tạp cốt lõi
  • Dù vậy, vẫn tốt nếu biết cách cải thiện khi giá trị xuất hiện
  • Một phương pháp có vẻ chắc chắn hữu ích để đạt tới bước 9 là data-oriented design
    • Đây không phải công cụ để áp dụng một cách mù quáng, mà là một lối tư duy nhìn chương trình ở bức tranh lớn về cách nó truy cập dữ liệu
    • Không nên để các công cụ như ECS che khuất hoạt động trí tuệ cốt lõi
  • Cách chia bước này có thể không hoàn toàn đúng
    • Có thể tôi đang đánh giá thấp những công cụ mà mình còn ít kinh nghiệm
    • Điều gì nằm sau những bước này vẫn còn là một câu hỏi mở
  • Từ bài viết năm 2019 về cách tôi lập trình, có thể thấy dấu vết của sự thay đổi trong suy nghĩ

1 bình luận

 
GN⁺ 2024-08-06
Ý kiến trên Hacker News
  • Nếu không có test thì bạn sẽ không thấy test thất bại, nên vấn đề chỉ trông như đã biến mất mà thôi
    Tôi chưa từng kiểm thử thứ gì mà không tìm ra bug, và phần lớn những thứ tôi kiểm thử đều là những thứ tôi vốn nghĩ đã có thể phát hành
    Nếu xóa test đi thì rốt cuộc rất có thể người duy nhất bạn lừa được là chính mình. Đọc bài viết thì có vẻ tác giả mệt mỏi với quản lý biến thể/cấu hình hơn là với bản thân việc test, và điều đó thì hoàn toàn có thể đồng cảm. Tuy vậy, phải có người dùng thì mới kiếm được tiền, và nếu đây là vấn đề dễ thì thị trường hẳn đã bão hòa bởi một giải pháp vạn năng rồi

    • Tôi nghĩ chuyện này phụ thuộc vào domain. Trong codebase tôi đang làm, có những phần mà test giúp ích rất nhiều cho refactor, nhưng những phần khác có nhiều hành vi UI nên test thủ công nhanh hơn hẳn
      Nếu UI hay workflow thay đổi quá nhanh, bạn sẽ không viết test vì biết rằng nó sẽ trở nên vô dụng ở vòng lặp tiếp theo; ngược lại, nếu chúng thay đổi quá chậm thì phần đó cũng chẳng mấy khi được đụng tới nữa, nên khả năng refactor đưa bug mới vào cũng thấp. Test hay type không phải chén thánh vạn năng, mà là công cụ phù hợp với từng công việc. Tôi chưa từng thấy codebase nào mà dù có độ bao phủ test tốt, bug lại không được phát hiện qua test thủ công hay sử dụng thực tế. Nói hơi phóng đại thì, nếu bạn giỏi đến mức viết được test hoàn hảo, vậy cứ viết code hoàn hảo là được. Còn nếu bạn không thể viết test hoàn hảo, làm sao biết được test đó là đầy đủ, không có bug và thực sự hữu ích?
    • Câu “test có thể cho thấy sự tồn tại của bug, nhưng không thể cho thấy sự vắng mặt của bug” phù hợp với trải nghiệm của tôi hơn
      Cứ vài tháng tôi lại tìm thấy một bug mới và chăm chỉ thêm test, nhưng vài tháng sau, người dùng thử 10 phút đầu tiên lại phát hiện thêm bug mới. Ở phiên bản mới chắc vẫn sẽ có bug để tìm, nhưng nhờ cấu trúc dữ liệu đã chọn, tôi cho rằng khá nhiều test cũ về mặt cấu trúc đã không còn cần thiết. Ít nhất với mức sử dụng nhẹ, tôi kỳ vọng chỉ cần bắt thêm vài bug nữa là sẽ khá ổn định. Test rất quý khi một đội lớn liên tục thay đổi codebase, nhưng ở đây tôi đang cố tạo ra một thứ có tập tính năng cố định
    • Tôi đã xem video Go Testing By Example của Russ Cox được đề xuất trong một thread gần đây: https://www.youtube.com/watch?v=X4rxi9jStLo
      Có rất nhiều lời khuyên hữu ích, nhưng điều tôi đặc biệt muốn nhắc tới là có thể test dựa trên một implementation đơn giản hơn, chẳng hạn implementation brute-force. Trong đó có một tầng trí tuệ sâu hơn. Mức hữu dụng của test phụ thuộc vào việc implementation của test đơn giản hơn implementation được test đến mức nào. Nói mạnh hơn, test chỉ hữu ích khi nó đơn giản hơn thứ được test. Dù viết bao nhiêu test đi nữa, cuối cùng bạn vẫn phải suy luận về code, và việc một thứ được gọi là “test” không tự động khiến nó hữu ích. Vì vậy tôi nghĩ nhiều lập trình viên cảnh giác với việc tách hàm thành các mảnh không phải là interface hữu ích chỉ để dễ viết test, hoặc test các helper đơn giản hay truy vấn nhỏ chỉ vì coverage, hoặc đưa vào đảo ngược phụ thuộc và mocking nhằm phục vụ riêng cho test. Tất nhiên mỗi việc đều có thể có lý do của nó, nhưng điều quan trọng là không đánh mất trọng tâm
    • Với tôi, mỗi lần viết unit test là bug lại lộ ra
      Thường thì tôi không hay làm theo kiểu phát triển hướng test, tức là viết test thất bại trước, nhưng thỉnh thoảng cũng có. Vì vậy các test kiểu này thường nhắm vào code mà tôi đã nghĩ là đang hoạt động. Tuy nhiên, thường thì tôi thích test harness hơn unit test[0]. Nó vẫn tìm ra bug, nhưng luồng không tuyến tính bằng. Nó khiến tôi test nhiều hơn trong quá trình phát triển và sửa bug ngay tại chỗ
      [0] https://littlegreenviper.com/testing-harness-vs-unit/
    • Việc tập trung vào unit/integration test tự động là một xu hướng tương đối hiện đại, có lẽ bắt đầu từ khoảng cuối thập niên 90. Trước đó vẫn có những phần mềm khá lớn và rất ổn định được phát hành
      Ví dụ, Linux kernel trước đây không có nhiều test, còn ngày nay có vẻ nhiều hơn. Unix hẳn cũng không có nhiều “test”. Trình biên dịch thì thường có test, nhưng hệ điều hành thì ít hơn, và các game như Doom nhiều khả năng cũng không có nhiều test. Rốt cuộc cần tìm được điểm cân bằng. Tôi biết rằng test tự động, tức unit, integration và end-to-end test, giúp tạo ra phần mềm chất lượng tốt. Đồng thời, test tốt không phải lúc nào cũng dễ viết, test tệ khiến việc refactor khó hơn, và test không ổn định ngốn rất nhiều thời gian trong các dự án lớn. Dù vậy, đặc biệt nếu phát triển một mình, việc thử nhiều cách khác nhau và tìm ra cách phù hợp với bản thân là điều thú vị
  • Phần “từ bỏ kiểm thử và phiên bản thì chương trình trở nên tốt hơn nhiều” khá khó hiểu. Không biết trong năm 2024 còn ai tự nguyện lập trình mà không có quản lý mã nguồn không
    Dù là dự án một người, khả năng làm việc trên nhiều thiết bị, xem lịch sử, quay lại và dùng branch mang lại giá trị rất lớn với chi phí gần như bằng không. Có lẽ tôi đã hiểu nhầm điều tác giả muốn nói bằng “phiên bản”

    • Tác giả đang làm một thứ nhỏ, nhanh và cố định tập tính năng. Họ quyết định xây trên một nền tảng không thay đổi thường xuyên; bối cảnh có thêm ở https://akkartik.name/freewheeling
      Nói rằng cách tiếp cận này không phù hợp với hầu hết chương trình mà người ta xây dựng ngày nay, tức là đội ngũ lớn và yêu cầu liên tục thay đổi, là đúng. Dù vậy họ vẫn dùng quản lý mã nguồn. Như bài gốc đã nói, họ chỉ ngừng lo chuyện gây xung đột merge với các fork khác. Hiện đã có hơn 24 fork, chi tiết nằm trong liên kết ở trên. Họ dùng quản lý phiên bản cho các mục đích cơ bản như sao lưu, “mình vừa đổi gì vậy?”, đưa phần mềm lên thiết bị mới. Chỉ là riêng với chương trình này, họ không còn xem quản lý phiên bản là phương tiện để hiểu và theo dõi thứ gì đã thay đổi. Chi tiết có ở https://akkartik.name/post/wart-layers. Ví dụ, họ bớt quan tâm đến việc giữ commit message sạch sẽ. Quản lý phiên bản vẫn tồn tại, nhưng trong bối cảnh hẹp này — làm ra một sản phẩm bền vững, có tập tính năng cố định và tồn tại hàng chục năm — nó đã giảm ưu tiên như một “thực hành lập trình tốt”
    • Có vẻ tác giả không ở trong tình huống phải hỗ trợ người dùng chuyên nghiệp hay người dùng trả phí, và dường như muốn tự do thử nghiệm hơn là đảm bảo một phiên bản ổn định đã biết
      Cũng có vẻ họ không xử lý hệ thống lớn hay làm việc nhóm quan trọng. Trong các điều kiện như vậy, công cụ có thể không mang lại nhiều giá trị. Người thổi sáo trong một dàn nhạc lớn chơi một bản giao hưởng phức tạp cần bản nhạc và nhạc trưởng, nhưng nếu chơi một mình theo drum machine hoặc chơi free jazz thì bản nhạc không cần thiết lắm, thậm chí có thể cản trở
    • Có vẻ tác giả đang trải qua mệt mỏi tinh thần hoặc burnout với lập trình. Nếu quản lý phiên bản gây khó chịu đến mức đó, tôi nghĩ đó là một tín hiệu khá rõ rằng nên nghỉ ngơi
    • Lập trình viên liên tục bị áp đảo bởi lựa chọn và tùy chọn. Công cụ và tinh thần thời đại nói rằng “công cụ tốt nhất” nhìn chung đi theo hướng làm một việc nào đó dễ hơn
      Nhưng nếu lúc nào cũng có 1000 lựa chọn dễ dàng, việc chọn đúng sẽ tạo ra gánh nặng nhận thức lớn. Đó cũng là một trong các lý do ngành này thần thánh hóa đủ loại best practice và tạo áp lực xã hội lên những người không tuân theo. Kiến trúc tệ và spaghetti code kinh khủng khiến công việc rất khó khăn, nhưng nếu nghi ngờ những thứ tưởng như hiển nhiên đúng và khám phá một môi trường phát triển nghiêm ngặt hơn, giảm bớt lựa chọn và công cụ, ta có thể tập trung hơn vào vấn đề cuối cùng. Quản lý phiên bản cũng khuyến khích branch chia chương trình thành các “tính năng độc lập”; lịch sử khiến ta dùng mù quáng những đơn vị tính năng có thể đã lỗi thời; còn cộng tác thường đóng cứng các ranh giới tổ chức không liên quan vào kiến trúc mã. Điều này cũng liên hệ với điều Mel Conway từng nói. Lợi ích của quản lý phiên bản là lẽ thường, nhưng ở cấp độ “giải quyết bài toán kinh doanh X” thì vẫn có đánh đổi thực sự. Việc ở cấp độ ngành gần như không thấy các đánh đổi này là điều đáng suy ngẫm
    • Trong trường hợp này, có vẻ điều tác giả nói là việc code logic phiên bản vào chính ứng dụng. Ví dụ như các endpoint API theo từng phiên bản để tương thích ngược
  • Ban đầu tôi nghĩ tác giả hoàn toàn sai, nhưng dù sao vẫn có vài insight hay
    Quy trình làm việc này rất hợp với tác giả. Phần lớn chúng ta cũng có thể nhớ lại những lúc Git hay kiểm thử tự động khiến mình nản hoặc giảm năng suất. Cũng có các giải pháp đơn giản hơn và ít gây gián đoạn hơn, như sao lưu code bằng Dropbox, FTP, v.v. Lý do cách trên phù hợp là vì tác giả đang tối ưu năng suất của bản thân trong một dự án cá nhân đầy tâm huyết có cộng tác với một số ít người. Kiểm thử tự động hữu ích, nhưng có vẻ tác giả thích làm các chương trình nhỏ đến mức giá trị của nó khó bộc lộ. Tôi vẫn cho rằng kiểm thử tự động có giá trị trong bối cảnh này, nhưng tất cả đều có thể đồng ý rằng kiểm thử tự động làm chậm tốc độ. Tất nhiên nhiều người sẽ nói phần thưởng sẽ đến về sau. Quản lý phiên bản và kiểm thử tự động giải quyết các vấn đề thực tế. Ngày nay bắt đầu một dự án mà không có quản lý phiên bản là vô lý, và kiểm thử tự động là best practice cũng có lý do. Tuy nhiên trong use case cụ thể của tác giả thì nghe vẫn hợp lý. Nếu bỏ qua phần gây tranh cãi về quản lý phiên bản/kiểm thử, các mục 7/8/9 nắm bắt hoàn hảo cách tôi suy nghĩ khi viết và refactor chương trình lớn. Viết, vứt bỏ, viết lại

    • Tôi không đồng ý về quản lý phiên bản. Dù là dự án làm một mình và không có nhiều branch phiên bản cũng vậy
      Con người mắc lỗi, và trong dự án hơn 100 nghìn dòng, biết mình đã thay đổi gì trong 3 tuần qua là rất hữu ích. Nó giúp tìm và sửa vấn đề. Tính năng tốt hơn nữa là branch, cho phép bạn thử bất cứ gì mình muốn trong khi vẫn giữ cách quay lại trạng thái ổn định trước đó. Tôi nghĩ không có kiểm thử tự động thì vẫn ổn
    • Ngay cả với dự án một người, thời gian học Git đủ để thiết lập .gitignore rồi chạy git init, git add -A, git commit -a -m "before I changed the foo function to use bar" nhằm có thể quay lại revision trước là hoàn toàn đáng giá
      Không cần thành thạo Git, nhưng chỉ riêng việc có commit message và phiên bản để quay lại đã cứu tôi vô số lần. Chưa nói đến các tính năng nâng cao hơn
  • Đây là một bài viết khá rối rắm. Tôi thật sự tò mò vì sao nó lại leo lên vị trí số 1

    • Một mặt, đây có thể là bài viết của một lập trình viên đang thử nghiệm các công cụ và kỹ thuật khác nhau để cải thiện cuộc sống của mình. Mặt khác, cũng có thể là mồi câu để kéo mọi người vào tranh luận
  • Động lực chính để có một bộ test hợp lý là giảm bớt sự nản lòng. Bộ test mang lại cho lập trình viên sự tự tin để tiến hóa hệ thống
    Nếu làm đúng, bạn cũng thường sẽ có những suy nghĩ kiểu “phần còn lại khó tìm cách test, nhưng dù sao mọi thứ vẫn ổn”. Khi độ phức tạp của tính năng tăng lên, việc test các component hoặc toàn bộ hệ thống có thể trở nên khó đến mức không kham nổi. Nhưng triết lý rằng từ bỏ test và quản lý phiên bản sẽ tạo ra chương trình tốt hơn thì không thể mở rộng vượt quá một người. Mà ngay cả vậy cũng chỉ khả thi khi người đó còn nhớ rất rõ, gần đây, mọi quyết định hiện tại và quá khứ nằm trong mã nguồn. Hơn nữa, nếu hiểu sâu về phần triển khai, thì theo định nghĩa mọi việc xác minh thay đổi đều phải thực hiện thủ công

    • Trước đây trên HN, tôi từng thấy câu chuyện về một người tuyệt đối không merge code nếu đó không phải là code chính họ viết trong ngày hôm đó
      Nội dung là nếu đến cuối ngày vẫn chưa ở trạng thái có thể merge, điều đó nghĩa là họ chưa hiểu vấn đề đủ rõ để diễn đạt nó trong một ngày, nên sáng hôm sau sẽ thử lại từ đầu. Không biết có ai nhớ chuyện này không, hay tôi đang nhầm với một trang khác hoặc một giai thoại khác
    • Với một đội lập trình một người thì điều này đúng. Thành thật mà nói, dù làm một mình, chỉ nghĩ đến việc lập trình mà không có bộ test hoặc quản lý phiên bản cũng đã thấy đáng sợ
      Tài liệu, test và quản lý phiên bản giúp giảm lượng ngữ cảnh code mà tôi phải ghi nhớ. Tôi vẫn phải nhớ các chi tiết code trước mắt, nhưng nếu đã ghi lại tài liệu, viết test, và check-in kèm commit message tốt về lý do/cách thay đổi, tôi có thể gạt đoạn code đó khỏi đầu và chuyển sang việc tiếp theo
  • Một ví dụ hay cho mục 3, rằng “những thay đổi nhỏ trong ngữ cảnh như người/địa điểm/chức năng mà bạn muốn hỗ trợ sẽ làm thay đổi mạnh mức độ chương trình phù hợp với ngữ cảnh đó”, là K9 Mail. Nó hiện đang dần trở thành Thunderbird cho Android
    K9 Mail bắt đầu với một UI không truyền thống: hiển thị danh sách tài khoản email trên màn hình chính và cho biết số thư chưa đọc cùng tổng số thư của từng tài khoản. Nó có hộp thư đến hợp nhất, nhưng không ép người dùng dùng. Tôi nhớ mình đã chọn ứng dụng này một cách rõ ràng vì muốn tách riêng một tài khoản cá nhân, một tài khoản công việc và nhiều tài khoản công việc do khách hàng cung cấp. Có lẽ nhiều người dùng K9 cũng chọn nó vì cùng lý do. Đó cũng là lý do có nhiều phàn nàn khi nhà phát triển chuyển sang UI Android truyền thống, nơi danh sách tài khoản trượt ra từ bên trái và cần thêm một lần chạm để chuyển giữa các tài khoản. Nếu thích kiểu UI đó thì có lẽ ngay từ đầu chúng tôi đã không chọn K9. Vì vậy, chỉ một thay đổi nhỏ — dù có lẽ tốn nhiều công code — đã phá hỏng mức độ phù hợp của ứng dụng với người dùng. Tôi vẫn dùng 5.600, phiên bản cuối cùng còn UI cũ, và mỗi khi mua thiết bị mới thì lại sideload nó. Đặc biệt hơn nữa, tôi chỉ dùng POP3 để truy cập tài khoản. Quy trình của tôi là xem trước trên điện thoại, xóa những gì cần xóa, nếu cần thì trả lời và BCC cho chính mình, rồi cuối cùng tải xuống trên laptop; K9 hoàn toàn khớp với workflow này. Tôi không cần gì hào nhoáng, một ứng dụng tầm thập niên 90 là đủ

  • Tôi cũng vẫn tò mò con đường này sẽ dẫn đến đâu. Có một điều chắc chắn: làm phần mềm một mình là một hoạt động hoàn toàn khác với làm trong một đội
    Về test, test là phương tiện chứ không phải mục đích. Tôi nghĩ thứ chúng ta tìm kiếm là sự tự tin. Khi tự tin vào phần triển khai, ta test ít hơn. Ngược lại, nếu có thứ bắt buộc phải tiếp tục hoạt động, ta thêm vài integration test ở ranh giới bên ngoài, nơi ít bị ảnh hưởng bởi refactoring hơn và làm chậm tốc độ ít hơn. Kiểu như chọc vào web backend từ bên ngoài thay vì test phần bên trong. Unit test rất tốt để cụ thể hóa thiết kế API mới, nhưng sau khi đã biết hướng đi, những test đó gần như vô dụng

    • Có rất nhiều lý do chính đáng để đặt test ngay cả trong dự án một người
      Việc tạm cố định câu lệnh if bằng kiểu true || để đi thẳng đến tính năng đang làm vừa tốn thời gian vừa phải gỡ bỏ sau đó. Cứ viết test rồi chạy thì có thể giữ lại làm regression test. Nếu đang deploy một ứng dụng lớn hoặc chậm, đôi khi chỉ riêng việc dùng Qt cũng khiến build hoặc chạy mất lâu; khi đó một test đơn lẻ sẽ load nhanh hơn và chạy nhanh hơn. Nếu tái hiện bug mất 45 giây thì dùng test sẽ tốt hơn. Bạn có thể tự động hóa phần nhàm chán nhất của công việc, giữ được flow, kiểm tra trạng thái bug thường xuyên tùy ý mà không phải lần nào cũng nghĩ xem có đáng kiểm tra không, và nó cũng sẽ được giữ lại làm regression test
  • Tôi thật sự thích tác giả này, và Mu là một trong những dự án tôi thích nhất. Một thứ giống Lisp machine hiện đại, lại là một dự án thú vị chạy trên QEMU

  • Tôi thích câu “phần lớn phần mềm đã bị nhiễm đến mức không thể sửa chữa bởi động cơ phục vụ thật nhiều người trong ngắn hạn”. Thay phần mềm bằng “kinh doanh” thì vẫn đúng y như vậy

  • Tất cả chúng ta đều bị choáng ngợp ở mức nào đó trước sự phức tạp trong lĩnh vực kỹ nghệ phần mềm. Đôi khi sự phức tạp đó là ngẫu nhiên
    Nhưng tôi không đồng ý rằng giải pháp là bác bỏ mọi ý tưởng đã được tạo ra trong suốt nhiều thập kỷ. Ngược lại, cũng không nên tiếp nhận mọi giải pháp theo đúng nghĩa đen hoặc dùng chúng “quá nhiều”. Theo định nghĩa, việc bị choáng ngợp xảy ra khi ta dùng một thứ gì đó quá nhiều. Hãy viết test, dùng hệ thống quản lý phiên bản, dùng abstraction, nhưng phải biết vì sao dùng. Khi “vì sao” đó không còn đúng nữa, cần đánh giá lại

    • Tôi nghĩ một nguồn lớn của vấn đề là giới học thuật. Tôi đang làm giám khảo ngoài cho sinh viên CS ở Đan Mạch, và họ vẫn học cách xây dựng abstraction upfront theo kiểu hướng đối tượng và kiến trúc củ hành
      Đây là một trong những câu thần chú gần như tệ nhất trong phát triển phần mềm. Tệ hơn nữa là những thứ này được học gần như ở mức tôn giáo. Điều lạ là trong nhiều năm qua, cách các chuyên gia viết phần mềm đã tiến bộ rất nhiều. Như đã nói, abstraction không phải bản chất là xấu trong mọi trường hợp. Cũng khó tưởng tượng việc không có một base class chứa các trường như updated, updated_by cho dữ liệu điển hình được đưa vào cơ sở dữ liệu SQL. Nhưng nhìn chung, tôi hầu như không dùng abstraction trừ khi thật sự bị bắt buộc. Vậy mà trong học thuật, họ vẫn dạy đúng chương trình mà tôi đã học 25 năm trước. Cảm giác rất kỳ lạ khi chấm điểm năng lực của sinh viên trong việc tạo ra những abstraction khổng lồ bằng UML bóng bẩy rồi triển khai chúng bằng code. Trong số họ, 90% có lẽ sẽ không bao giờ nhìn thấy thêm một sơ đồ UML nào nữa. Ít nhất là trong phạm vi nhỏ nơi tôi đang làm việc thì sẽ như vậy. Dù sao thì thực tế vẫn là thực tế
    • Lý do duy nhất khiến tôi thật sự bắt đầu dùng Git là magit
      Tôi ước mọi thứ đều có “porcelain” ở mức dòng lệnh. Nếu có output --help=ui chuẩn và giao diện kiểu dialog, có lẽ có thể tự động hóa được. Vấn đề không hẳn là bị choáng ngợp bởi độ phức tạp, mà gần hơn với việc lượng trí nhớ cơ bắp chủ động ta có thể tận dụng là có giới hạn, và ở đâu đó ta phải cắt bớt