Clean Coder – Phụ Lục A – Công Cụ

Vào năm 1978, tôi đang làm việc tại Teradyne trong hệ thống kiểm tra điện thoại mà tôi đã mô tả trước đây. Hệ thống này có khoảng 80 nghìn dòng code ngôn ngữ assembly M365. Chúng tôi lưu giữ source code trên những cuộn băng từ.

Những cuộn băng từ này tương tự như các cuộn băng ca nhạc stereo 8 bài rất phổ biến trong những năm 70. Cuộn băng này là một vòng lặp vô hạn, và đầu đọc băng chỉ có thể di chuyển theo một hướng. Các cuộn băng này có độ dài 10’, 25’, 50’, và 100’. Băng càng dài thì nó càng mất nhiều thời gian để “tua lại” do đầu đọc băng phải di chuyển nó về phía trước đến khi nó tìm được “điểm nạp”. Một cuộn băng 100’ mất 5 phút để đi tới điểm nạp, vì vậy chúng tôi phải lựa chọn chiều dài của các cuộn băng rất thận trọng[1].

Về mặt logic, các cuộn băng được chia thành những file. Bạn có thể có nhiều file trên một cuộn băng nếu nó chứa vừa. Để tìm một file, bạn phải nạp cuộn băng và sau đó tiến dần qua từng file cho đến khi bạn tìm thấy file bạn muốn. Chúng tôi giữ một danh sách thư mục source code trên tường để chúng tôi có thể biết được bao nhiêu file cần được bỏ qua cho đến khi chúng tôi tới được file mong muốn.

Có một cuộn băng chủ 100’ chứa bản copy của source code trên một giá trong phòng thí nghiệm. Nó được dán nhãn MASTER. Khi chúng tôi muốn sửa một file, chúng tôi nạp cuộn băng nguồn MASTER này vào một đầu đọc và một cuộn băng 10’ trống vào một đầu đọc khác. Chúng tôi tìm trên cuộn băng MASTER cho đến khi chúng tôi tới được file cần. Sau đó chúng tôi copy file đó sang cuộn băng trống đó. Rồi chúng tôi “tua lại” cả hai cuộn băng và đặt lại cuộn MASTER trở lại giá.

Có một danh sách thư mục đặc biệt của cuộn MASTER nằm trên bảng thông báo trong phòng thí nghiệm. Mỗi khi chúng tôi tạo một bản copy của những file chúng tôi cần chỉnh sửa, chúng tôi lại đặt một ghim màu lên bảng ngay cạnh tên của file đó. Đó là cách chúng tôi check out file!

Chúng tôi chỉnh sửa các cuộn băng trên một màn hình. Phần mềm chỉnh sửa văn bản của chúng tôi, ED-402, thực sự là rất tốt. Nó rất giống với vi[2]. Chúng tôi phải đọc một “trang” trong cuộn băng, chỉnh sửa nội dung, và sau đó ghi trang đó ra và đọc trang tiếp theo. Một trang thông thường dài khoảng 50 dòng code. Bạn không thể nhìn phía trên cuộn băng để biết được những trang nào đang tới, và bạn cũng không thể quay lại cuộn băng để xem trang nào bạn vừa chỉnh sửa. Vì vậy chúng tôi đã dùng các danh sách.

Quả thực là chúng tôi phải đánh dấu các danh sách của chúng tôi với tất cả những thay đổi chúng tôi muốn thực hiện, và sau đó chúng tôi chỉnh sửa file theo như danh sách đó. Không ai ghi hoặc chỉnh sửa code trực tiếp tại đầu cuối! Bởi đó là tự sát.

Một khi những thay đổi được thực hiện cho tất cả các file cần phải chỉnh sửa, chúng tôi kết hợp những file này với cuộn băng MASTER để tạo ra một cuộn băng làm việc. Đây là cuộn băng chúng tôi dùng để chạy biên dịch và các bài test.

Một khi chúng tôi hoàn tất kiểm tra và chắc chắn những thay đổi của chúng tôi đều hoạt động, chúng tôi nhìn vào bảng. Nếu không có ghim nào mới trên bảng thì chúng tôi chỉ cần dán nhãn lại cuộn băng đang dùng là MASTER và bỏ ghim trên bảng của chúng tôi xuống. Nếu đã xuất hiện ghim mới trên bảng, chúng tôi sẽ bỏ ghim của chúng tôi đi và trao cuộn băng đang dùng của chúng tôi cho người đã gắn ghim mới lên bảng. Họ sẽ phải thực hiện việc kết hợp.

Ba bọn tôi, mỗi người có một màu ghim riêng, vì vậy chúng tôi có thể dễ dàng biết ai đang kiểm tra file nào. Và do tất cả chúng tôi đều làm việc tại cùng một phòng và luôn trao đổi với nhau nên chúng tôi luôn nhớ được trạng thái của tấm bảng đó trong đầu mình. Vì vậy thường thì tấm bảng đó khá thừa thãi, và chúng tôi không sử dụng nó thường xuyên lắm.

Các công cụ

Ngày nay các lập trình viên phần mềm có rất nhiều công cụ để lựa chọn. Phần lớn đều không đáng để động tới, nhưng cũng có vài công cụ mà mọi lập trình viên phần mềm bắt buộc phải thành thạo. Chương này sẽ mô tả bộ công cụ cá nhân hiện tại của tôi. Tôi không thực hiện một cuộc khảo sát toàn diện về tất cả các công cụ có ngoài đó, vì vậy đây không nên xem như một đánh giá toàn diện. Nó chỉ đơn giản là những thứ tôi đang dùng.

Quản lý source code

Khi nghĩ tới quản lý source code, các công cụ mã nguồn mở luôn luôn là sự lựa chọn tốt nhất của tôi. Tại sao? Bởi vì chúng được viết bởi các lập trình viên và dành cho các lập trình viên. Các công cụ mã nguồn mở là thứ các lập trình viên viết cho chính họ khi họ cần sử dụng một công cụ nào đó.

Ngoài ra, cũng có một số ít một số hệ thống quản lý source code phiên bản “enterprise”, thương mại và đắt đỏ. Tôi thấy những công cụ này không để bán cho các lập trình viên mà chủ yếu để bán cho những người quản lý, những người điều hành, và các “nhóm công cụ”. Danh sách chức năng của chúng rất ấn tượng và hấp dẫn. Không may thay, chúng thường không có những chức năng mà các lập trình viên thực sự cần. Đứng đầu trong đó là tốc độ.

Hệ thống quản lý source code “enterprise”

Có thể công ty của bạn đầu tư một khoản ngân sách nhỏ vào một hệ thống quản lý source code “enterprise”. Nếu như vậy, tôi xin chia buồn. Có lẽ về mặt chính trị sẽ không thích hợp nếu bạn đi xung quanh và nói với mọi người rằng “Bác Bob nói là không nên dùng nó.” Tuy nhiên, có một giải pháp dễ dàng.

Bạn có thể lấy source code của bạn trong hệ thống “enterprise” đó vào cuối mỗi chu kỳ (chu kỳ hai tuần hoặc tương tự) và dùng một trong những hệ thống mã nguồn mở ở khoảng giữa mỗi chu kỳ. Điều này giúp cho mọi người đều vui vẻ, bạn không vi phạm bất cứ quy định nào của tổ chức, và vẫn giữ cho năng suất của bạn được đảm bảo.

Khóa bi quan Vs Khóa lạc quan

Khóa bi quan dường như là một ý tưởng tốt vào những năm 80. Sau cùng thì cách đơn giản nhất để kiểm soát các vấn đề về việc cập nhật đồng thời là hãy xếp nối tiếp chúng. Vì vậy nếu tôi chỉnh sửa một file, bạn tốt hơn hết là không. Quả thực là hệ thống các ghim màu sắc mà tôi đã dùng vào cuối những năm 70 là một dạng của khóa bi quan. Nếu có một ghim ở một file nào đó thì bạn không được chỉnh sửa file đó.

Dĩ nhiên, khóa bi quan cũng có vấn đề của nó. Nếu tôi khóa một file và sau đó đi du lịch thì những người khác muốn chỉnh sửa file đó sẽ bị ách tắc. Quả thực, ngay cả nếu tôi chỉ khóa file đó trong một hoặc hai ngày, thì như vậy cũng đủ để làm trễ những người khác cần thay đổi.

Các công cụ của chúng ta bay giờ đã tốt hơn rất nhiều trong việc kết hợp các file source code được chỉnh sửa cùng lúc. Nó thực sự đáng kinh ngạc nếu bạn suy nghĩ về nó. Các công cụ đó nhìn vào hai file khác nhau và vào file gốc của hai file đó, và sau đó chúng dùng nhiều thuật toán để tìm ra cách kết hợp những thay đổi đồng thời đó. Và chúng làm công việc đó khá tốt.

Vì vậy kỷ nguyên của khóa bi quan đã kết thúc. Chúng ta không cần phải khóa file khi chúng ta lấy source code về nữa. Chúng ta hoàn toàn không phải bận tâm tới việc lấy từng file đơn lẻ về nữa. Chúng ta chỉ việc lấy source code của cả hệ thống và chỉnh sửa bất cứ file nào mà chúng ta cần.

Khi chúng ta sẵn sàng đưa những thay đổi của chúng ta vào hệ thống, chúng ta sẽ thực hiện một quá trình “cập nhật”. Nó sẽ nói cho chúng ta xem có ai đó khác đã đưa source code mới vào trước chúng ta không, nó tự động kết hợp các thay đổi, tìm những xung đột, và giúp chúng ta làm nốt công việc kết hợp còn lại. Sau đó chúng ta commit phần code đã kết hợp lên hệ thống.

Tôi sẽ có nhiều điều để nói về vai trò mà các bài test tự động và hệ thống tích hợp liên tục CI tham gia cùng với quy trình này sau trong chương này. Vào lúc này, tôi chỉ muốn nói là chúng ta không bao giờ được đưa code vào hệ thống nếu nó chưa vượt qua được tất cả các bài test. Không bao giờ được làm như vậy.

CVS/SVN

Một hệ thống quản lý source code đã cổ là CVS. Nó khá tốt khi được dùng trước đây nhưng không còn phù hợp đối với những dự án ngày nay. Mặc dù nó rất tốt trong việc xử lý những file và thư mục riêng lẻ, nhưng nó không được tốt trong việc đổi tên file hoặc xóa thư mục. Và… Thôi, nói càng ít về nó thì càng tốt.

Subversion SVN, mặt khác, thì hoạt động rất tốt. Nó cho phép bạn lấy về toàn bộ source code chỉ với một thao tác. Bạn có thể dễ dàng cập nhật, kết hợp, và đưa code lên hệ thống. Nếu bạn không làm việc với các nhánh thì hệ thống SVN khá đơn giản để quản lý.

Rẽ nhánh

Mãi đến năm 2008, tôi đã tránh tất cả từ dạng đơn giản nhất của việc rẽ nhánh. Nếu một lập trình viên tạo ra một nhánh, thì nhánh đó phải được mang trở lại nhánh chính trước khi kết thúc chu kỳ làm việc. Quả thực là tôi khắt khe về việc rẽ nhánh đến nỗi rất hiếm khi thực hiện rẽ nhánh trong các dự án mà tôi tham gia.

Nếu bạn đang dùng SVN, thì tôi vẫn nghĩ đó là một chính sách tốt. Tuy nhiên, có một vài công cụ mới đã thay đổi cuộc chơi hoàn toàn. Chúng là các hệ thống quản lý source code phân tán. Trong đó, git là hệ thống quản lý source code phân tán ưa thích của tôi. Hãy để tôi nói với bạn về nó.

git

Tôi bắt đầu dùng git vào cuối năm 2008, và nó đã thay đổi mọi thứ về cách mà tôi thường quản lý source code. Hiểu được tại sao công cụ này lại là một thứ thay đổi cuộc chơi như vậy thì nằm ngoài phạm vi của cuốn sách này. Nhưng việc so sánh Hình A-1 và Hình A-2 có thể đáng với vài từ mà tôi không định nói ra ở đây.

Hình A-1 chỉ ra những gì được phát triển trong vài tuần đối với dự án FitNesse khi mà nó vẫn được quản lý bởi SVN. Bạn có thể nhìn thấy hiệu quả của quy định không rẽ nhánh khắt khe của tôi. Chúng tôi chỉ đơn giản là không rẽ nhánh. Thay vào đó, chúng tôi thực hiện việc cập nhật, kết hợp, và đẩy code vào nhánh chính rất thường xuyên.

Hình A-1. FitNesse dưới sự quản lý của Subversion
Hình A‑2. FitNesse dưới sự quản lý của git

Hình A-2 chỉ ra những gì được phát triển trong vài tuần của cùng dự án đó nhưng dùng git. Như bạn có thể thấy, chúng tôi rẽ nhánh và kết hợp code ở mọi nơi. Đây không phải là bởi tôi buông lỏng chính sách không rẽ nhánh của tôi; mà nó đơn giản trở thành cách rõ ràng và thuận tiện nhất để làm việc. Các lập trình viên riêng lẻ có thể tạo ra những nhánh rất ngắn và sau đó kết hợp chúng với code của những người khác.

Cũng lưu ý rằng bạn không thể thấy một nhánh chính thực sự. Đó là bởi vì không có cái nào như vậy. Khi bạn dùng git thì không có thứ gì được gọi là kho chứa trung tâm, hoặc nhánh chính. Mỗi lập trình viên giữ một bản copy của toàn bộ lịch sử của dự án trên máy tính cục bộ của họ. Họ check in và check out bản copy cục bộ đó, và sau đó kết hợp nó với những người khác khi cần.

Sự thực là tôi có giữ một kho chứa đặc biệt mà tôi đẩy tất cả các phiên bản đã phát hành và các bản build tạm thời vào đó. Nhưng để gọi kho chứa này là nhánh chính sẽ là một điểm thiếu sót. Nó thực sự chỉ là một bản sao thuận tiện toàn bộ lịch sử mà mỗi lập trình viên lưu cục bộ mà thôi.

Nếu bạn không hiểu điều này, thì cũng không sao. git là thứ khá khó hiểu ban đầu. Bạn phải làm quen với cách nó làm việc. Nhưng hãy để tôi nói với bạn điều này: git, và các công cụ tương tự như vậy sẽ là tương lai của quản lý source code.

IDE/Trình soạn thảo

Là những lập trình viên, chúng ta dùng thời gian nhiều nhất để đọc và chỉnh sửa code. Các công cụ chúng ta dùng cho mục đích này đã thay đổi lớn lao qua nhiều thập niên. Một số hết sức mạnh mẽ, và một số chỉ thay đổi chút ít so với những năm 70.

vi

Bạn sẽ nghĩ rằng những ngày dùng vi làm trình soạn thảo code chính đã qua từ lâu. Ngày nay có rất nhiều công cụ vượt xa vi, và các trình soạn thảo văn bản đơn giản giống như nó. Nhưng sự thực là vi đã được sử dụng phổ biến lại đáng kể bởi sự đơn giản, dễ dùng, tốc độ, và linh hoạt của nó. vi có thể không mạnh mẽ như Emacs, hay eclipse, nhưng nó vẫn là một trình soạn thảo nhanh và mạnh mẽ.

Phải nói rằng, tôi không phải là một người dùng vi nhiều nữa. Đã có thời gian tôi được biết đến như là một “thánh” vi, nhưng những ngày đó đã trôi qua lâu rồi. Thỉnh thoảng tôi vẫn dùng vi nếu tôi cần phải chỉnh sửa nhanh một file văn bản. Tôi thậm chí gần đây vẫn còn dùng nó để sửa nhanh các file source code Java trong môi trường làm việc từ xa. Nhưng lượng code thực sự mà tôi đã viết bằng vi trong 10 năm gần đây là rất nhỏ.

Emacs

Emacs vẫn là một trong những trình soạn thảo mạnh mẽ nhất hiện nay, và có lẽ sẽ vẫn duy trì như vậy trong nhiều thập niên tới. Mô hình lisp bên trong của nó đảm bảo cho điều đó. Là một công cụ chỉnh sửa đa mục đích, không có công cụ nào khác có thể làm được như nó. Nhưng mặt khác, tôi nghĩ Emacs không thể thực sự cạnh tranh được với các IDE chuyên dụng hiện đang thống trị. Việc sửa code không phải là một công việc chỉnh sửa đa dụng.

Vào những năm thập niên 90, tôi đã là một tín đồ của Emacs. Tôi đã định không dùng bất cứ thứ gì khác nữa. Các trình soạn thảo trỏ-và-nhấn ngày nay là các món đồ chơi đáng cười mà không một lập trình viên nào sử dụng một cách nghiêm túc. Nhưng vào đầu thập niên 00, tôi đã được giới thiệu IntelliJ, lựa chọn IDE hiện tại của tôi, và tôi sẽ không bao giờ phải hối tiếc.

Eclipse/IntelliJ

Tôi là một người dùng IntelliJ. Tôi yêu nó. Tôi dùng nó để viết Java, Ruby, Clojure, Scala, Javascript, và nhiều ngôn ngữ khác. Công cụ này được viết bởi những lập trình viên hiểu những lập trình viên cần gì khi viết code. Trải qua nhiều năm, họ hiếm khi làm tôi thất vọng và hầu như luôn luôn làm tôi hài lòng.

Eclipse cũng tương đương về sức mạnh và phạm vi như IntelliJ. Cả hai đơn giản là bước nhảy vọt và trên tầm Emacs khi nói đến code Java. Cũng có những IDE khác trong danh mục này, nhưng tôi sẽ không đề cập chúng ở đây bởi vì tôi chưa có trải nghiệm trực tiếp với chúng.

Các chức năng mà đặt những IDE này trên tầm những công cụ như Emacs bao gồm các cách cực kỳ mạnh mẽ trong việc giúp bạn thao tác với code. Trong IntelliJ, lấy ví dụ, bạn có thể trích xuất một superclass từ một class với chỉ một lệnh. Bạn có thể đổi tên biến, trích xuất các method, và chuyển đổi kế thừa thành composition, ngoài ra còn nhiều chức năng tuyệt vời khác.

Với những công cụ này, việc sửa code không còn chỉ là thao tác với dòng và ký tự mà nó còn gồm nhiều thao tác phức tạp khác. Thay vì nghĩ về những ký tự tiếp theo và những dòng mà bạn cần phải gõ, thì bạn hãy nghĩ về những chuyển đổi mà bạn cần phải thực hiện tiếp theo. Nói ngắn gọn, mô hình lập trình đã cực kỳ khác và có năng suất rất cao.

Dĩ nhiên, sức mạnh này cũng đi với cái giá của nó. Để sử dụng thành thạo được những IDE này sẽ cần nhiều thời gian và thời gian thiết lập môi trường cho một dự án cũng không ít. Những công cụ này cũng không hề nhẹ. Chúng cần rất nhiều tài nguyên máy tính để chạy.

TextMate

TextMate là một trình soạn thảo nhẹ và mạnh mẽ. Nó không thể thực hiện những thao tác tuyệt vời như IntelliJ và Eclipse có thể làm. Nó không có engine lisp và thư viện mạnh mẽ như Emacs. Nó không có được tốc độ và sự nuột nà như vi. Nhưng nó lại có ưu điểm là thời gian làm quen ngắn, và các thao tác làm việc trực quan.

Tôi thỉnh thoảng vẫn sử dụng TextMate, đặc biệt là khi code C++. Tôi sẽ dùng Emacs cho một dự án C++ lớn, nhưng tôi sẽ không cần phải bận tâm tới Emacs cho những nhiệm vụ ngắn code C++ mà tôi có.

Theo dõi vấn đề

Hiện tại tôi đang dùng Pivotal Tracker. Nó là một hệ thống gọn gàng và đơn giản để sử dụng. Nó rất phù hợp với Agile/cách tiếp cận vòng lặp. Nó cho phép tất cả các bên và các lập trình viên giao tiếp với nhau nhanh chóng. Tôi rất hài lòng với nó.

Đối với các dự án rất nhỏ, tôi thỉnh thoảng dùng Lighthouse. Nó rất dễ dàng và nhanh chóng để thiết lập và sử dụng. Nhưng các chức năng của nó không đủ mạnh để theo dõi vấn đề.

Tôi cũng đơn giản dùng wiki. Các wiki tốt cho các dự án nội bộ. Chúng cho phép bạn thiết lập bất cứ kế hoạch nào mà bạn muốn. Bạn không bị buộc phải theo một quy trình nhất định hoặc một cấu trúc cứng nhắc. Chúng rất dễ hiểu và dễ dùng.

Đôi khi hệ thống theo dõi vấn đề tốt nhất tất cả chỉ là một bộ thẻ và một bảng thông báo. Bảng thông báo được chia thành các cột như “Cần Làm”, “Đang Thực Hiện”, và “Hoàn Thành”. Các lập trình viên chỉ đơn giản di chuyển những tấm thẻ từ cột này sang cột bên cạnh khi thích hợp. Quả thực, đây có thể là hệ thống theo dõi vấn đề phổ biến nhất được dùng bởi các nhóm agile ngày nay.

Lời khuyên tôi đưa ra cho các khách hàng là hãy bắt đầu với một hệ thống thủ công bằng bảng thông báo trước khi bạn mua một công cụ theo dõi. Một khi bạn làm chủ được hệ thống thủ công này, thì bạn sẽ có kiến thức bạn cần để lựa chọn ra một công cụ thích hợp. Và quả thực lựa chọn phù hợp có thể chỉ đơn giản là tiếp tục dùng hệ thống thủ công này.

Đếm bug

Các nhóm lập trình viên chắc chắn cần một danh sách các vấn đề để làm việc. Những vấn đề này bao gồm các nhiệm vụ mới và các chức năng cũng như các bug. Đối với bất cứ một nhóm cỡ vừa nào (từ 5 tới 12 lập trình viên) thì kích thước của danh sách đó có thể từ vài chục cho tới vài trăm. Nếu không phải là hàng nghìn.

Nếu bạn có tới hàng nghìn bug, thì tức là có gì đó đang sai sai. Nếu bạn có hàng nghìn chức năng và/hoặc các nhiệm vụ thì cũng tức là có gì đó đang sai sai. Nói chung thì danh sách các vấn đề cần phải tương đối nhỏ, và do đó mới có thể quản lý được với một công cụ nhẹ như wiki, Lighthouse hoặc Pivotal Tracker.

Có một vài công cụ thương mại có vẻ như khá tốt. Tôi đã nhìn thấy một vài khách hàng dùng chúng nhưng vẫn chưa có cơ hội để làm việc với chúng trực tiếp. Tôi không phản đối những công cụ như thế này, miễn là số lượng các vấn đề cần được giữ ở một con số nhỏ và quản lý được. Khi các công cụ theo dõi vấn đề bị buộc phải theo dõi hàng nghìn vấn đề, thì từ “theo dõi” đã mất đi ý nghĩa của nó. Chúng sẽ trở thành “những đống vấn đề” (và thường thì nó cũng bốc mùi như đống rác).

Build liên tục

Mới gần đây tôi đã dùng Jenkins làm hệ thống Build Liên Tục của tôi. Nó nhẹ, đơn giản, và hầu như không phải mất thời gian để học sử dụng. Bạn tải nó xuống, chạy, thực hiện một vài cấu hình nhanh chóng và đơn giản, đã xong và bạn chạy được hệ thống. Rất tuyệt.

Triết lý của tôi về hệ thống build liên tục rất đơn giản: Kết nối nó với hệ thống quản lý source code. Bất cứ khi nào ai đó check in code thì nó cần phải tự động build và sau đó báo cáo lại trạng thái cho cả nhóm.

Nhóm bắt buộc lúc nào cũng phải duy trì quá trình build. Nếu một lần build bị lỗi, nó cần phải là một sự kiện khẩn cấp “dừng máy dập” và nhóm cần phải họp để giải quyết vấn đề nhanh chóng. Không một hoàn cảnh nào được phép để cho một lần build lỗi tồn tại quá một ngày.

Đối với dự án FitNesse tôi yêu cầu mỗi lập trình viên chạy một script build liên tục trước khi họ commit code. Mỗi lần build mất dưới 5 phút, vì vậy nó không phải việc gì quá nặng nề. Nếu có vấn đề gì, những lập trình viên sẽ phải giải quyết chúng trước khi commit code. Vì vậy mỗi lần build tự động hiếm khi có vấn đề gì xảy ra. Nguyên nhân phổ biến nhất của việc build tự động bị lỗi thường do các vấn đề liên quan đến môi trường do môi trường build tự động của tôi khá khác biệt so với môi trường phát triển của các lập trình viên.

các công cụ unit test

Mỗi ngôn ngữ đều có một công cụ để thực hiện unit test riêng. Những công cụ ưa thích của tôi là JUnit cho Java, RSPEC cho Ruby, NUnit cho .Net, Midje cho Clojure, CppUTest cho C và C++.

Dù công cụ unit test nào mà bạn lựa chọn, thì tất cả chúng đều phải hỗ trợ một vài chức năng cơ bản sau.

  1. Nó cần phải nhanh và dễ dàng để chạy các test. Cho dù nó được thực hiện thông qua các plugin của IDE hay các công cụ dòng lệnh đơn giản thì điều này không quan trọng, miễn là các lập trình viên có thể chạy những bài test một cách nhanh chóng. Thao tác để chạy các bài test cần phải rất nhanh chóng. Lấy ví dụ, tôi chạy các bài test CppUTest bằng cách gõ command-M trong TextMate. Tôi đã thiết lập lệnh này để chạy makefile của tôi, file này tự động chạy các bài test và in báo cáo một dòng nếu tất cả các bài test đều vượt qua. Cả JUnit và RSPEC đều được hỗ trợ trong IntelliJ, vì vậy tất cả tôi phải làm là nhấn nút. Đối với NUnit, việc sử dụng plugin ReSharper cho phép tôi có được một nút test.
  2. Công cụ này cần phải cho bạn một dấu hiệu pass/fail một cách trực quan rõ ràng. Không quan trọng việc nó là một thanh màu xanh lá đồ họa hay một thông báo trên màn hình nói là “Tất cả các bài test đều đã pass”. Mấu chốt là việc bạn phải có thể biết tất cả các bài test đã được pass một cách nhanh chóng và rõ ràng. Nếu bạn phải đọc một báo cáo nhiều dòng, hay tệ hơn là phải so sánh đầu ra của 2 file với nhau mới biết được các bài test có pass hay không thì bạn đã thất bại ở điểm này.
  3. Công cụ này cần phải cho bạn một chỉ thị tiến độ trực quan rõ ràng. Không quan trong việc nó có dạng thanh tiến trình đồ họa hoặc một chuỗi các dấu chấm miến là bạn có thể biết rằng tiến trình vẫn đang diễn ra và các bài test không bị dừng lại hoặc bị hủy bỏ.
  4. Các công cụ này cần phải ngăn cản các bài test riêng lẻ giao tiếp lẫn nhau. JUnit thực hiện việc này bằng cách tạo một đối tượng mới của test class đối với mỗi test method, nhờ đó ngăn chặn các bài test khỏi việc dùng các biến của đối tượng để liên lạc với những bài test khác. Các công cụ khác sẽ chạy các test method theo thứ tự ngẫu nhiên để bạn không thể phụ thuộc vào việc bài test này thực hiện trước bài test kia. Dù là bất cứ cơ chế nào, công cụ này cũng cần phải giúp bạn giữ cho các bài test được độc lập khỏi những bài test khác. Các bài test bị phụ thuộc là một cạm bẫy sâu mà bạn không bao giờ muốn bị rơi vào đó.
  5. Công cụ này cần phải giúp bạn rất dễ dàng viết được các bài test. JUnit thực hiện việc này bằng cách cung cấp các API thuận tiện để làm các kiểm tra đánh giá. Nó cũng dùng các thuộc tính của Java để phân biệt các function test với các function bình thường. Điều này cho phép một IDE tốt có thể tự động xác định được tất cả các bài test của bạn, loại bỏ được sự phức tạp của việc tạo các bộ test và tạo ra danh sách các bài test vốn rất

dễ xảy ra lỗi.

Các công cụ component test

Các công cụ này được dùng để test các component ở cấp độ API. Vai trò của chúng là đảm bảo rằng hành vi của một component được thể hiện bằng một ngôn ngữ mà cả khách hàng và nhóm QA đều có thể hiểu được. Quả thực là trong trường hợp lý tưởng thì những người phân tích nghiệp vụ và QA có thể viết các bài test đó bằng công cụ này.

Định nghĩa Hoàn thành

Hơn bất cứ công cụ nào khác, công cụ component test được dùng để chúng ta xác định hoàn thành nghĩa là gì. Khi những người phân tích nghiệp vụ và QA cộng tác để tạo ra một bộ tiêu chuẩn định nghĩa hành vi của một component, và khi tiêu chuẩn đó có thể được thực hiện như một bộ các bài test có thể pass hoặc fail, thì từ hoàn thành có một ý nghĩa rất rõ ràng: “Tất cả các bài test đều phải pass.”

FitNesse

Công cụ component test ưa thích của tôi là FitNesse. Tôi đã viết phần lớn nó, và tôi là người đóng góp chính. Chính vì vậy nó là đứa con tinh thần của tôi.

FitNesse là một hệ thống dựa trên wiki cho phép các người phân tích nghiệp vụ và QA viết các bài test ở dạng bảng rất đơn giản. Những bảng này tương tự như bảng Parnas cả về hình thức lẫn mục đích. Những bài test này có thể được ghép nhanh chóng thành các bộ test, và các bộ test này có thể chạy một cách nhanh chóng.

FitNesse được viết bằng Java nhưng nó có thể kiểm tra các hệ thống được viết bằng bất cứ ngôn ngữ nào bởi vì nó liên lạc với hệ thống kiểm tra nằm bên dưới, hệ thống này có thể được viết bằng bất cứ ngôn ngữ nào. Các ngôn ngữ được hỗ trợ bao gồm Java, C#/.NET, C, C++, Python, Ruby, PHP, Delphi, và nhiều ngôn ngữ khác.

Có hai hệ thống kiểm tra làm nền tảng cho FitNesse: Fit và Slim. Fit được viết bởi Ward Cunningham và là cảm hứng ban đầu của FitNesse. Slim thì là một hệ thống kiểm tra đơn giản và linh động hơn nhiều, đây là những đặc điểm được yêu thích bởi những người dùng FitNesse hiện nay.

Các công cụ khác

Tôi có biết một vài công cụ khác mà có thể phân loại là các công cụ component test.

  • RobotFX là một công cụ được phát triển bởi các kỹ sư Nokia. Nó dùng dạng bảng tương tự như FitNesse, nhưng không phải dựa trên wiki. Công cụ này đơn giản là chạy trên các file được chuẩn bị bằng Excel hoặc phần mềm tương tự. Công cụ này được viết bằng Python nhưng có thể kiểm tra các hệ thống ở bất cứ ngôn ngữ nào bằng cách dùng các cầu nối phù hợp.
  • Green Pepper là một công cụ thương mại hóa có một số tính năng tương tự như FitNesse. Nó dựa trên confluence wiki phổ biến.
  • Cucumber là một công cụ văn bản thuần túy được điều khiển bởi Ruby engine, nhưng nó cũng có khả năng kiểm tra nhiều nền tảng khác nhau. Ngôn ngữ của Cucumber có phong cách Given/When/Then quen thuộc.
  • JBehave tương tự như Cucumber. Nó được viết bằng Java.

Các công cụ integration test

Các công cụ component test cũng có thể được dùng đối với nhiều bài integration test, nhưng chúng ít phù hợp cho những bài test được thực hiện thông qua UI.

Nói chung, chúng ta không muốn thực hiện quá nhiều bài test thông qua UI bởi vì UI là thành phần rất hay thay đổi. Việc hay thay đổi đó làm cho những bài test thực hiện qua UI rất mong manh. Nhưng cũng phải nói rằng, có những bài test bắt buộc phải thông qua UI – quan trọng nhất là các bài test của UI. Ngoài ra, có một số bài test đầu-cuối cũng cần phải đi qua toàn bộ hệ thống hoàn thiện, bao gồm cả UI.

Các công cụ tôi thích nhất để test UI là Selenium và Watir.

UML/MDA

Vào đầu những năm 90, tôi rất hy vọng rằng các công cụ CASE[3] sẽ tạo ra một thay đổi căn bản cách mà các lập trình viên làm việc. Khi tôi liên tưởng trong những ngày đó, tôi đã nghĩ rằng mọi người sẽ code bằng biểu đồ ở một cấp độ trừu tượng cao hơn và việc code bằng ký tự sẽ là điều thuộc về quá khứ.

Tôi đã sai. Không chỉ giấc mơ này không thành hiện thức, mà mọi nỗ lực để đi theo hướng đó đều thất bại nặng nề. Không phải bởi vì không có các công cụ và hệ thống nào chứng minh được tiềm năng này; nó đơn giản là do các công cụ này không thực sự hiện thực hóa giấc mơ đó, và hiếm có ai dường như muốn dùng chúng.

Giấc mơ về việc các lập trình viên phần mềm có thể không cần quan tâm chi tiết tới phần code ký tự và có thể xây dựng hệ thống ở một cấp độ ngôn ngữ cao hơn là các biểu đồ. Quả thực là nếu giấc mơ đó tới, chúng ta có thể hoàn toàn không cần tới các lập trình viên nữa. Các kiến trúc sư có thể tạo ra toàn bộ hệ thống từ các biểu đồ UML. Các cỗ máy to lớn, lạnh lùng và vô cảm với cảnh ngộ của những lập trình viên thuần túy, sẽ biến đổi những biểu đồ đó thành code thực thi. Đó là giấc mơ vĩ đại về Kiến Trúc Định Hướng Mô Hình (Model Driven Architecture – MDA).

Không may thay, giấc mơ vĩ đại này có một thiếu sót rất rất nhỏ. MDA xem vấn đề là code. Nhưng code không phải là vấn đề. Nó không bao giờ là vấn đề. Vấn đề là ở chi tiết.

Chi tiết

Các lập trình viên là những người quản lý chi tiết. Đó là những gì chúng ta làm. Chúng ta chỉ ra hành vi chi tiết của hệ thống. Chúng ta dùng ngôn ngữ văn bản cho điều này (code) bởi vì ngôn ngữ văn bản đặc biệt thuận tiện (lấy ví dụ như tiếng Anh).

Chúng ta quản lý loại chi tiết gì?

Bạn biết sự khác biệt giữa hai ký tự \n và \r không? Cái đầu tiên, \n, được gọi là line feed. Cái thứ hai, \r, được gọi là carriage return. Vậy carriage là cái gì?

Trong những năm 60 và đầu những năm 70, một trong những thiết bị đầu ra ngày càng phổ biến của máy vi tính là máy điện báo đánh chữ. Mẫu ASR-33[4] là loại phổ biến nhất.

Thiết bị này bao gồm một đầu in mà nó có thể in được 10 ký tự mỗi giây. Đầu in này bao gồm một xylanh nhỏ với các ký tự nổi lên trên nó. Xylanh sẽ quay và nâng lên để cho ký tự tiếp xúc với mặt giấy, và sau đó một búa nhỏ sẽ đập xylanh vào mặt giấy. Có một băng mực ở giữa xylanh và giấy, và mực được truyền vào giấy theo hình dạng của ký tự.

Các đầu in này nằm trên một carriage. Với mỗi ký tự, carriage này di chuyển một khoảng về bên phải, mang theo đầu in với nó. Khi carriage tới điểm cuối của dòng 72 ký tự, bạn phải đưa carriage về bằng cách gửi ký tự carriage return (\r = 0 x 0D), nếu không đầu in sẽ vẫn tiếp tục in các ký tự ở cột thứ 72, biến vùng đó thành một hình chữ hình màu đen bẩn bẩn.

Dĩ nhiên, như vậy là chưa đủ. Việc đưa carriage về không giúp nâng tấm giấy lên dòng tiếp theo. Nếu bạn đưa carriage về mà không gửi một ký tự line feed (\n = 0 x 0A) thì dòng mới sẽ vẫn in trên dòng cũ.

Bởi vậy, đối với máy điện báo đánh chữ ASR-33 thì chuỗi hết dòng là “\r\n”. Thực tế là bạn phải cẩn thận về việc đó do carriage có thể mất hơn 100ms để trở về. Nếu bạn gửi “\n\r” thì ký tự tiếp theo có thể bị in trong khi carriage đang trở về, do đó nó sẽ tạo ra một ký tự mờ mờ ở giữa dòng. Để an toàn, chúng tôi thường thêm vào chuỗi ký tự hết dòng với 1 hoặc 2 ký tự rubout[5] (0 x FF).

Trong những năm 70, máy điện báo đánh chữ dần dần biến mất, các hệ điều hành như UNIX đã làm ngắn bớt chuỗi kết thúc dòng thành đơn giản chỉ là “\n”. Tuy nhiên, các hệ điều hành khác, như DOS, vẫn tiếp tục dùng quy ước “\r\n”.

Lần cuối cùng bạn phải xử lý những file văn bản dùng “sai” quy ước này là khi nào? Tôi gặp phải vấn đề này ít nhất là mỗi năm một lần. Hai file source code tương tự nhau nhưng khi so sánh lại không giống nhau và không tạo ra được checksum[6] như nhau, chỉ bởi vì chúng dùng các quy ước kết thúc dòng khác nhau. Các trình xử lý văn bản không word-wrap chính xác, hoặc bị nhân đôi khoảng cách ký tự bởi vì kết thúc dòng “sai”. Các chương trình không mong muốn dòng trống bị thoát ra ngoài bởi vì chúng dịch “\r\n” là hai dòng. Một số chương trình nhận biết “\r\n” nhưng không nhận biết được “\n\r”. Và nhiều vấn đề khác.

Đó chính là thứ mà tôi muốn nói về chi tiết ở đây. Bạn hãy thử code một luồng logic kinh khủng để phân loại kết thúc dòng bằng UML xem!

Không hy vọng, không thay đổi

Hy vọng của phong trào MDA là có thể loại bỏ một lượng lớn phần chi tiết bằng cách dùng biểu đồ thay vì code. Hy vọng đó cho đến nay đã được chứng minh là không còn. Nó đã chỉ ra rằng chúng ta không loại bỏ được quá nhiều phần code chi tiết nhúng bên trong hình ảnh. Hơn thế nữa, hình ảnh còn chứa những chi tiết thừa của bản thân chúng nữa. Hình ảnh có ngữ pháp, cú pháp, quy tắc và ràng buộc riêng. Vì vậy, cuối cùng, sự khác biệt về chi tiết là không thể xóa được.

Hy vọng về MDA đó là các biểu đồ sẽ chứng minh nó sẽ là một cấp trừu tượng cao hơn code, cũng giống như Java là một cấp trừu tượng cao hơn ngôn ngữ assembly. Nhưng lại một lần nữa, hy vọng đó cho đến nay đã được chứng minh là không đúng chỗ. Sự khác biệt về mức độ trừu tượng là rất nhỏ.

Và cuối cùng, chúng ta hãy nói rằng một ngày nào đó ai đó sẽ phát minh ra một ngôn ngữ biểu đồ thực sự hữu ích. Không phải là các kiến trúc sư sẽ vẽ ra những biểu đồ đó, mà sẽ là những lập trình viên. Biểu đồ đó sẽ đơn giản trở thành một loại code mới, và những lập trình viên sẽ cần vẽ code bởi vì, cuối cùng, tất cả là về chi tiết, và các lập trình viên là những người quản lý chi tiết.

Kết luận

Các công cụ phần mềm đã trở nên mạnh mẽ và phong phú hơn rất nhiều kể từ khi tôi bắt đầu lập trình. Bộ công cụ hiện giờ của tôi là một tập con đơn giản của một số lượng khổng lồ các công cụ đó. Tôi dùng git để quản lý source code, Pivotal Tracker để quản lý vấn đề, Jenkin để build liên tục, IntelliJ làm IDE, XUnit để test, và FitNesse để thực hiện component test. Chiếc máy làm việc của tôi là Macbook Pro, 2,8Ghz Intel Core i7, với màn hình 17-inch, RAM 8GB, ổ SSD 512GB, và 2 màn hình phụ.


[1] Những cuộn băng này chỉ có thể di chuyển theo một hướng. Vì vậy khi nó đọc lỗi, không có cách nào để đầu đọc lùi lại và đọc lại lần nữa. Bạn phải dừng việc bạn đang làm, cho cuộn băng lùi trở lại điểm nạp và sau đó bắt đầu lại lần nữa. Điều này xảy ra 2 hoặc 3 lần một ngày. Việc ghi lỗi cũng rất phổ biến, và thiết bị này không có cách nào để phát hiện ra được chúng. Vì vậy chúng tôi luôn ghi băng thành từng cặp và sau đó kiểm tra cặp này sau khi hoàn thành. Nếu một cuộn băng bị lỗi, chúng tôi lập tức tạo một bản copy. Nếu cả hai bị lỗi, điều này hiếm khi xảy ra, thì chúng tôi sẽ phải bắt đầu lại toàn bộ quá trình từ đầu. Đó là cuộc sống vào những năm 70.

[2] vi: tên gọi một phần mềm chỉnh sửa văn bản phổ biến trên hệ điều hành Linux.

[3] CASE: Computer-Aided Software Engineering, hệ thống các công cụ được sử dụng để thiết kế và phát triển phần mềm dưới sự hỗ trợ của máy tính.

[4] http://en.wikipedia.org/wiki/ASR-33_Teletype

[5] Ký tự rubout rất hữu ích để chỉnh sửa các băng giấy. Về quy tắc thì các ký tự rubout sẽ được bỏ qua. Code của nó là 0 x FF, nghĩa là mọi lỗ trên hàng của băng giấy đó đã được bấm. Điều này nghĩa là bất cứ ký tự nào có thể được chuyển thành một rubout bằng cách đục lộ nó. Do đó, nếu bạn gõ nhầm chương trình của bạn thì bạn có thể lùi lại và ấn rubout, sau đó tiếp tục gõ.

[6] checksum: giá trị tổng kiểm.

Trả lời

Email của bạn sẽ không được hiển thị công khai.