8 kỹ thuật prompt engineering thực dụng cho developer dùng LLM trong coding
8 kỹ thuật prompt engineering có thể áp dụng ngay để dùng LLM viết code, review, debug và sinh test ít lỗi hơn.
Mở đầu
Developer đang dùng LLM nhiều hơn, nhưng phần lớn vẫn prompt kiểu: “viết API login”, “fix bug này”, “review code giúp”. Kết quả: code chạy được ở demo, vỡ khi vào repo thật. Lý do không nằm hết ở model. Vấn đề lớn nằm ở input: thiếu context, thiếu ràng buộc, thiếu tiêu chí kiểm tra.
LLM không đọc được ý định trong đầu bạn. Nó dự đoán output dựa trên prompt, context window, training data, tool access. Prompt mơ hồ → output trung bình. Prompt có cấu trúc → output kiểm soát tốt hơn.
Bài này tập trung vào 8 kỹ thuật prompt engineering thực dụng cho developer: viết feature, debug, review PR, sinh test, refactor, tạo migration, đọc log. Không nói “AI thay đổi tương lai”. Chỉ nói cách dùng để giảm bug, giảm vòng lặp sửa sai, tăng chất lượng code trong workflow hằng ngày.
8 kỹ thuật prompt engineering dùng được ngay
1. Giao vai trò cụ thể, nhưng đừng dừng ở “you are senior engineer”
Prompt “Bạn là senior backend engineer” có ích rất ít nếu không gắn domain, stack, tiêu chuẩn output. Role tốt phải nói rõ phạm vi quyết định.
Ví dụ kém:
1
Bạn là senior backend engineer. Viết API tạo đơn hàng.
Ví dụ tốt:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Bạn là senior backend engineer làm Node.js + PostgreSQL.
Nhiệm vụ: thiết kế endpoint POST /orders cho hệ thống thương mại điện tử.
Ràng buộc:
- Dùng Express.js
- Dùng transaction PostgreSQL
- Không trừ tồn kho nếu payment chưa confirmed
- Validate input bằng zod
- Trả lỗi theo format { code, message, details }
- Không viết pseudo-code
Output:
1. Route handler
2. Service function
3. SQL schema cần thiết
4. 3 test case quan trọng
Khác biệt: prompt thứ hai cho model biết stack, invariant nghiệp vụ, format lỗi, output mong muốn. Nó giảm khả năng model tự chọn framework, tự bịa flow payment, tự bỏ transaction.
2. Đưa context theo lớp: mục tiêu → hiện trạng → constraint → output
Nhiều developer paste cả file 800 dòng rồi hỏi “sửa bug”. Model dễ lạc. Context nên chia lớp.
Cấu trúc dùng được:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Mục tiêu:
Fix lỗi user bị charge tiền 2 lần khi retry payment callback.
Hiện trạng:
- Backend: NestJS
- DB: PostgreSQL
- Payment provider có thể gửi callback trùng trong 5 phút
- Bảng payments có cột provider_event_id nhưng chưa unique
Code liên quan:
[PASTE CODE]
Constraint:
- Không đổi public API
- Không drop data
- Cần migration an toàn cho production
- Callback phải idempotent
Yêu cầu output:
1. Giải thích root cause
2. Đề xuất fix
3. Viết migration SQL
4. Viết code patch
5. Liệt kê test cases
LLM mạnh ở tổng hợp, nhưng yếu khi phải đoán ưu tiên. Context theo lớp biến bài toán mơ hồ thành checklist kỹ thuật.
3. Bắt model hỏi lại nếu thiếu dữ kiện
Mặc định nhiều model sẽ “điền vào chỗ trống”. Với coding, đây là nguồn bug lớn. Thêm rule: không đủ dữ kiện thì hỏi.
1
2
3
Nếu thiếu thông tin để đưa ra code an toàn, hãy hỏi tối đa 5 câu trước.
Không được tự giả định schema, framework version, hoặc business rule.
Nếu phải giả định, ghi rõ "Giả định:" trước khi viết code.
Kỹ thuật này đặc biệt hữu ích khi làm migration, auth, payment, queue worker, cache invalidation. Những phần này sai một chút là tạo incident.
Ví dụ: nếu bạn hỏi “thêm Redis cache cho API product”, model tốt phải hỏi TTL, invalidation rule, cache key, stale data acceptable không. Nếu nó nhảy ngay vào code redis.setex(), prompt chưa đủ chặt.
4. Yêu cầu reasoning ngắn, không cần chain-of-thought dài
Bạn không cần model show toàn bộ suy luận. Nhưng cần nó nêu quyết định kỹ thuật chính. Prompt tốt:
1
2
3
4
5
6
7
Trước khi viết code, hãy nêu ngắn:
- Assumptions
- Edge cases
- Failure modes
- Trade-off của giải pháp
Sau đó mới viết code.
Output này giúp review nhanh. Ví dụ với API import CSV, failure modes có thể là file lớn, encoding sai, duplicate row, partial failure, retry job. Nếu model bỏ qua, bạn phát hiện sớm trước khi đọc code.
Tránh yêu cầu “suy nghĩ từng bước thật chi tiết”. Thay vào đó, yêu cầu “decision summary”. Ngắn hơn, dễ kiểm tra hơn, ít noise hơn.
5. Dùng prompt theo pha: design trước, code sau, test cuối
Một lỗi phổ biến: yêu cầu model viết full solution ngay. Với feature vừa đủ phức tạp, nên chia 3 pha.
Pha 1: thiết kế.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Thiết kế giải pháp cho rate limit API login.
Stack:
- Go
- Gin
- Redis
- PostgreSQL
Yêu cầu:
- Limit theo IP + email
- Chống brute force
- Không lock nhầm user sau 1-2 lần sai
- Có metric cho Prometheus
Chỉ output design, data structure Redis, failure modes. Chưa viết code.
Pha 2: code theo design đã chốt.
1
2
3
4
5
6
Dựa trên design trên, viết middleware Gin.
Yêu cầu:
- Code production-ready
- Context timeout cho Redis
- Fail-open hay fail-closed phải ghi rõ
- Không dùng global mutable state
Pha 3: test.
1
2
3
4
5
6
Viết unit test cho middleware:
- Login sai dưới limit
- Vượt limit theo IP
- Vượt limit theo email
- Redis timeout
- Reset sau TTL
Lợi ích: bạn review được kiến trúc trước khi model sinh nhiều code. Sai design thì sửa rẻ. Sai sau 300 dòng code thì tốn công.
6. Cung cấp ví dụ input/output giống production
LLM học pattern từ prompt. Nếu bạn đưa sample nông, output sẽ nông. Với API, log parser, SQL report, data transform, hãy đưa ví dụ thật hoặc gần thật.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Viết function parse log Nginx thành JSON.
Input sample:
127.0.0.1 - - [17/May/2026:08:12:33 +0700] "POST /api/orders HTTP/1.1" 201 532 "-" "Mozilla/5.0" request_time=0.243 upstream_time=0.198 trace_id=abc-123
Output mong muốn:
{
"ip": "127.0.0.1",
"method": "POST",
"path": "/api/orders",
"status": 201,
"bytes": 532,
"request_time_ms": 243,
"upstream_time_ms": 198,
"trace_id": "abc-123"
}
Edge cases:
- upstream_time="-"
- path có query string
- user agent chứa dấu ngoặc kép escaped
Ví dụ này ép model xử lý kiểu dữ liệu, unit conversion, edge case. Không cần giải thích dài.
7. Bắt output theo format có thể copy vào workflow
Nếu muốn dùng LLM trong CI, PR review, runbook, issue triage, format phải ổn định. Markdown tự do khó parse. Dùng JSON, YAML, checklist, diff.
Ví dụ prompt review PR:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Review patch dưới đây như backend reviewer.
Chỉ output theo format:
## Blockers
- [file:line] Vấn đề, impact, fix đề xuất
## Non-blockers
- [file:line] Vấn đề, impact, fix đề xuất
## Missing tests
- Test case cụ thể
## Security notes
- Rủi ro nếu có
Nếu không có issue trong mục nào, ghi "None".
Patch:
[PASTE DIFF]
Format này giúp team copy thẳng vào PR comment. Quan trọng hơn: nó ép model phân loại mức độ. Không phải mọi góp ý đều đáng block merge.
Với coding agent, format còn quan trọng hơn. Prompt nên yêu cầu patch nhỏ, file list, command test:
1
2
3
4
5
Output:
1. Files changed
2. Unified diff
3. Commands to run
4. Rollback note
8. Thêm vòng kiểm chứng: model tự phản biện output
LLM có thể viết code sai nhưng nhìn vẫn hợp lý. Sau khi sinh output, yêu cầu nó kiểm tra bằng checklist riêng.
1
2
3
4
5
6
7
8
9
Sau khi viết code, tự review theo checklist:
- Có race condition không?
- Có transaction boundary rõ không?
- Có leak secret vào log không?
- Có timeout cho network call không?
- Có xử lý retry/idempotency không?
- Có test cho failure path không?
Nếu phát hiện lỗi, sửa lại code trước khi trả lời cuối.
Kỹ thuật này không thay thế review người thật. Nhưng bắt được nhiều lỗi cơ bản: quên await, quên rollback, log raw token, HTTP client không timeout, SQL query thiếu index.
Với PostgreSQL, thêm checklist riêng:
1
2
3
4
5
6
PostgreSQL checklist:
- Query có dùng index không?
- Migration có lock bảng lâu không?
- Có cần CONCURRENTLY không?
- Có backfill theo batch không?
- Constraint có NOT VALID/VALIDATE CONSTRAINT không?
Đây là phần nhiều model hay bỏ qua nếu bạn không nhắc. Migration production không giống migration local. Prompt phải nói rõ.
Ứng dụng thực tế: dùng LLM như pair engineer trong backend team
Một backend team 5-10 người có thể dùng 8 kỹ thuật trên vào 3 điểm đau: review PR, debug production, viết test.
Với review PR, team tạo prompt cố định nhận git diff. Output chia thành Blockers, Non-blockers, Missing tests, Security notes. Reviewer người vẫn quyết định, nhưng LLM lọc trước lỗi cơ bản: thiếu validate input, query N+1, log thông tin nhạy cảm, thiếu test failure path. Kết quả thực tế: reviewer không mất 20 phút soi những lỗi lặp lại, tập trung vào business logic và kiến trúc.
Với debug production, prompt theo lớp giúp giảm nhiễu. Thay vì paste log và hỏi “lỗi gì”, engineer đưa timeline, deploy version, metric thay đổi, log sample, code liên quan, constraint rollback. LLM có thể đề xuất giả thuyết: connection pool cạn, lock DB, retry storm, cache stampede. Quan trọng: nó tạo checklist điều tra nhanh, không thay thế observability.
Với viết test, LLM mạnh nếu bạn đưa interface và edge case. Ví dụ service tạo order cần test: tồn kho không đủ, payment pending, callback trùng, transaction rollback, concurrent request. Developer thường test happy path. Prompt đúng ép model sinh failure path.
Cách triển khai nhẹ: lưu prompt vào repo tại .github/ai-prompts/. Ví dụ:
1
2
3
4
.github/ai-prompts/pr-review.md
.github/ai-prompts/debug-incident.md
.github/ai-prompts/generate-tests.md
.github/ai-prompts/postgres-migration-review.md
Mỗi prompt có format output cố định. Team chỉnh dần sau mỗi PR hoặc incident. Prompt trở thành tài sản kỹ thuật, giống runbook.
Kết luận + Takeaway
Prompt engineering tốt không phải mẹo vặt. Nó là cách đóng gói context, constraint, tiêu chí kiểm tra cho LLM.
Takeaway cụ thể:
- Đừng hỏi chung. Luôn đưa mục tiêu, hiện trạng, constraint, output format.
- Chia việc lớn thành design → code → test. Review sớm, sửa rẻ.
- Thêm checklist tự kiểm chứng cho phần rủi ro: transaction, migration, security, retry, timeout.
LLM viết code nhanh. Prompt tốt giúp nó viết ít sai hơn. Developer vẫn chịu trách nhiệm cuối.