Chuyển đến nội dung chính

Phát triển phần mềm với Devin: Bảo mật, Triển khai, Bảo trì (Phần 4)

Tìm hiểu cách dùng Devin để thêm xác thực người dùng thực với NextAuth, giám sát lỗi bằng Sentry, và triển khai frontend lên Vercel với URL xem trước.
Đã cập nhật 5 thg 10, 2026  · 12 phút đọc

Khám phá cùng AI

ChatGPTClaudePerplexity

Chào mừng quay trở lại! Sắp về đích rồi: Slack đang réo, Jira có ticket, test đều xanh, và CI chặn mọi PR cẩu thả. Phần bốn là cú hích cuối, nơi chúng ta sẽ biến fp-ts playground thành thứ mà về lý thuyết có thể cho công chúng dùng thử.

Bạn có thể truy cập toàn bộ chuỗi hướng dẫn Devin tại đây:

  1. Thiết lập và Pull Request đầu tiên (Phần 1)
  2. Triển khai một lát cắt dọc với Devin (Phần 2)
  3. Tích hợp, Kiểm thử, và CI/CD (Phần 3) 
  4. Bảo mật, Triển khai, Bảo trì (Phần 4)

Kế hoạch cho bài hướng dẫn thứ tư:

  • Auth: Không còn UUID ẩn danh; chúng ta sẽ thêm NextAuth credentials, cookie JWT, và một GqlAuthGuard để tiến độ gắn với người dùng thực.
  • Theo dõi production: Một dòng Sentry trong web app, một dòng trong Nest API, và mọi exception đổ về dashboard.
  • Triển khai một nút bấm: Front-end lên Vercel với URL xem trước trên mỗi PR. (API tạm thời vẫn local, chúng ta giả lập prod bằng Docker.)

Chúng ta cũng sẽ nhìn lại trải nghiệm pair programming với Devin để xem đâu là điểm mạnh và đâu là chỗ con người vẫn cần chỉnh chu trước khi mở repo cho thế giới. Sẵn sàng cho chặng cuối?

Từ UUID ẩn danh đến người dùng thực

Tôi đã yêu cầu Devin làm gì

Tóm lại, tôi yêu cầu Devin bỏ hẳn luồng UUID ẩn danh và dựng cho tôi một stack auth chuẩn chỉnh:

  • NextAuth v6 credentials provider trong apps/web
  • NestJS AuthModule với băm Argon2 và cookie JWT
  • Prisma migration thêm bảng User và đổi Progress.sessionId → userId (không null)
  • Sao chép mọi dòng tiến độ UUID hiện có sang một người dùng placeholder để người học giữ được huy hiệu
  • Không chuyển Postgres sang SQLite

Kết quả trả về (phần lớn ổn)

Tóm tắt kết quả:

Lớp

Đầu ra của Devin

Cơ sở dữ liệu

Xoá bảng Session; thêm model User với các trường email, hash, streak và timestamp. Script migration điền lại từng dòng sessionId cũ thành một pseudo-user tên legacy-<uuid>@anon.local.

Backend

AuthModule, AuthService, GraphQL AuthResolver, JwtStrategy, và một GqlAuthGuard. Theo đúng chuẩn Nest, 100% có kiểu.

Frontend

AuthProvider React context, LoginForm, RegisterForm, một modal bật lên ở các route cần bảo vệ, và nút đăng xuất trong header dashboard.

Bảo mật

Argon2 (12 vòng), xác thực DTO bằng class-validator, JWT lưu trong localStorage, guard ở mọi resolver của progress.

Kiểm thử

Unit test cho AuthService.register/login và một e2e mutation test xác nhận guard từ chối khi thiếu token. (Nhắc lần hai để nó thêm test)

Lại thêm một migration SQLite

Đoán xem tôi lại thấy gì trong ô suy nghĩ nhỏ xíu của Devin.

Đã chuyển thành công từ PostgreSQL sang SQLite cho phát triển local 🥳

Chính xác điều tôi đã bảo đừng làm. Prisma schema bị viết lại cho SQLite, chuỗi kết nối và kiểu dữ liệu cũng đổi (từ TIMESTAMP sang DATETIME). Tôi bắt quả tang giữa chừng:

  1. Tạm dừng phiên.
  2. git checkout main apps/api/prisma/schema.prisma (khôi phục schema Postgres).
  3. Xoá file dev.db mới.
  4. npm run prisma:migrate --workspace=apps/api để áp lại các migration Postgres.
  5. Tiếp tục Devin; nó phát hiện việc hoàn tác và chạy tiếp không phàn nàn.

Thiệt hại: 0.8 ACU và khoảng mười phút.

Checklist thay đổi phá vỡ cho đồng đội

Tôi rất thích phần hướng dẫn Devin để lại trong PR. Ở đây, nó dặn chúng tôi:

# 1  Install new deps
npm install --workspaces

# 2  Run the Postgres migration
npm run prisma:migrate --workspace=apps/api

# 3  Restart everything
npm run dev:api   &   npm run dev:web

Ảnh chụp kiến trúc

Trong wiki, tôi thấy sơ đồ hữu ích này về hệ thống auth mới của chúng ta:

Sơ đồ của Devin cho hệ thống auth mới của chúng tôi

Với người dùng thực đã sẵn sàng và cơ sở dữ liệu kiên định trên Postgres, cuối cùng chúng ta đã sẵn sàng theo dõi tiến độ học thật sự, gửi bản dựng xem trước lên Vercel, và bắt lỗi runtime bằng Sentry.

Front-end trên Vercel, liên kết xem trước trên mọi PR

Khi đăng nhập an toàn đã ổn, cột mốc hiển nhiên tiếp theo là cho thế giới thấy thứ gì đó có thể bấm được. Chúng tôi quyết định chỉ triển khai front-end Next.js lúc này. API và Postgres sẽ vẫn ở máy local của tôi cho đến khi chọn được nhà cung cấp phù hợp ngân sách.

Kết nối tài khoản thủ công (0 ACU)

Chưa thể kết nối Devin với tài khoản Vercel cá nhân của tôi qua tích hợp gốc. Vì vậy tôi làm thủ công qua bảng điều khiển Vercel:

  1. Đăng nhập Vercel và bấm “New Project → Import Git Repository”.
  2. Chọn repo fp-ts-exercises, giữ nguyên lệnh build là npm run build --workspace apps/web, rồi bấm Deploy.
  3. Sao chép VERCEL_PROJECT_ID và VERCEL_TOKEN vào GitHub Secrets.

Để Devin cập nhật workflow CI (0.4 ACU)

Theo lời nhắc của tôi, Devin thêm một job vào cuối workflow hiện có và một bước shell nhỏ curl URL tạo ra để gửi Slack:

- name: Deploy to Vercel
  if: ${{ success() && github.event_name == 'pull_request' }}
  run: npx vercel deploy --prod --token ${{ secrets.VERCEL_TOKEN }}
  env:
    VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
    VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

Kết quả

Đẩy nhánh → CI xanh → Slack ping:

✅ Preview ready: https://fp-ts-exercises-git-my-branch.vercel.app

Mở liên kết hiển thị playground của chúng ta, luồng auth đầy đủ, và dashboard người học. Chưa cần cấu hình môi trường trên Vercel, vì front-end nói chuyện với API local của tôi qua http://localhost:4000.

Nhìn kỹ hơn vào hệ thống Secrets của Devin

Trước khi tích hợp Sentry, tôi xem qua hệ thống secrets của Devin để quyết định có nên dùng không. Devin cung cấp một kho “Secrets” tích hợp để bạn đưa cho agent khóa API, URL DB, hoặc token OAuth mà không phải commit vào Git và không phơi ra trong chat. Hãy coi nó như một bản tương đương gọn nhẹ của GitHub Actions secrets hoặc Environment Variables của Vercel, nhưng phạm vi trong workspace đám mây của riêng Devin.

Tính năng

Cách hoạt động

Ghi chú

Phạm vi

Secrets có ba phạm vi: tổ chức, repo, và phiên.

Bạn có thể cung cấp secrets ở cấp tổ chức, dùng cho mọi phiên.

Nếu muốn thêm secrets cho một repo duy nhất, bạn có thể chỉ định khi thiết lập repo.Nếu muốn secrets theo từng phiên, bạn có thể cung cấp trong cài đặt phiên. 

Mã hoá

Lưu phía server bằng AES-256 và dùng TLS 1.3+ khi truyền

Mọi dữ liệu khách hàng được mã hoá bằng khoá KMS tuỳ chỉnh, do AWS quản lý.

Không tốn ACU

Thêm, sửa, hoặc xóa secret là thao tác control-plane: 0 ACU. Chỉ các tác vụ dùng secret mới tiêu tốn compute credits.

Nơi phù hợp để cất token sống lâu.

Truy cập trong prompt

Dùng $.<NAME> trong prompt hoặc lệnh. Devin sẽ thay thế trước khi chạy shell.

Ví dụ: DATABASE_URL=$.POSTGRES_URL

Giới hạn kích thước

4 KB mỗi khoá, 100 khoá mỗi đội trên gói Core.

Đủ cho chứng chỉ PEM hoặc khoá riêng.

Thêm một secret

Các bước để thêm secret:

  1. Mở Team → Secrets trong bảng điều khiển Devin.
  2. Bấm “Add Secret” và nhập tên (SENTRY_DSN_WEB) cùng giá trị.
  3. Devin xác nhận lưu trữ; khoá giờ khả dụng với tên $.SENTRY_DSN_WEB.

lưu một secret mới trong devin

Dùng secret trong một tác vụ

Tác vụ: cập nhật Sentry init của API; Đặt DSN thành $SECRET.SENTRY_DSN_API

Devin thay thế giá trị khi nó ghi main.ts. Trong tab shell, bạn sẽ thấy DSN dạng thô, nhưng nhật ký chat thì che đi.

Ưu và nhược điểm

Ưu điểm:

  • Không lo rò rỉ khoá vào PR hoặc log Timelapse.
  • Secrets phạm vi tổ chức dùng được qua nhiều phiên: tải lên một lần, dùng nhiều lần.
  • Lưu trữ hoặc lấy ra không tốn credit.

Nhược điểm:

  • Khoá không sẵn có ở local trừ khi bạn chép vào .env. Test chạy ngoài Devin cần thiết lập thủ công.
  • Không có phạm vi theo môi trường (ví dụ, “chỉ ở production”). Mọi phiên đều thấy mọi secret.

Khi nào nên dùng

Tôi thấy dễ hơn khi dùng kho của Devin lúc bản thân Devin cần khoá (ví dụ, đẩy lên Vercel, truy cập registry riêng, hoặc nối Sentry trong quá trình build). Tôi sẽ tiếp tục dùng .env / GitHub Actions secrets cho mọi thứ cũng chạy trong shell dev local hoặc runner CI.

Tích hợp Devin với Sentry

Đây là prompt tôi dùng: Thêm Sentry vào cả hai app để mọi exception xuất hiện trên dashboard của tôi. Dùng biến môi trường trong .env. Không thay đổi hành vi nào khác.”

Công việc của Devin (0.6 ACU, một phiên)

Tóm tắt kết quả:

Stack

File đã chỉnh

Mã chính

Next.js (web)

apps/web/sentry.client.config.tsx apps/web/sentry.server.config.ts và một import trong app/layout.tsx

ts import * as Sentry from '@sentry/nextjs'; Sentry.init({ dsn: process.env.SENTRY_DSN_WEB, tracesSampleRate: 1.0 });

NestJS (API)

apps/api/src/sentry.ts và hai dòng trong main.ts

ts import * as Sentry from '@sentry/node'; Sentry.init({ dsn: process.env.SENTRY_DSN_API }); app.useGlobalFilters(new Sentry.GqlExceptionFilter());

Khung env

Thêm placeholder vào .env.example

SENTRY_DSN_WEB=   SENTRY_DSN_API=

Kiểm thử

Thêm Jest spy để xác nhận Sentry.captureException được gọi khi ép sinh lỗi.

 

Các bước thủ công của tôi

Tôi đã làm như sau:

  1. Sao chép DSN từ dự án Sentry của tôi vào .env.
  2. Khởi động lại cả hai server dev.
  3. Gọi http://localhost:4000/graphql bằng một truy vấn cố tình sai để kiểm tra. Một issue xuất hiện trên Sentry sau vài giây.

Vì sao tôi bỏ qua tính năng secrets của Devin

Lý do tôi bỏ qua tính năng secrets của Devin:

  • Mô hình tinh thần đơn giản hơn: cùng một file .env ở mọi nơi.
  • Dễ chạy bản dựng xem trước trên Vercel (các biến môi trường đó đã được đặt sẵn ở đó).
  • Không đốt ACU cho các thao tác với kho secret.

Suy ngẫm: Devin làm tốt ở đâu và vấp ở đâu

Bốn phần trước, repo này chỉ là đống bài tập fp-ts phủ bụi—giờ nó đã có:

  • Hiện đại hoá codebase: Dependencies mới nhất, TS cấu hình nghiêm ngặt, luật lint & Prettier (Phần 1).
  • Playground chạy trong trình duyệt: Next.js 14 + trình soạn Sandpack với phản hồi Vitest trực tiếp (Phần 2).
  • Backend NestJS + Postgres: API GraphQL, Prisma migration, theo dõi tiến độ từ ẩn danh → JWT (Phần 2 & 4).
  • Pipeline xanh: CI lint, kiểm tra kiểu, chạy Jest & Playwright, và chặn mọi PR lỗi (Phần 3).
  • Tích hợp nhóm: Ping trạng thái Slack, phân tích nhãn Jira, và URL xem trước trên mọi nhánh (Phần 3).
  • Khả kiến production: Sentry nối vào cả web lẫn API, ghi nhận mọi exception (Phần 4).
  • Triển khai xem trước: Vercel tạo URL chia sẻ cho mỗi pull request (Phần 4).

Chúng tôi tiêu tốn khoảng ≈ 70 ACU tổng cộng (~$157 trên gói Core) và khoảng hai ngày làm việc để rà soát trực tiếp.

Devin làm rất tốt điều gì

Những điểm Devin làm rất tốt:

Lĩnh vực

Vì sao gây ấn tượng

Hạ tầng khuôn mẫu

AuthModule scaffold, Dockerfile, YAML workflow: tạo trong vài phút, không lỗi.

Mã theo chuẩn framework

Decorator NestJS, cấu hình NextAuth, Prisma migration đều khớp tài liệu chính thức.

Dựng bộ kiểm thử

Bộ unit và Playwright chạy ngay từ đầu—không thiếu stub cho các cạnh lề.

Nơi con người vẫn hơn agent

Những điểm Devin còn cần cải thiện:

Điểm đau

Ví dụ

Đánh bóng tính năng xuyên lớp

Trình soạn Sandpack hard-code sang stub tests; không nắm được tên component SandpackTests.

Duy trì cấu hình

Mặc định quay về SQLite 5 lần, dù đã nhấn mạnh “Phải ở lại Postgres.”

Đặt tên / dọn dẹp

Tiêu đề PR lệch hướng, file cũ đọng lại trừ khi bạn bảo Devin xoá.

Gu thẩm mỹ & UX

Dashboard trông tạm ổn, nhưng khoảng cách, câu chữ, màu sắc vẫn cần chỉnh tay.

Ý kiến của tôi

Tài liệu của chính Devin chưa bao giờ hứa hẹn là trình dựng sản phẩm. Họ nêu một điểm mạnh hẹp hơn nhiều, và nhìn lại, lẽ ra tôi nên ở trong phạm vi đó. Trang chính thức “Điểm mạnh của Devin là gì?” liệt kê bốn mục tiêu nổi bật:

điểm mạnh của devin

Tài liệu gọi phát triển tính năng bao trùm UI, API, và luồng dữ liệu là “thử nghiệm”. Chúng được mô tả là các tác vụ lớn, mở, nơi agent có thể “cần nhiều vòng rà soát và lặp lại của con người.” Đó chính xác là những gì chúng ta thấy khi Sandpack hard-code test hoặc khi dashboard trông ổn mà không thực sự tính toán dữ liệu.

Vì vậy, quy tắc ngón tay cái:

Hãy dùng Devin cho bất kỳ việc gì một lập trình viên senior có thể tự động hoá bằng script, và giữ tay lái cho các tác vụ cần cảm quan sản phẩm.

Kết luận

Chúng ta đã đến cuối chuỗi bốn phần:

  1. Thiết lập và Pull Request đầu tiên (Phần 1)
  2. Triển khai một lát cắt dọc với Devin (Phần 2) 
  3. Tích hợp, Kiểm thử, và CI/CD (Phần 3) 
  4. Bảo mật, Triển khai, Bảo trì (Phần 4)

Một vài bước tiếp theo chúng ta có thể thực hiện:

  1. Host API để các bản dựng xem trước (và người dùng sau này) truy cập backend thật.
  2. Đảm bảo chức năng cốt lõi hoạt động và sửa 30 lỗi mà tôi biết là đang tồn tại
  3. Đánh bóng UI
  4. Thêm nội dung và bài tập
  5. Phát hành cho mọi người dùng!

Nếu bạn đi theo con đường này, hãy nhớ: bạn có thể để agent đổ bê tông, nhưng bạn vẫn phải là người thiết kế ngôi nhà. Chúc bạn code vui vẻ!


Marie Fayard's photo
Author
Marie Fayard

Tôi là một trưởng nhóm kỹ thuật định hướng sản phẩm, chuyên giúp các startup giai đoạn đầu phát triển từ nguyên mẫu đầu tiên đến khi đạt được sự phù hợp sản phẩm - thị trường và xa hơn nữa. Tôi luôn tò mò về cách con người sử dụng công nghệ, và tôi thích làm việc sát sao với nhà sáng lập và các nhóm liên chức năng để biến những ý tưởng táo bạo thành hiện thực. Khi không xây dựng sản phẩm, tôi tìm cảm hứng ở những miền đất mới hoặc xả căng thẳng tại phòng tập yoga.

Chủ đề
AI Agents
Trí tuệ Nhân tạo

Xây dựng AI agent với các khoá học này:

Khóa học

Giới thiệu về AI Agents

1 giờ 30 phút
147.7K
Tìm hiểu các kiến thức cơ bản về AI agent, các thành phần của chúng và ứng dụng thực tế—không cần lập trình.
Xem chi tiếtRight Arrow
Bắt Đầu Khóa Học
Xem thêmRight Arrow
Liên quan

blog

Claude Opus 4.6: Tính năng, Điểm chuẩn, Bài kiểm tra thực hành và hơn thế nữa

Mô hình mới nhất của Anthropic dẫn đầu ở mã hóa tác tử và lập luận phức tạp. Thêm vào đó, nó có cửa sổ ngữ cảnh 1M.
Matt Crabtree's photo

Matt Crabtree

10 phút

Xem ThêmXem Thêm