Hãy chú ý về cách AI thay đổi cơ sở mã (codebase) của bạn
Be intentional about how AI changes your codebase
AI code generation đòi hỏi chúng ta phải chú trọng hơn vào cấu trúc và sự rõ ràng của codebase. Lập trình viên nên ưu tiên xây dựng các "semantic functions" – những hàm nhỏ gọn, chính xác, không có side-effect và tự nó đã giải thích chức năng. Chúng sẽ là những khối xây dựng vững chắc. Các luồng phức tạp nên được quản lý bởi "pragmatic functions", là những wrapper được tài liệu hóa kỹ lưỡng bao quanh các semantic functions. Quan trọng không kém, các data models cần được định kiểu chặt chẽ (strictly typed) để tránh các trạng thái không hợp lệ. Thiết kế có chủ đích này giúp codebase dễ bảo trì hơn và hỗ trợ các AI assistants tích hợp hiệu quả mà không mang lại những bug khó lường.
Mã phải tự ghi lại Cách bạn chia logic thành các hàm và định hình dữ liệu mà chúng truyền xung quanh sẽ xác định mức độ duy trì của cơ sở mã theo thời gian. Hàm ngữ nghĩa Hàm ngữ nghĩa là công trình xây dựng...
Mã phải tự ghi lại
Cách bạn phân tách logic thành các hàm và định hình dữ liệu mà chúng truyền xung quanh sẽ quyết định mức độ tồn tại của cơ sở mã theo thời gian.
Hàm ngữ nghĩa
Hàm ngữ nghĩa là nền tảng của bất kỳ cơ sở mã nào, một hàm ngữ nghĩa tốt phải tối thiểu nhất có thể để ưu tiên tính chính xác trong đó. Một hàm ngữ nghĩa phải nhận tất cả các đầu vào cần thiết để hoàn thành mục tiêu của nó và trả về trực tiếp tất cả các đầu ra cần thiết. Các hàm ngữ nghĩa có thể bao bọc các hàm ngữ nghĩa khác để mô tả các luồng và cách sử dụng mong muốn; Là các khối xây dựng của cơ sở mã, nếu có các luồng phức tạp được sử dụng ở mọi nơi được xác định rõ ràng, hãy sử dụng hàm ngữ nghĩa để hệ thống hóa chúng.
Các tác dụng phụ nói chung là không mong muốn trong các hàm ngữ nghĩa trừ khi chúng là mục tiêu rõ ràng vì các hàm ngữ nghĩa phải an toàn để sử dụng lại mà không cần hiểu nội dung bên trong của chúng về những gì chúng nói chúng làm. Nếu logic phức tạp và không rõ nó làm gì trong một luồng lớn, thì một mẫu hay là chia luồng đó thành một loạt các hàm ngữ nghĩa tự mô tả để nhận những gì chúng cần, trả về dữ liệu cần thiết cho bước tiếp theo và không làm gì khác. Ví dụ về các hàm ngữ nghĩa tốt bao gồm từ quadratic_formula() đến retry_with_exponential_backoff_and_run_y_in_between. Ngay cả khi các hàm này không bao giờ được sử dụng nữa, con người và tác nhân trong tương lai khi xem mã sẽ đánh giá cao việc lập chỉ mục thông tin.
Các hàm ngữ nghĩa không cần bất kỳ nhận xét nào xung quanh chúng, bản thân mã phải là định nghĩa tự mô tả về chức năng của nó. Lý tưởng nhất là các hàm ngữ nghĩa phải có khả năng kiểm tra đơn vị cực kỳ cao vì hàm ngữ nghĩa tốt là một hàm được xác định rõ ràng.
Hàm thực dụng
Hàm thực dụng nên được sử dụng làm lớp bao bọc xung quanh một loạt hàm ngữ nghĩa và logic duy nhất. Chúng là các quy trình phức tạp của cơ sở mã của bạn. Khi tạo ra các hệ thống sản xuất, logic sẽ trở nên lộn xộn là điều đương nhiên, các chức năng thực dụng là tổ chức cho những điều này. Nói chung, chúng không nên được sử dụng ở nhiều nơi, nếu có, hãy cân nhắc việc chia nhỏ logic rõ ràng và chuyển nó thành các hàm ngữ nghĩa. Ví dụ: provision_new_workspace_for_github_repo(repo, user) hoặc handle_user_signup_webhook(). Kiểm thử các chức năng thực dụng thuộc lĩnh vực kiểm thử tích hợp và thường được thực hiện trong bối cảnh kiểm thử toàn bộ chức năng của ứng dụng. Các chức năng thực dụng được kỳ vọng sẽ thay đổi hoàn toàn theo thời gian, từ bên trong cho đến những gì chúng thực hiện. Để giải quyết vấn đề đó, bạn nên có các nhận xét tài liệu phía trên chúng. Tránh đặt lại tên hàm hoặc các đặc điểm rõ ràng về nó, thay vào đó hãy lưu ý những điều không mong muốn như "thất bại sớm khi số dư dưới 10" hoặc chống lại những quan niệm sai lầm khác đến từ tên hàm. Vì người đọc các nhận xét về tài liệu không mấy tin tưởng nên các lập trình viên làm việc bên trong hàm có thể đã quên cập nhật chúng và trên thực tế, bạn nên kiểm tra chúng khi cho rằng chúng có thể không chính xác.
Mô hình
Hình dạng dữ liệu của bạn phải khiến cho các trạng thái sai là không thể xảy ra. Nếu một mô hình cho phép kết hợp các trường không bao giờ tồn tại cùng nhau trong thực tế thì mô hình đó đang không thực hiện được nhiệm vụ của mình. Mỗi trường tùy chọn là một câu hỏi mà phần còn lại của cơ sở mã phải trả lời mỗi khi chạm vào dữ liệu đó và mỗi trường được gõ lỏng lẻo là một lời mời để người gọi chuyển nội dung nào đó có vẻ đúng nhưng thực tế không phải vậy. Khi các mô hình thực thi tính chính xác, các lỗi sẽ xuất hiện ở thời điểm xây dựng thay vì ở sâu bên trong một số luồng không liên quan, nơi các giả định cuối cùng sụp đổ. Tên của mô hình phải đủ chính xác để bạn có thể xem bất kỳ trường nào và biết liệu nó có thuộc về nó hay không — nếu tên không cho bạn biết thì mô hình đó đang cố gắng trở thành quá nhiều thứ. Khi hai khái niệm thường cần đến với nhau nhưng độc lập, hãy kết hợp chúng thay vì hợp nhất chúng - ví dụ: UserAndWorkspace { user: User, Workspace: Workspace giữ nguyên cả hai mô hình thay vì làm phẳng các trường không gian làm việc cho người dùng. Những cái tên hay như UnverifiedEmail, PendingInvite và BillingAddress cho bạn biết chính xác trường nào thuộc về. Nếu bạn thấy trường số_điện thoại trên Địa chỉ thanh toán thì bạn biết đã xảy ra lỗi.
Các giá trị có hình dạng giống hệt nhau có thể biểu thị các khái niệm miền hoàn toàn khác nhau: { id: "123" có thể là DocumentReference ở một nơi và MessagePointer ở một nơi khác và nếu các hàm của bạn chỉ chấp nhận { id: String thì mã sẽ chấp nhận một trong hai mà không phàn nàn. Loại thương hiệu giải quyết vấn đề này bằng cách gói một kiểu nguyên thủy vào một loại riêng biệt để trình biên dịch coi chúng là riêng biệt: DocumentId(UUID) thay vì UUID trần. Khi đã có thương hiệu, việc vô tình hoán đổi hai ID sẽ trở thành một lỗi cú pháp thay vì một lỗi ngầm ẩn sâu trong ba lớp.
Sự cố xảy ra ở đâu
Sự cố thường xảy ra khi một hàm ngữ nghĩa biến thành một hàm thực dụng để dễ dàng, và sau đó những vị trí khác trong cơ sở mã dựa vào đó sẽ thực hiện những việc mà chúng không có ý định. Để giải quyết vấn đề này, hãy rõ ràng khi tạo một hàm bằng cách đặt tên cho nó thay vì theo chức năng của nó mà theo nơi nó được sử dụng. Bản chất của tên của họ phải làm cho các lập trình viên khác trong tên của họ thấy rõ rằng hành vi của họ không được xác định chặt chẽ và không nên dựa vào những người nội bộ để thực hiện một nhiệm vụ chính xác, đồng thời giúp việc gỡ lỗi hồi quy từ họ trở nên dễ dàng hơn.
Các mô hình cũng hoạt động theo cách tương tự nhưng chậm hơn. Họ bắt đầu tập trung, sau đó ai đó thêm trường tùy chọn "chỉ một nữa" vì nó dễ hơn việc tạo một mô hình mới, sau đó người khác cũng làm như vậy và cuối cùng mô hình đó là một túi dữ liệu lỏng lẻo chứa một nửa dữ liệu liên quan đến một nửa mà mọi người tiêu dùng phải đoán trường nào thực sự được đặt và tại sao. Tên ngừng mô tả dữ liệu là gì, các trường ngừng liên kết xung quanh một khái niệm duy nhất và mọi tính năng mới chạm vào mô hình đều phải điều hướng các trạng thái mà nó chưa bao giờ được thiết kế để thể hiện. Khi các trường của một mô hình không còn gắn kết xung quanh tên của nó nữa, đó là tín hiệu để chia nó thành những phần riêng biệt mà nó đã ghép với nhau.
Tác giả: benswerd