- Thiết kế phần mềm được học sâu hơn trong dự án thực tế khi bạn nhận trách nhiệm và vấn đề thực sự trở thành việc của mình, hơn là qua các bài giảng
- Conway’s Law là góc nhìn cho rằng phần mềm lặp lại cấu trúc xã hội của tổ chức, và khác biệt giữa mã khoa học với mã công nghiệp cũng có thể phát sinh từ cấu trúc khuyến khích
- rust-analyzer tạo điều kiện để người đóng góp hiệu suất cao dễ tập trung hơn nhờ build nhanh, hỗ trợ stable, bỏ phụ thuộc C và bộ test chỉ mất vài giây
- rust-analyzer bảo vệ các tính năng độc lập bằng
catch_unwind và hạ thấp tiêu chuẩn cho PR, nhưng áp dụng tiêu chuẩn chất lượng nghiêm ngặt hơn nhiều cho spine cốt lõi
- Cấu trúc mang tính thử nghiệm có thể trở thành hiện thực dài hạn, và rust-analyzer cũng đi từ một prototype kiến trúc LSP đến việc phải duy trì thêm một compiler nữa
Thiết kế phần mềm được học tốt nhất trong thực chiến
- Thiết kế phần mềm được học tốt hơn khi nhận trách nhiệm trong dự án thực tế và trực tiếp giải quyết vấn đề, thay vì qua các khóa học chính quy
- So với khi đảm nhiệm vai trò “kiến trúc sư” trong lớp học thiết kế ở đại học và các course project, việc học chỉ thực sự tăng tốc khi các vấn đề thiết kế trở thành việc của chính mình trong dự án thực tế thứ hai là IntelliJ Rust
- Trong IntelliJ Rust đã có một vài sai lầm, nhưng không đến mức chí mạng, và nhờ quá trình đó có thể học được rất nhiều
- Kỹ nghệ phần mềm ở một khía cạnh nào đó cũng đủ đơn giản để người có tính tò mò có thể học bằng cách suy nghĩ từ nguyên lý và đọc nhiều bài viết
Cấu trúc khuyến khích và Conway’s Law
- Conway’s Law là góc nhìn cho rằng phần mềm lặp lại cấu trúc xã hội của tổ chức tạo ra nó
- Sự khác biệt giữa phần mềm công nghiệp và mã khoa học có thể bắt nguồn không phải từ bản thân tri thức xây dựng phần mềm, mà từ cấu trúc khuyến khích khiến con người tạo ra phần mềm
- Những tình huống như “một nghiên cứu sinh PhD phải công bố bài báo trong vòng 3 tháng” có thể là yếu tố quan trọng định hình dạng thức của mã khoa học
- Có thể phản ứng với cấu trúc khuyến khích theo hai cách lớn
-
Thiết kế hoặc dịch chuyển khuyến khích của dự án
- Cơ hội để thiết kế hoặc điều chỉnh cấu trúc khuyến khích của một dự án là hiếm, nhưng khi có thì tác động rất lớn
- Trọng tâm của TIGER_STYLE không nằm ở danh sách quy tắc, mà ở bối cảnh xã hội khiến các quy tắc đó trở thành lựa chọn tốt
-
Nếu không thể thay đổi, hãy thích nghi với ràng buộc
- Cấu trúc khuyến khích hiếm khi được trao cho ta đúng như mong muốn, và nếu không thể thay đổi thì phải thích nghi theo cấu trúc đó
- Ngay cả trong các dự án phần mềm công nghiệp cũng gần như không bao giờ có “đủ thời gian để làm cho đúng”, nên phải làm tốt nhất có thể trong các ràng buộc được giao
Cách rust-analyzer căn chỉnh cấu trúc với người tham gia
- rust-analyzer là một dự án vừa có chiều sâu vừa có bề rộng
- Ở chiều sâu, tính chất là một compiler giúp nó thu hút được những người đóng góp xuất sắc và tận tâm
- Ở chiều rộng, IDE kiểu cổ điển có rất nhiều tính năng chuyên biệt theo mục đích, nên phù hợp để người đang học Rust hoặc những người đóng góp cuối tuần khó tham gia thường xuyên bỏ ra một hai giờ để sửa đúng chỗ đang gây khó chịu cho họ
- Lý do rust-analyzer kiên quyết không yêu cầu build
rustc, có thể build trên stable, không có phụ thuộc C và để toàn bộ test suite chạy xong trong vài giây là nhằm thu hút người đóng góp hiệu suất cao
- Mục tiêu là mài giũa hệ thống build để mọi người có thể tập trung vào việc với borrow checker mà không phải bận tâm thứ khác
- Để thu hút người đóng góp cuối tuần, bên trong rust-analyzer được chia thành nhiều tính năng độc lập, và mỗi tính năng được bảo vệ ở runtime bằng
catch_unwind
- Tiêu chuẩn cho PR tính năng được hạ xuống thành “đường đi hạnh phúc hoạt động và có test”, và ngay cả khi phần mã đó bị crash thì vẫn được xem là chấp nhận được
- Tuy vậy vẫn cần hai điều kiện
- Vấn đề chất lượng phải được cô lập trong từng tính năng riêng lẻ và không lan sang phần khác
- Crash ở runtime không được lộ ra với người dùng, và để làm vậy các tính năng của rust-analyzer phải vận hành trên snapshot bất biến và không được phép làm bẩn dữ liệu
- Ngược lại, với spine cốt lõi nâng đỡ các tính năng, tiêu chuẩn chất lượng được áp dụng nghiêm ngặt hơn rất nhiều
Rủi ro khi cấu trúc thử nghiệm trở thành hiện thực dài hạn
- Khi thích nghi với cấu trúc khuyến khích thay vì sửa nó, cần cẩn trọng rằng tương lai là bất định và thường sẽ hiện thực hóa theo cách bất tiện nhất
- Động cơ ban đầu của rust-analyzer là tránh phải viết thêm một compiler song song bên trong IntelliJ Rust, đồng thời kiểm chứng bằng prototype một kiến trúc tốt hơn cho LSP rồi đưa những gì học được trở lại
rustc
- Vì thế mã nguồn, kể cả phần lõi, mang tính thử nghiệm rất cao
- Kết quả là cuối cùng vẫn phải duy trì thêm một compiler nữa
- Tương tự, dự án uutils cũng khởi đầu như điểm đến chính cho những người học Rust, rồi trở thành implementation coreutils của Ubuntu
Tài liệu và sách đáng tham khảo
- Không có một cuốn sách duy nhất chứa đáp án đúng, và thực chiến dường như là thành phần không thể thiếu
- Boundaries by Gary Bernhardt
- Đây là tài liệu có lời khuyên cụ thể rất vững chắc và đã khơi mở những khám phá ở tầng cao hơn
- How to Test
- Tầm quan trọng của test thì được hiểu ngay, nhưng phải mất rất lâu mới thừa nhận rằng nhiều lời khuyên về test thường được trích dẫn rộng rãi lại không thực tế, rồi mới khái niệm hóa được cách làm thực sự hiệu quả
- ∅MQ guide và các bài viết của Pieter Hintjens
- Đây là các tài liệu giúp tiếp cận lối tư duy kiểu Conway’s Law
- Kiến trúc phát triển tính năng của rust-analyzer là một dạng áp dụng optimistic merging
- Reflections on a decade of coding by Jamii
- Bài này bàn về những nội dung rất mang tính meta, và xuất sắc đến mức được đặt làm mục đầu tiên trong bộ sưu tập liên kết
- Ted Kaminski blog
- Với hình thức như những ghi chú cho một cuốn sách không tồn tại, đây là thứ gần nhất với một lý thuyết nhất quán về phát triển phần mềm
- Software Engineering at Google và The Philosophy of Software Design của Ousterhout
1 bình luận
Ý kiến trên Hacker News
Nếu tóm tắt thành một cheat sheet, thì thiết kế tốt nên là kiểu mà một ý tưởng xuyên suốt toàn bộ và giảm thiểu sự bất ngờ
Nếu hệ thống cho phép thì rốt cuộc mọi người cũng sẽ dùng theo cách đó, và những lời giải bắt đầu bằng “nếu mọi người cứ làm X thôi” thì không phải là lời giải
Hãy tách phần biến đổi dữ liệu khỏi phần sử dụng dữ liệu, mô hình dữ liệu sống lâu hơn code, và độ kết dính là gốc rễ của nhiều vấn đề
Quản lý phiên bản là điều không thể tránh, trạng thái phải được thể hiện một cách tường minh, và mỗi thông tin nên có một nguồn sự thật duy nhất
Hãy dành nhiều thời gian hơn cho việc đặt tên, nếu khó test thì thiết kế đã sai, và những quyết định không được tài liệu hóa rồi sẽ khiến bạn hối hận
Giao tiếp là chi phí nên phải có lý do chính đáng trước khi trả giá, và công việc của kỹ sư là giải quyết vấn đề bằng kinh nghiệm thực hành trong điều kiện thông tin không hoàn hảo
Bản thân bài viết cũng không thật sự nhất quán nếu nhìn từ góc độ kiến trúc phần mềm
Mô hình kiến trúc 4+1 là một khung khái niệm tốt để nghĩ về bức tranh lớn nếu bỏ qua UML, và series Pattern-Oriented Software Architecture cũng tổng hợp tốt nhiều kiểu kiến trúc mà người ta thường đi tới
Grady Booch từng làm một handbook về kiến trúc phần mềm nhưng giờ có vẻ gần như dừng lại, và thời đó trên mailing list người ta hay ghi lại kiến trúc hệ thống lớn của các công ty hay dự án mã nguồn mở quy mô lớn
Nếu đào sâu vào các tài liệu như vậy, bạn sẽ thấy kiến trúc được tạo ra để phục vụ các trọng tâm khác nhau như quy mô, an toàn, hiệu năng, khả năng tương tác, fail-safe, và mỗi mục tiêu đều có những đánh đổi thực tế đi kèm
Nếu tốt hơn theo tiêu chí đó thì dù trông như thiết kế tệ, nó vẫn là thiết kế tốt
Giao diện nên được làm sao để dùng đúng thì dễ, dùng sai thì khó, và cần nghĩ xem một người không biết dự án sẽ dùng nó thế nào
Code đúng phải dễ viết, code đáng ngờ phải dễ nhận ra, và bug nên được đẩy sang bên trái càng nhiều càng tốt
Tốt hơn là loại bỏ một lớp bug thay vì sửa từng bug riêng lẻ, và vì giao diện khó đổi hơn triển khai nên nếu giao diện đúng thì triển khai xấu xí cũng không sao
Comment và tài liệu nên giải thích vì sao code lại có hình dạng như vậy, và nếu có vẻ tồn tại cách đơn giản hơn nhưng bị ràng buộc nào đó ngăn lại thì cần ghi điều đó ra
Từ góc nhìn dữ liệu thì không nên lặp lại, vì nếu cùng một sự thật được lưu ở nhiều nơi thì sớm muộn cũng lệch nhau và sinh bug
Rời khỏi con đường đã được mài nhẵn luôn có chi phí, và đôi khi vẫn đáng làm, nhưng không nên đánh giá thấp chi phí đó
Công nghệ nhàm chán trông có vẻ kém hơn thường lại là công nghệ tốt hơn, và cần xét kỳ vọng không phải theo kiểu “có đáng làm không?” mà là “so với việc làm thứ khác thì có đáng làm không?”
Dù bạn nghĩ mình thông minh hơn người khác thì vẫn có những vấn đề mà chỉ thông minh thôi là không đủ, và có những vấn đề thật sự không thể phát hiện cho tới khi nó nổ ra, nên hãy học từ sai lầm của người khác
Ma sát là kẻ giết người thầm lặng
Lập kế hoạch là tốt, nhưng đôi khi phải tự tay thử mới biết, và mọi thứ đều tốn tiền
Nếu thiết kế mà không tính chi phí thì về sau sẽ bị ép phải đưa ra những lựa chọn khó khăn
Ngay cả với dự án làm một mình, các ràng buộc của engine vẫn đóng vai trò dẫn đường kiểu “hãy thêm tính năng lạ mới xuất hiện khi test theo cách này”
Không còn phải mỗi lần đều cầm theo một đống tài liệu khổng lồ kiểu “muốn tạo hiệu ứng âm thanh mới chạy nhiều lần trong một chuyển đổi trạng thái cụ thể thì làm thế này”
Nhà thiết kế hệ thống phải hiểu ngành đó; không cần chấp nhận hoàn toàn thuật ngữ và thói quen mô hình hóa của họ, nhưng phải hiểu lý do và góc nhìn khi họ nhìn vào các tập dữ liệu
Có những phần chúng tôi cố ý đơn giản hóa sự phức tạp của thị trường y tế để loại bỏ định nghĩa thừa và đưa ra mô hình thống nhất hơn, nhưng những thay đổi như vậy chỉ có thể làm một cách tự tin vì đã hiểu đầy đủ miền bài toán
Đặc biệt là tên gọi gần như không chết
Đôi khi nó cũng chết, nhưng đổi tên đòi hỏi nỗ lực cực lớn, nên thực sự rất đáng dành nhiều thời gian để các chuyên gia miền xem xét kỹ phương án đặt tên nhằm bảo đảm không bỏ sót điều gì
Một vài khái niệm có thể ép thông qua, nhưng phía kinh doanh như sales và marketing sẽ liên tục đòi hỏi thuật ngữ của ngành và gây áp lực để mô hình bám theo góc nhìn hiện tại của thị trường
Nếu bạn đã quyết định phá vỡ dòng chảy đó thì sự đứt đoạn ấy phải có chủ đích và mục tiêu rõ ràng
Thuộc tính cần được nhấn mạnh nhất trong phần mềm là khả năng bảo trì
Chi phí xây dựng là một câu hỏi, nhưng chi phí vận hành còn ảnh hưởng lớn hơn nhiều vì không chỉ gồm hạ tầng mà còn cả yêu cầu tính năng tích lũy, refactor code, và duy trì phiên bản phần mềm bên thứ ba
Danh sách gợi ý thường khá tốt như A Philosophy of Software Design của Ousterhout, nhưng nhìn chung nó gần với lý luận chung về phát triển phần mềm hơn là bản thân kiến trúc phần mềm
Nếu muốn xem về kiến trúc, tôi khuyên đọc các tác phẩm kinh điển như Software Architecture: Perspectives on an Emerging Discipline của Shaw/Garlan và các bài viết của Mary Shaw
Các bài gần đây như Myths and Mythconceptions: What Does It Mean to Be a Programming Language, Anyhow? hay Revisiting Abstractions for Software Architecture and Tools to Support Them, vốn khám phá vì sao lĩnh vực kiến trúc phần mềm không đi theo hướng người ta kỳ vọng, cũng rất hay
Về thực tiễn, nên xem vì sao Unix pipe và filter, REST đã thành công và ở đâu, vì sao chúng sụp đổ; kiến trúc hexagonal cũng là cốt lõi
Cá nhân tôi còn thích Beyond Procedure Calls as Component Glue: Connectors Deserve Metaclass Status, vốn nối kiến trúc phần mềm với metaobject protocol để xem đó như nền tảng mới cho ngôn ngữ lập trình và lập trình nói chung
Đây là một lời đáp lại bài Procedure Calls Are the Assembly Language of Software Interconnection: Connectors Deserve First-Class Status của Mary Shaw, đặt câu hỏi rằng nếu lời gọi thủ tục là assembly thì ngôn ngữ bậc cao sẽ trông như thế nào
Có lẽ kiến trúc phần mềm vẫn còn một tương lai sáng sủa và thực dụng hơn
Chỉ cần vài cấu trúc dữ liệu và kiểu dữ liệu, cùng một tập hàm cơ bản nhỏ, rồi ghép chúng lại là được
Một điều tôi thích ở Lisp là các kiểu dữ liệu phức tạp hơn, đặc biệt là kiểu đến từ FFI, luôn là kiểu opaque
Tôi muốn thấy một triển khai CLOS mà trong đó khi bạn định nghĩa một struct kiểu C thì sẽ có sẵn một bộ hàm chuẩn đi kèm
Cách tốt nhất để học kiến trúc không phải là tạo ra một dự án đủ lớn mà là bảo trì nó
Và còn nên làm như vậy với ít nhất hai hoặc ba dự án
Nếu dự án quá nhỏ thì kiến trúc nào cũng hoạt động tốt, và “lớn” nên được đánh giá theo số người đã làm trên đó, tốt hơn nữa là số team, chứ không phải số dòng code
Bạn cần ít nhất hai dự án khác nhau để có cái mà so sánh, và tôi từng thấy những người bị mắc kẹt hàng chục năm trong một dự án nên không biết các cách giải quyết vấn đề hiện đại
Nhưng thực tế thì thường người tạo ra dự án sẽ được thăng chức thành kiến trúc sư, còn người bảo trì thì hiếm khi
Ở Google điều này còn rõ hơn vì bạn phải ra mắt thứ mới thì mới được thăng chức, bảo trì thì không, nên nếu có thể, rời đi ngay sau khi ra mắt thường có lợi hơn
Nghịch lý là người ở vị trí tốt nhất để trở thành kiến trúc sư có thể lại là contractor bên ngoài được giao bảo trì những dự án cũ mà không ai trong công ty muốn đụng vào
Họ phải duy trì kiến trúc và đi qua nhiều dự án nên có thể so sánh được
Tuy nhiên, nếu tính phí theo giờ thì lại có rủi ro họ sẽ làm kiến trúc phức tạp quá mức để tính được nhiều giờ hơn
Trong bối cảnh này, tôi thực sự khuyên đọc Architecture of Open Source Applications
Đây là một series sách mà mỗi chương do chính người bảo trì của dự án đó viết, nên bạn có thể học kiến trúc qua ví dụ
Nó không chỉ cho bạn biết kiến trúc là gì mà còn cho bạn thấy các ràng buộc đã tạo ra nó, thường là lịch sử và tầm nhìn thay đổi của dự án
Do hạn chế của sách nhiều tác giả nên không phải chương nào cũng hay hoặc thú vị như nhau, và tất cả đều đã cũ, nhưng vẫn đáng đọc
http://aosabook.org/
Tôi muốn dành nhiều thời gian hơn để có được mô hình tinh thần tốt hơn về dự án mình đang làm, nhưng nếu bắt đầu ghét ngôn ngữ lập trình, một lựa chọn kiến trúc cụ thể, hoặc một phần nào đó đã trở nên phức tạp đến mức dường như không đáng để bỏ thời gian vào nữa, thì động lực của tôi giảm đi rất nhiều
Tùy dự án, nhưng làm việc như một “full-stack developer” cho tôi cảm giác như đang rút hết niềm vui của việc lập trình
Tôi đã dành 40 giờ mỗi tuần để nhìn vào dự án nhàm chán nhất có thể tưởng tượng được rồi
Không có lấy một dự án nào là hoàn hảo
Và nếu ngôn ngữ lập trình là vấn đề lớn đến vậy thì tốt hơn nên đổi thuyền
Tôi nghĩ ai cũng nên có khả năng làm việc với nhiều ngôn ngữ, nhưng rốt cuộc lựa chọn là của bạn
Bạn phải bảo đảm rằng quyết định mình dành nhiều thời gian nhất có thật sự là quyết định quan trọng nhất hay không
Một cấu trúc dữ liệu được thiết kế tốt có ảnh hưởng đến hiệu năng và khả năng bảo trì lớn hơn rất nhiều so với framework, ngôn ngữ hay nền tảng
Cá nhân tôi phải sống và làm việc cùng ADHD mỗi ngày, nên tôi phải liên tục đẩy để có tiến triển; điều đó có nghĩa là chọn ra các quyết định không quan trọng rồi chốt chúng, để tập trung vào phần không gian vấn đề còn lại thật sự cần suy nghĩ cẩn trọng
Những cụm như “clean code” hay “beautiful code” không giúp junior học các thực hành tốt về kiến trúc phần mềm bao nhiêu
Khi junior hỏi vì sao dùng ORM mà senior trả lời “vì nó sạch hơn” thì thứ còn lại chỉ là dấu hỏi
Tốt hơn là định nghĩa một danh sách mục tiêu rõ ràng
Các tiêu chí như khả năng bảo trì, hiệu năng và khả năng mở rộng, hiệu suất, khả năng phục hồi, observability, khả năng kiểm thử và đã được kiểm thử, tính bảo mật, mức độ dễ đọc với người mới vào đều cần được cân bằng với nhau
Càng thêm tiêu chí thì khi lưỡng lự bạn càng dễ đưa ra lựa chọn tốt hơn, và điều đó cũng có ý nghĩa với người ngoài team phát triển để có thể thống nhất với khách hàng rằng họ đang trả tiền cho điều gì
Một dự án dành phần lớn vòng đời của nó ở chế độ bảo trì, và đó là dấu hiệu tốt cho thấy dự án đã thành công, nên khả năng bảo trì cũng có thể được định nghĩa
Một điểm khởi đầu tốt là khả năng thêm tính năng mới mà không phá vỡ kiến trúc, tốt hơn nữa là không phá vỡ cả chữ ký của một method đơn lẻ
Cần cực kỳ thận trọng với abstraction
Câu “abstraction thường che giấu việc thứ bạn muốn thực ra đơn giản đến mức nào” là rất đúng, và ORM chính là ví dụ điển hình cho điều đó
Trong đa số trường hợp, dữ liệu nên được đối xử như công dân hạng nhất, và điều đó cũng giúp việc cộng tác với DBA tốt hơn
Vừa không rơi vào tối ưu hóa sớm, vừa nghĩ đến những thứ nằm ngoài happy path cũng là một thách thức, nhưng nó giúp ngăn việc lao đầu vào triển khai một ý tưởng hay mà chưa nhìn thấy hết mặt lợi hại
Nếu tôi tưởng tượng người sẽ phải đụng vào code của mình đang có một ngày cực kỳ tệ, điều đó giúp tôi viết sao cho dễ đọc hơn
Comment ở nhiều chỗ, biến cục bộ dù có thể lược bỏ nhưng vẫn giữ lại, hay tên biến đều là những thứ thuộc về điều đó
Framework phải được chọn cẩn thận; nó là đầy tớ tốt nhưng là ông chủ tồi
Cách diễn đạt trong bài “đừng làm frameworker, hãy làm engineer” là rất đúng
Bản thân việc có quan điểm mạnh không phải là điều tôi ghét, và với library hay công cụ thì đó thậm chí còn có thể là đặc tính tuyệt vời
Library hay công cụ đó có khả năng sở hữu rất nhiều chuyên môn miền trong đúng phạm vi của nó
Nhưng với framework thì quan điểm mạnh rất dễ biến thành kiểu “đã cầm búa thì cái gì cũng trông như đinh”
Không phải lúc nào cũng vậy hay lúc nào cũng là vấn đề, nhưng khi giáo điều chính thống của một miền bị đem áp rộng ra, thì lập luận vốn đúng trong miền đó có thể đổ vỡ trong bối cảnh rộng hơn
Vì vậy tôi thích framework có tính mô-đun, nơi bạn có thể cắm ORM khác hoặc tầng tích hợp persistence khác, và thay được router, validator hay những thành phần khác không phù hợp với bài toán
Điều này đặc biệt có giá trị khi bên trong hệ sinh thái framework có các lựa chọn thay thế thuộc nhiều paradigm khác nhau
Vì khi đó bạn có thể tìm một công cụ dựng sẵn gần đúng nhất, thấy rõ nhược điểm của nó, và chọn thứ có thể bù đắp khi cần
Để bảo trì tốt thì chống lại hội chứng NIH là điều cực kỳ quan trọng
Đây là một rủi ro thường trực có thể hút tài nguyên với tốc độ đáng kinh ngạc
Đồng thời, cũng có những thành phần mà tự tinh chỉnh hoặc sở hữu hoàn toàn lại đem lại lợi ích lớn, và phần khó là biết cái nào thuộc phía nào
Tôi hoàn toàn đồng cảm với việc đặt khả năng bảo trì lên đầu danh sách
Từ góc nhìn kỹ nghệ hệ thống, kiến trúc phần mềm giống như thiết kế đường ống
Nó rất quan trọng, nhưng con người không sống trong đường ống mà sống trong ngôi nhà có hệ thống đường ống đó
Nếu đường ống không tính đến phần còn lại của ngôi nhà thì chi phí sửa chữa có thể cực kỳ lớn
Bài viết hay
Học “kiến trúc phần mềm” là hiểu rằng không có một đáp án đúng duy nhất
Đây vừa là nghệ thuật vừa là khoa học
Về tài liệu đọc, tôi khuyên Simplify IT - The art and science towards simpler IT solution
https://nocomplexity.com/documents/reports/SimplifyIT.pdf
Bài nói chuyện của Gary Bernhardt thực sự rất đặc biệt
Nó chứa rất nhiều khái niệm có thể dẫn bạn đến những nơi thú vị khác