Sử dụng công cụ AI (Github Copilot) trong việc hỗ trợ phát triển (dev/code)

Sử dụng công cụ AI (Github Copilot) trong việc hỗ trợ phát triển (dev/code)

Ngày nay có rất nhiều công cụ hỗ trợ cho việc phát triển code dựa trên AI như:

  • ChatGPT
  • Clause
  • Gemini
  • GitHub Copilot
  • Replit v.v…

Chúng là những công cụ hỗ trợ lý tưởng cho việc lập trình. Có rất nhiều cách tiếp cận trong việc sử dụng AI vào trong code, như việc sử dụng AI cho việc tự động hóa viết code thông qua cấu hình MCP, hay như việc sử dụng AI như một trợ lý ảo cho lập trình.

Trong bài viết này tập trung vào việc trình bày sử dụng AI như một trợ lý ảo cho lập trình viên (Github Copilot) mà tác giả đã sử dụng trong dự án thực tế.

Toàn bộ code minh họa trong bài viết Chia tầng (layered) cho ứng dụng Spring Boot được tạo từ Github Copilot thông qua các câu Prompt được giới thiệu trong bài viết này.

Một số lưu ý trước khi bắt đầu:

Nếu chỉ để tạo CRUD đơn thuẩn cho một đối tượng cụ thể với những công cụ AI mạnh mẽ như ngày nay có thể dùng 1 câu lệnh để tạo ra được một implement hoàn chỉnh, hoặc sử dụng các công cụ generate code như JHipster. Mình chia nhỏ thành các câu lệnh nhỏ để minh họa cho việc AI cần những thông tin đầu vào là gì để hoạt động tốt.

Các câu trả lời của AI có thể sẽ không hoàn toàn chính xác, mình hay gặp trong trường hợp như import có khi AI đưa ra một thư viện khoặc một hàm không có trong thực tế. AI rất mạnh mẽ khi xử lý các phần liên quan đến thuật toán và logic.

Một số lệnh build-in hữu ích

Trong Github Copilot (sau đây gọi tắt là Copilot hoặc AI) có xây dựng sẵn một số câu lệnh cho việc sử dụng

  • /explain ta bôi đen vào đoạn code để cho AI diễn giải logic, rất hữu ích khi tìm hiểu code của một dự án khác, hoặc muốn biết tính năng để bổ xung hoặc thay đổi chức năng cũ
  • /fix khi class của chúng ta bị lỗi complie (lỗi đỏ) chọn lệnh này AI sẽ tìm hiểu dựa trên ngữ cảnh (là file hiện tại, và các thông tin hỏi trước đó) để đưa ra giải pháp xử lý tình huống
  • /tests lệnh này rất hữu ích khi giúp chúng ta viết các UnitTest cho một class cụ thể, đứng tại class muốn viết UnitTest ta chỉ cần gọi lệnh này. Cần lưu ý, có những mô hình AI đưa ra những câu trả lời tối thiểu (bỏ qua phần import, tên cho test case, comment) ta có thể bổ xung sau câu lệnh /tests with full import and description for each test case
  • /simplify thường mình sẽ chạy câu lệnh này cuối cùng khi đã test nghiệp vụ chạy đúng, đứng tại một file gọi lệnh này AI sẽ tính toán và đưa ra một phương án code tối ưu gọn gàng hơn. Wow nhiều trường hợp mình cũng không ngờ tới có thể tối ưu thêm được.
  • /doc dùng để tạo JavaDoc hoặc tạo OpenAPI/Swagger cho APIs rất hiệu quả

Các câu lệnh build-in khác các bạn có thể tìm hiểu và sử dụng

Xây dựng các câu lệnh (prompt) cho AI hỗ trợ viết code

Dưới đây là các quan điểm và ý kiến cá nhân của mình, nó có thể không hoàn toàn đúng, mình chỉ đúc rút lại trong quá trình sử dụng AI trong các dự án thực tế.

Từ khóa quan trọng hơn ngữ pháp

Các mô hình học máy với ngôn ngữ lớn LLM đào tạo dữ liệu và đánh dấu, lưu trữ dữ liệu giá trị, để truy xuất dữ liệu đã được xử lý AI sử dụng các từ khóa ‘tokens’ và các cửa sổ ngữ cảnh ‘context window’ để đưa ra câu trả lời, AI không suy luận ra câu trả lời mà đưa ra các khả năng của câu trả lời dựa trên dữ liệu đã có.

Với một đoạn mệnh lệnh prompt mà chúng ta đưa vào AI sẽ tìm kiếm các từ khóa quan trọng, kết hợp với ngữ cảnh trước đó để lục tìm các câu trả lời cho chúng ta. Ví dụ khi mình muốn AI tạo giúp một class để cho thực thể Product câu prompt mình dùng là

create ProductEntity use Record in Java to store ProductRequest, also add more those fields: status, createdDate, modifiedDate, userId

Một số từ khóa mà AI sẽ dựa vào đó để tìm câu trả lời (theo mình): createrecordmore those fields

Kết quả đầu ra đúng như mình mong muốn, AI sẽ tham khảo ngữ cảnh trước đó là ProductRequest gồm những trường nào, nó sẽ thêm những trường mà trong câu prompt mình gợi ý, AI sẽ cho mình kiểu record không phải là class hoặc interface, các kiểu dữ liệu mình không đưa cụ thể nhưng với tên của biến AI có thể suy luận ra được kiểu dữ liệu mà các biến khác với tên này hay dùng, nó sẽ gợi ý cho chúng ra các kiểu dữ liệu đó.

package com.koder.course.applayered.entity;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * Entity record for Product, storing all request fields plus status, timestamps, and userId.
 */
public record ProductEntity(
    UUID uuid,
    String name,
    String sku,
    BigDecimal price,
    String desc,
    String status,
    LocalDateTime createdDate,
    LocalDateTime modifiedDate,
    UUID userId
) {}

câu lệnh trên nếu viết đầy đủ mặt ngữ pháp (có thể không chuẩn xác) sẽ có dạng

I want to create a class with the name is ProductEntity, it has record type and has all fields of ProductRequest, also....

Chúng ta có thể nhận được kết quả tương tự, một số từ khóa sẽ không nằm trong ngữ pháp của câu prompt.

Mình muốn nhấn mạnh ở đây là ta có thể viết những prompt cho AI ở mức độ ngữ pháp tối thiểu (đôi khi có thể sai), do vậy những lập trình viên đang kém chút về ngữ pháp cũng có thể sử dụng.

Oh, vậy có thể sử dụng tiếng Việt được không? Được, nhưng mình nghĩ, việc training và đánh dấu dữ liệu của AI hiện tiếng Anh đang chiếm số lượng lớn trong lĩnh vực phần mềm, nếu ta đưa prompt bằng tiếng Việt sẽ cần qua bộ thông dịch, do vậy các từ khóa quan trọng mà ta muốn có trong prompt có thể sẽ không được dữ nguyên khi prompt -> dịch TA -> tạo token -> tìm kiếm, do vậy kết quả có thể không được như ta mong muốn. AI thì luôn đưa ra câu trả lời với bất kỳ dữ liệu đầu vào nào.

AI là chương trình điện toán, nên không có cảm xúc, do vậy cũng không cần thêm vào các các câu chào hỏi và cảm ơn, đôi khi thêm vào các câu như vậy làm AI thêm những tính toán không cần thiết.

Một prompt có giới hạn ký tự

Một lệnh đưa cho AI, nó sẽ phân tích câu lệnh đầu vào và biến thành tokens, mỗi AI có giới hạn tokens, con số chính sác mình không nhớ rõ. Nhưng nó cũng khuyển khích một điều, đưa càng nhiều dữ liệu, đầu vào càng tốt.

Trong dự án cũ cần maintain khi AI không theo mình phát triển từ đầu, hoặc ngôn ngữ cách viết trong dự án đó khá cũ và có nhiều khác biệt so với mô hình code ngày nay, cách tốt nhấy là cung cấp nhiều thông tin nhất có thể cho AI. Có một case cụ thể trong dự án như sau, dự án A phát triển trên một ngôn ngữ mới mà mình chưa làm trước đó, mình cần xử lý một nghiệp vụ merge hai file svc lại với nhau và có một số nghiệp vụ cần thực hiện khi merge. Để thực hiện việc này mình sẽ xây dựng câu prompt có cấu trúc như sau:

  • Ngôn ngữ mong muốn
  • Cung cấp cấu trúc của 2 file csv
  • Mô tả nghiệp vụ
  • Mô tả một sổ tình huống với dữ liệu cụ thể

AI đưa ra được phiên bản đầu tiên

  • Yêu cầu test đầu vào với dữ liệu giả định
  • Mô tả thêm những case đặt biệt
  • Check null, empty, …

AI cập nhật và đưa ra phiên bản tiếp theo, cứ lặp lại như vậy đến khi cảm thấy được phiên bản tốt hoặc có thể chấp nhận được.

Nó có giới hạn nhưng cứ cung cấp càng nhiều càng tốt

Ngoài cửa sổ chat, có thể hỏi ngay trên Editor

Khi ta sử dụng Copilot với các IDE hỗ trợ như IntelliJ hoặc Visual Studio Code việc gợi ý code được thực hiện ngay trên Editor, AI đưa ra các đoạn code tiếp theo dựa trên ngữ cảnh đang có được.

Để cụ thể hóa gợi ý, code ta có thể tạo ra các code comment, dựa vào comment code AI sẽ đưa ra gợi ý (code suggestion) cho chức năng mong muốn, thông thường sẽ dùng phím Tab để chấp nhận hoặc dấu cách để bỏ qua

Với code comment là

// Validates if a product with the given name already exists.
// If it does, it throws the ProductNameExistingException exception

AI sẽ gợi ý cho ta một hàm tương ứng, ở đây ta có thấy đoạn code được gợi ý, hàm này bản chất không tồn tại AI dựa trên dữ liệu thông thường hàm sẽ có tên như vậy khi sử dụng JPA, nhưng ở đây mình đã tạo ra hàm khác có tên là existsByNameIgnoreCase, do vậy sẽ bị báo đỏ khi apply đoạn code này. Ta chỉ điều chình để phù hợp với tên hàm thực tế là được

boolean exists = productEntityRepository.existsByName(productName);

Danh sách các câu lệnh prompt để xây dựng CRUD APIs trên Spring Boot

Tham khảo lại bài viết về chia lớp cho ứng dụng CRUD tại đây

Trước hết ta hình dung luồng để xây dựng ứng dụng: DTO -> Interface for APIs -> Entity -> Repository -> Exceptions -> Validator -> Advice Controller -> Mapper -> Service -> Implement APIs -> support to create SQL file

Code được tạo ra cần được tinh chỉnh bởi developer, nhưng khi đã hiểu về Code và được API hỗ trợ việc tạo code việc lập trình trở lên dễ dàng hơn, và code có chất lượng tốt hơn.

Tạo các DTO

#1 create ProductRequest with those fields: uuid, name, sku, price, base, desc

#2 can you use lombok

#3 use valid for all field in ProductRequest

Đây là kết quả ta nhận được

package com.koder.course.applayered.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;

import java.math.BigDecimal;
import java.util.UUID;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ProductRequest {
    @NotNull
    private UUID uuid;

    @NotBlank
    private String name;

    @NotBlank
    private String sku;

    @NotNull
    @Positive
    private BigDecimal price;

    @NotBlank
    private String base;

    @NotBlank
    private String desc;
}

Tạo ProductResponse

create ProductResponse with all fields same ProductRequest but add more those fields: status, createDate, updateDate

Tạo interface cho CURD APIs

create interface for CRUD restful apis for Product, using ProductRequest as request object, productresponse as response object

Khi ta muốn tạo tài liệu cho API, ta có thể dùng lệnh

/doc add swagger (openAPI) for this CRUD, use ProblemDetail for case error

Nó sẽ bổ xung thêm annation cho các interface đã được tạo ra trước đó

@Operation(summary = "Create a new product")
@ApiResponses(value = {
    @ApiResponse(responseCode = "200", description = "Product created",
        content = @Content(schema = @Schema(implementation = ProductResponse.class))),
    @ApiResponse(responseCode = "400", description = "Invalid input",
        content = @Content(schema = @Schema(implementation = ProblemDetail.class)))
})
@PostMapping
ResponseEntity<ProductResponse> createProduct(
    @RequestBody ProductRequest request
);

Trong trường hợp mình quên không thêm dependency cho OpenAPI, ta có thể nhờ AI add

help me add openapi dependency

và đây là kết quả

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'

Tạo Entity Object và Repository Interface

#1 create ProductEntity use Record in Java to store ProductRequest, also add more those field: status, createdDate, modifiedDate, userId

#2 create a repository for ProductEntity use CRUD jpa

Tạo exception class

create ProductNameExistingException extend RuntimeException

Tạo validator

Với logic cho validator như mình đã miêu tả ở trên, sau khi tạo ra một class trống, đưa ra một số mô tả về class nó sẽ gợi ý cho ta method mong muốn

// Validates if a product with the given name already exists.
// If it does, it throws the ProductNameExistingException exception

Tạo lớp mapper

create ProductMapper class, it has a method for mapping ProductEntity to ProductResponse, becareful ProductEntity with type record

Tạo ProductService

ProductService kết hợp sử dụng mapperrepository do vậy ta tạo mẫu một class và mô tả method mong muốn, AI có thể bị nhầm khi gợi ý bởi các dự án trước đó đã thực hiện. Như hiện tại trên máy mình cần khai báo tường minh cho AI không sử dụng reactive, vì các dự trước đó mình có sử dụng reactive do vậy nó có thể gợi ý sử dụng reactive.

// Method to save a product from ProductRequest
// no reactive implementation here, just a simple save
// return ProductResponse
public ProductResponse saveProduct(ProductRequest productRequest, String userId) {
    log.info("Saving product: {}", productRequest);
    ProductEntity productEntity = productMapper.toProductEntity(productRequest, userId);
    ProductEntity savedProduct = productRepository.save(productEntity);
    log.info("Product saved successfully: {}", savedProduct);
    return productMapper.toProductResponse(savedProduct);
}

Lần đầu có thể bị sai cần điều chỉnh, nhưng những lần sau khi AI quen với style viết code như vậy nó sẽ gợi ý sát hơn.

Tương tự với ProductApisImpl

Tạo GlobalExceptionHandler

Help me create advice controller class to catch exception then then response to client

AI gợi ý cho ta tạo một ErrorResponse class và GlobalExceptionHandler nhưng trong GlobalExceptionHandler thiếu việc handle cho case trùng tên ProductNameExistingException ta có thể yêu cầu thêm

add execption for ProductNameExistingException return badrequest code 400

Tổng kết

  • Có nhiều cách áp dụng AI để hỗ trợ việc lập trình ta lựa chọn và áp dụng linh hoạt
  • Sử dụng hiệu quả các build-in command của AI
  • AI cũng có thể sai
  • AI không suy luận như con người, AI đưa ra câu trả lời dựa trên các tập dữ liệu đã có được, dựa vào tokens phân tích từ prompt truyền vào các các cửa sổ ngữ cảnh AI đưa ra các câu trả lời tương ứng
  • Hiểu được nguyên tắc AI để xây dựng các prompt hiệu quả hơn
Written by :

user

Java Developer, System Architect, Learner and becoming Youtuber

View All Posts

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *