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

Trang này thảo luận về hệ thống xây dựng, chức năng của hệ thống, lý do bạn nên sử dụng a hệ thống xây dựng và lý do trình biên dịch và tập lệnh xây 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ô. Trang này dành cho những nhà phát triển chưa 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, tất cả hệ thống xây dựng đều có một mục đích đơn giản: chuyển đổi mã nguồn do kỹ sư viết thành tệp nhị phân có thể thực thi mà máy có thể đọc được bởi máy. Hệ thống xây dựng không chỉ dành cho mã do con người viết mà còn cho phép máy tự động tạo bản dựng, cho dù là để kiểm thử hay phát hành cho môi trường sản xuất. 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 đều được kích hoạt tự động thay vì do kỹ sư trực tiếp kích hoạt.

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

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

javac *.java

Lệnh 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 thành 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 vấn đề phức tạp sẽ bắt đầu. javac đủ thông minh để tìm kiếm trong các thư mục con của thư mục hiện tại nhằm tìm mã để nhập. Nhưng nó không có cách nào để tìm mã được lưu trữ ở các phần khác của hệ thống tệp (có thể là một thư viện do nhiều dự án chia sẻ). Nó cũng chỉ biết cách xây dựng 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 mạng lưới các phần phụ thuộc giữa các phần đó, nghĩa là không có trình biên dịch nào cho một ngôn ngữ 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 xây dựng mã không còn là quy trình một bước nữa. Giờ đây, bạn phải đánh giá những gì mã của bạn phụ thuộc vào và xây dựng các phần đó theo đúng thứ tự, có thể sử dụng một bộ công cụ khác cho mỗi 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 tệp nhị phân lỗi thời. Đối với cơ sở mã có kích thước vừa phải, quy 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ư 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ý việc này bằng cách tải phần phụ thuộc xuống từ Internet, dán vào thư mục lib trên ổ cứng và định cấu hình trình biên dịch để đọc thư viện từ thư mục đó. Theo thời gian, việc duy trì các 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 thì sao?

Giả sử dự án sở thích của bạn bắt đầu đủ đơn giản để bạ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ó lẽ bạn vẫn không nghĩ rằng mình cần một hệ thống xây dựng và có thể tự động hoá các phần tẻ nhạt bằ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ự. Điều này giúp ích trong một thời gian, nhưng chẳng bao lâu sau, bạn bắt đầu gặp phải nhiều vấn đề hơn:

  • 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 bắt đầu dành gần như cùng một khoảng thời gian để làm việc trên tập lệnh xây dựng như trên mã thực. Việc gỡ lỗi tập lệnh shell rất khó khăn, với ngày càng nhiều bản vá được xếp chồng lên nhau.

  • Chậm. Để đảm bảo rằng bạn không vô tình dựa vào các thư viện lỗi thời, bạn sẽ có tập lệnh xây dựng xây dựng mọi phần phụ thuộc theo thứ tự mỗi khi bạn chạy tập lệnh đó. Bạn 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 đó nghe có vẻ cực kỳ phức tạp và dễ xảy ra lỗi đối với một tập lệnh. Hoặc bạn nghĩ đến việc chỉ định những phần cần được xây dựng lại mỗi lần, nhưng sau đó bạn lại quay về điểm xuất phát.

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

  • Thảm hoạ! Ổ cứng của bạn bị hỏng và giờ bạn cần tạo lại toàn bộ hệ thống. Bạn đủ thông minh để giữ tất cả các tệp nguồn của mình trong hệ thống kiểm soát phiên bản, nhưng còn những thư viện mà bạn đã tải xuống thì sao? Bạn có thể tìm lại tất cả các thư viện đó và đảm bảo rằng chúng có cùng phiên bản như khi bạn tải xuống lần đầu 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 ở những vị trí cụ thể – bạn có thể khôi phục cùng một môi trường đó để 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 đã đặt a từ lâu để trình biên dịch hoạt động đúng cách rồi quên mất thì sao?

  • Bất chấp các vấn đề, dự án của bạn đủ thành công để bạn có thể bắt đầu thuê thêm kỹ sư. Giờ đây, bạn nhận ra rằng không cần phải có thảm hoạ thì các vấn đề trước đó mới nảy sinh – bạn cần phải trải qua quy trình khởi động khó khăn tương tự mỗi khi một nhà phát triển mới gia nhập nhóm của bạn. Và bất chấp những nỗ lực tốt nhất của bạn, vẫn có những điểm khác biệt nhỏ trong hệ thống của mỗi người. 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 lần mất vài giờ để gỡ lỗi đường dẫn công cụ hoặc phiên bản thư viện để tìm ra điểm khác biệt.

  • 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, điều này là đơ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 xây dựng mỗi đêm bằng cron. Bạn vẫn cần phải trải qua quy trình thiết lập khó khăn, nhưng giờ đây, bạn không có lợi ích của bộ não con người có thể phát hiện và giải quyết các vấn đề nhỏ. Giờ đây, mỗi sáng khi bạn đến , bạn thấy rằng bản dựng đêm qua không thành công 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 lần đều là một bản sửa lỗi đơn giản, nhưng điều đó xảy ra quá thường xuyên khiến bạn mất 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.

  • Bản dựng ngày càng chậm hơn khi dự án phát triển. Một ngày nọ, trong khi chờ bản dựng hoàn tất, bạn buồn bã nhìn vào màn hình máy tính để bàn đang nhàn rỗi của đồng nghiệp đang đi nghỉ và ước rằng có cách tận dụng tất cả sức mạnh tính toán bị 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 làm việc trên tối đa vài trăm dòng mã trong tối đa một hoặc hai tuần (có thể là toàn bộ kinh nghiệm cho đến nay của một nhà phát triển cấp dưới vừa tốt nghiệp đại học), thì bạn chỉ cần một trình biên dịch. Tập lệnh có thể đưa bạn đi xa hơn một chút. Nhưng ngay khi bạn cần phối hợp giữa nhiều nhà phát triển và máy của họ, thì ngay cả một tập lệnh xây dựng hoàn hảo cũng không đủ vì rất khó để tính đến những điểm khác biệt nhỏ trong các máy đó. Tại thời điểm này, phương pháp đơn giản này sẽ bị phá vỡ và đã đến lúc đầu tư vào một hệ thống xây dựng thực.