Tại sao nên có một hệ thống xây dựng?

Trang này thảo luận về các hệ thống xây dựng, chức năng của hệ thống xây dựng, lý do bạn nên sử dụng hệ thống xây dựng và lý do trình biên dịch và tập lệnh bản dựng không phải là lựa chọn tốt nhất khi tổ chức của bạn bắt đầu mở rộng quy mô. Hướng dẫn này dành cho các nhà phát triển không có nhiều kinh nghiệm về hệ thống xây dựng.

Hệ thống xây dựng là gì?

Về cơ bản, mọi hệ thống xây dựng đều có mục đích đơn giản: biến đổi mã nguồn do các kỹ sư viết thành các tệp nhị phân có thể thực thi mà máy có thể đọc được. Các hệ thống xây dựng không chỉ dành cho mã do con người tạo, mà còn cho phép máy tự động tạo bản dựng, cho dù để kiểm thử hay phát hành chính thức. Trong một tổ chức có hàng nghìn kỹ sư, thông thường, hầu hết các bản dựng sẽ được kích hoạt tự động thay vì trực tiếp bởi kỹ sư.

Tôi không thể chỉ sử dụng một trình biên dịch?

Nhu cầu xây dựng hệ thống xây dựng có thể chưa rõ ràng ngay lập tức. Hầu hết các kỹ sư đều không sử dụng hệ thống xây dựng trong khi học lập trình: hầu hết đều bắt đầu bằng cách gọi các công cụ như gcc hoặc javac trực tiếp từ dòng lệnh hoặc các công cụ tương đương trong môi trường phát triển tích hợp (IDE). Miễn là tất cả các mã nguồn nằm trong cùng một thư mục, thì một lệnh như sau sẽ hoạt động hiệu quả:

javac *.java

Thao tác này hướng dẫn trình biên dịch Java lấy mọi tệp nguồn Java trong thư mục hiện tại và chuyển tệp đó thành một tệp lớp nhị phân. Trong trường hợp đơn giản nhất, đây là tất cả những gì bạn cần.

Tuy nhiên, ngay khi mã mở rộng, các chức năng sẽ bắt đầu. javac đủ thông minh để tìm trong các thư mục con của thư mục hiện tại nhằm tìm mã cần nhập. Nhưng không có cách nào để tìm mã được lưu trữ trong các phần khác của hệ thống tệp (có thể là một thư viện do một số dự án chia sẻ). Trình quản lý thẻ của Google cũng chỉ biết cách tạo mã Java. Các hệ thống lớn thường bao gồm nhiều phần được viết bằng nhiều ngôn ngữ lập trình với các mạng lưới phụ thuộc giữa các phần phụ thuộc đó, nghĩa là không có trình biên dịch nào cho một ngôn ngữ riêng lẻ có thể xây dựng toàn bộ hệ thống.

Khi bạn xử lý mã từ nhiều ngôn ngữ hoặc nhiều đơn vị biên dịch, việc tạo mã xây dựng không còn là quy trình một bước nữa. Bây giờ, bạn phải đánh giá những gì mã phụ thuộc và xây dựng các mảnh đó theo thứ tự thích hợp, có thể sử dụng một bộ công cụ khác nhau cho từng phần. Nếu có bất kỳ phần phụ thuộc nào thay đổi, bạn phải lặp lại quy trình này để tránh phụ thuộc vào các tệp nhị phân cũ. Đối với cơ sở mã có kích thước thậm chí vừa phải, quá trình này nhanh chóng trở nên tẻ nhạt và dễ xảy ra lỗi.

Trình biên dịch cũng không biết gì về cách xử lý các phần phụ thuộc bên ngoài, chẳng hạn như các tệp JAR của bên thứ ba trong Java. Nếu không có hệ thống xây dựng, bạn có thể quản lý bằng cách tải phần phụ thuộc xuống qua Internet, đưa phần phụ thuộc đó vào thư mục lib trên ổ đĩa cứng và định cấu hình trình biên dịch để đọc các thư viện trong thư mục đó. Theo thời gian, việc duy trì bản cập nhật, phiên bản và nguồn của các phần phụ thuộc bên ngoài này sẽ trở nên khó khăn.

Còn tập lệnh shell?

Giả sử dự án sở thích của bạn bắt đầu đủ đơn giản để có thể xây dựng dự án chỉ bằng trình biên dịch, nhưng bạn bắt đầu gặp phải một số vấn đề được mô tả trước đó. Có thể bạn vẫn không nghĩ mình cần hệ thống xây dựng và có thể tự động hoá những phần tẻ nhạt bằng cách sử dụng một số tập lệnh shell đơn giản giúp xây dựng mọi thứ theo đúng thứ tự. Việc này sẽ giúp ích trong một thời gian, nhưng sau đó, bạn còn bắt đầu gặp nhiều vấn đề hơn nữa:

  • Việc này trở nên tẻ nhạt. Khi hệ thống của bạn trở nên phức tạp hơn, bạn sẽ bắt đầu dành nhiều thời gian để làm việc trên các tập lệnh bản dựng như trên mã thực. Gỡ lỗi tập lệnh shell rất khó khăn, với ngày càng nhiều bản tấn công được xếp chồng lên nhau.

  • Ứng dụng chậm. Để đảm bảo bạn không vô tình dựa vào các thư viện cũ, bạn có thể tạo tập lệnh bản dựng để tạo mọi phần phụ thuộc theo thứ tự mỗi khi chạy tập lệnh đó. Bạn hãy nghĩ đến việc thêm một số logic để phát hiện những phần cần được xây dựng lại, nhưng điều đó có vẻ cực kỳ phức tạp và dễ xảy ra lỗi đối với tập lệnh. Hoặc bạn nghĩ đến việc chỉ định phần nào cần được xây dựng lại mỗi lần, nhưng sau đó bạn sẽ quay lại hình vuông.

  • Tin vui: đã đến lúc phát hành! Tốt hơn là hãy tìm hiểu tất cả các đối số bạn cần truyền đến lệnh jar để tạo bản dựng cuối cùng. Đồng thời, hãy nhớ cách tải tệp lên và đẩy vào kho lưu trữ trung tâm. Đồng thời, tạo và đẩy bản cập nhật tài liệu, đồng thời gửi thông báo cho người dùng. Rất tiếc, có thể đoạn mã này cần một tập lệnh khác...

  • Thảm hoạ! Ổ đĩa cứng của bạn gặp sự cố và giờ đây bạn cần tạo lại toàn bộ hệ thống. Bạn đã đủ thông minh để giữ tất cả tệp nguồn của mình ở chế độ quản lý phiên bản, nhưng còn những thư viện bạn đã tải xuống thì sao? Bạn có thể tìm lại tất cả các tệp và đảm bảo chúng giống phiên bản như khi tải xuống lần đầu tiên không? Tập lệnh của bạn có thể phụ thuộc vào các công cụ cụ thể được cài đặt ở các vị trí cụ thể – bạn có thể khôi phục cùng một môi trường để các tập lệnh hoạt động trở lại không? Còn tất cả các biến môi trường mà bạn đã thiết lập từ lâu để trình biên dịch hoạt động bình thường rồi quên mất thì sao?

  • Bất kể vấn đề, dự án của bạn vẫn đủ thành công để bạn có thể bắt đầu tuyển dụng thêm kỹ sư. Bây giờ, bạn nhận ra rằng các vấn đề trước đó phát sinh thì không phải là thảm hoạ đâu. Bạn cần phải trải qua cùng một quy trình khởi động đầy đau đớn mỗi khi có một nhà phát triển mới tham gia nhóm. Và mặc dù đã cố gắng hết sức, nhưng hệ thống của mỗi người vẫn có những điểm khác biệt nhỏ. Thông thường, những gì hoạt động trên máy của một người không hoạt động trên máy của người khác và mỗi khi cần vài giờ để tìm ra sự khác biệt giữa các đường dẫn công cụ hoặc phiên bản thư viện.

  • Bạn quyết định rằng mình cần tự động hoá hệ thống xây dựng. Về lý thuyết, việc này đơn giản như việc mua một máy tính mới và thiết lập để chạy tập lệnh bản dựng mỗi đêm bằng cách sử dụng cron. Bạn vẫn phải trải qua quá trình thiết lập đầy đau đớn, nhưng giờ đây bạn không còn chút lợi ích của bộ não người có khả năng phát hiện và giải quyết các vấn đề nhỏ. Bây giờ, mỗi sáng khi truy cập, bạn sẽ thấy bản dựng không thành công tối qua vì hôm qua, một nhà phát triển đã thực hiện thay đổi hoạt động trên hệ thống của họ nhưng không hoạt động trên hệ thống xây dựng tự động. Mỗi khi một bản sửa lỗi đơn giản, nhưng nó xảy ra thường xuyên đến mức bạn phải dành nhiều thời gian mỗi ngày để khám phá và áp dụng các bản sửa lỗi đơn giản này.

  • Các bản dựng sẽ ngày càng chậm hơn khi dự án phát triển. Một ngày, trong khi chờ bản dựng hoàn thành, bạn nhìn chăm chăm nhìn vào máy tính để bàn đang rảnh của đồng nghiệp đang đi nghỉ và ước gì có cách tận dụng được toàn bộ sức mạnh tính toán lãng phí đó.

Bạn đã gặp phải một vấn đề kinh điển về quy mô. Đối với một nhà phát triển đơn lẻ làm việc với tối đa vài trăm dòng mã trong tối đa 1 hoặc 2 tuần (có thể là toàn bộ trải nghiệm của một nhà phát triển chưa tốt nghiệp đại học vừa tốt nghiệp đại học), thì trình biên dịch là tất cả những gì bạn cần. Kịch bản có thể đưa bạn tiến xa hơn một chút. Tuy nhiên, ngay khi bạn cần điều phối giữa nhiều nhà phát triển và máy của họ, thì một tập lệnh bản dựng hoàn hảo cũng chưa đủ vì việc tính đến những khác biệt nhỏ trong các máy đó là rất khó. Tại thời điểm này, phương pháp đơn giản này sẽ bị hỏng và đã đến lúc đầu tư vào một hệ thống xây dựng thực tế.