Những mẫu hình khiến code backend thật sự sạch & dễ bảo trì — module, lỗi, API, cấu hình, class. AI viết; bạn đọc, đánh giá và dẫn dắt.
← / → để chuyển slide · chuyển EN/VI bên dưới
Cơ bản giúp script chạy được. Nâng cao giúp bạn làm việc trong một dự án thật — kỹ năng mà deck này xây dựng.
AI viết phần lớn code này rất trôi chảy. Việc của bạn là nhận ra mỗi khối đang làm gì và có đúng không.
Deck này là bản đồ về VÌ SAO những thứ này tồn tại và chúng nằm ở đâu. Cú pháp chính xác — bạn tra hoặc hỏi AI.
Mỗi phần đều nằm trong một dự án Flask thật: cấu hình, xử lý lỗi, gọi API, file, log, model. Không có gì trừu tượng.
Module & môi trường → lỗi → dữ liệu gọn & API → hạ tầng (cấu hình, file, log) → OOP → toàn cảnh.
Code được tổ chức ra sao — và dự án lấy gói (package) từ đâu — trước khi bất kỳ dòng nào chạy.
Chương trình lớn không nằm vừa trong một file. Module là cách Python chia việc thành những hộp có tên.
Module là hộp có tên chứa hàm, class và dữ liệu — json, os, requests, và cả file của chính bạn.
import json nạp module vào file của bạn; json.loads(...) là dùng nó. Một dòng = một hộp công cụ trên bàn.
Chỉ lấy phần cần dùng: from utils import clean_name — chỗ gọi ngắn và sạch hơn.
main.py + routes.py + models.py + utils.py. Import là cách các file này nói chuyện với nhau.
Ba kiểu: cả module, phần riêng, và module của chính dự án.
import json # standard library — ships with Python
import requests # installed with pip (see requirements.txt)
from utils import clean_name # a module in YOUR project
from models import User
print(json.__file__) # proof: every module is just a .py file
Thư viện chuẩn, gói từ pip, và file của bạn — tất cả đi qua cùng một cánh cửa.
Máy bạn có một Python. Dự án của bạn có một bản sao riêng — cô lập, có phiên bản, bỏ đi được.
python -m venv .venv tạo thư mục chứa python riêng + gói riêng. Không gì rò rỉ vào ra.
Dự án A cần requests 2.32, dự án B cần 2.28 — cả hai cài được, cạnh nhau, mỗi cái trong phòng riêng của mình.
venv + requirements.txt = cấu hình giống hệt trên laptop, server, và mọi sandbox AI.
source .venv/bin/activate — từ đó, pip chỉ cài vào DỰ ÁN NÀY. Hủy kích hoạt khi xong.
Mọi dự án Python đều bắt đầu bằng bốn lệnh này.
pip cài gói; requirements.txt là danh sách mua sắm khiến việc cài đặt lặp lại được.
pip install requests tải gói từ PyPI (chợ ứng dụng của Python) vào venv của bạn.
Mỗi gói kèm phiên bản chính xác. pip freeze > requirements.txt ghi danh sách những gì đang cài.
Máy mới: tạo venv → activate → pip install -r requirements.txt. Cùng dự án, cùng cách chạy.
Nói với AI bạn cần gì; AI sẽ thêm gói và cập nhật requirements.txt. Kiểm tra xem nó làm thật không.
Phần mềm thật gặp dữ liệu rác, file thiếu, mạng chậm. Sống sót có chủ đích.
Mọi thứ dưới đây đều sẽ xảy ra trong production. Câu hỏi là app của bạn làm gì khi chuyện đó xảy ra.
Người dùng gõ chữ vào ô số. File bị thiếu. API bị timeout. Đĩa đầy.
Ngoại lệ không bắt được giết chết cả tiến trình — mọi request sau đó cũng hỏng. Một dữ liệu rác là quật ngã cả site.
Bắt lỗi có thể dự đoán và lùi lại nhẹ nhàng: giá trị mặc định, thử lại, thông báo lỗi tử tế.
Ghi log sự bất ngờ, trả về 500 gọn gàng. Người dùng thấy yên tâm; bạn có đủ chi tiết để sửa.
Một kiểu, ba việc: thử phần rủi ro, phục hồi khi gặp lỗi đã biết, luôn dọn dẹp.
def load(path):
try:
with open(path) as f: # the risky part
return f.read()
except FileNotFoundError: # one known failure
print("no such file:", path)
return "" # graceful fallback
finally:
print("done") # ALWAYS runs
try = lần thử · except = phương án B cho một lỗi có tên · finally = dọn dẹp không thể bỏ qua.
Đôi khi cách đúng là từ chối: ném một ngoại lệ gọi tên vấn đề.
def set_age(user, value):
if not isinstance(value, int):
raise TypeError("age must be an int") # stop, with a reason
try:
set_age(user, "thirty") # boom: TypeError
except TypeError as e: # e carries the message
logging.warning("bad input: %s", e)
raise = lời 'không, và đây là lý do' có cấu trúc. Bắt nó ở cấp cao hơn vẫn giữ thông báo lỗi hữu ích.
Hai thói quen phân biệt code cẩn thận với code may rủi — và red flag cần nhận ra trong output của AI.
except FileNotFoundError: xử lý đúng trường hợp đó. except trống: nuốt hết mọi thứ — kể cả bug thật — và giấu chúng đi.
Nhận ra except trống trong code AI: thường là red flag, không phải điểm cộng.
Khi phát triển, để lỗi bất ngờ crash — traceback chỉ đúng chỗ. Khi chạy production, xử lý lỗi đã biết, ghi log lỗi bất ngờ.
Quy tắc ngón tay cái: xử lý đầu vào, ghi log sự bất ngờ.
Comprehension, JSON, và gọi API của người khác bằng requests.
Idiom 'nâng cao' phổ biến nhất. Nó xây list từ một tập dữ liệu — lọc + biến đổi.
prices = [1200, 45, 999, 12]
# the plain loop
over_100 = []
for p in prices:
if p > 100:
over_100.append(p)
# the same thing, one lineover_100 = [p for p in prices if p > 100] # [1200, 999]
Đọc từ phải sang trái: lấy từng p trong prices, giữ lại nếu p > 100, thu kết quả.
Cùng ý tưởng với dấu ngoặc nhọn: xây dict từ vòng lặp — cách hay dùng để tra theo id.
users = [{"id": 1, "name": "An"},
{"id": 2, "name": "Minh"},
{"id": 3, "name": "Linh"}]
# build a dict from a loop: id -> name
by_id = {u["id"]: u["name"] for u in users}
# {1: "An", 2: "Minh", 3: "Linh"}
Một dòng biến list bản ghi thành bảng tra dùng được ngay — bạn sẽ thấy nó ở khắp nơi.
Trình duyệt, API, CSDL và app Flask của bạn đều nói ngôn ngữ này. Trong Python, nó gần như miễn phí.
{"name": "An", "score": 9} và [1, 2, 3] — dict/list của bạn, viết thành text truyền được trên mạng.
Text JSON → dict/list Python. Đây là việc bạn làm với body của response API.
dict/list → text JSON, sẵn sàng gửi về trình duyệt hay một dịch vụ khác.
dict và list của Python CHÍNH là object và array của JSON. Không cần nhảy múa chuyển đổi — chỉ có ranh giới text.
Hai hàm, hai chiều — toàn bộ JSON bạn sẽ cần.
import json
text = '{"name": "An", "score": 9}' # JSON in (e.g. from an API)
data = json.loads(text) # text -> dict
print(data["score"]) # 9
out = json.dumps(data, indent=2) # dict -> text
print(out) # back to JSON, for the wire
loads = nạp (text vào), dumps = đổ ra (text ra). Tên hàm mô tả luôn chiều di chuyển.
App của bạn hiếm khi tự làm hết. Thư viện requests biến HTTP thành hai dòng.
Một lời gọi = một request HTTP. get = lấy, post = gửi/tạo. URL là đối số.
Giải mã body của response thành dict/list JSON ngay — không cần tự json.loads.
200 ổn, 404 thiếu, 500 lỗi phía họ. Luôn kiểm tra trước khi tin vào body của response.
Token xác thực và content type đi kèm: headers={"Authorization": ...} ở mỗi lời gọi có bảo vệ.
Đúng kiểu bạn sẽ thấy trong một nửa code backend của mọi dự án.
import requests
r = requests.get("https://api.example.com/users",
headers={"Authorization": "Bearer " + TOKEN})
if r.status_code == 200: # 2xx = success
users = r.json() # response text -> list of dicts
else:
logging.error("API error: %s", r.status_code)
Gọi → kiểm tra phán quyết → giải mã phần tải. Đó là toàn bộ quy trình.
Cấu hình nằm ngoài code, file tự dọn dẹp sau mình, và log thay cho print.
Cùng một code phải chạy trên laptop, staging và production — với các giá trị khác nhau.
URL CSDL, API key, token: đọc từ os.environ lúc chạy, không bao giờ viết cứng trong file .py.
Một codebase, ba môi trường với giá trị khác nhau — và bí mật không bao giờ nằm trong repo (hay lịch sử chat AI).
File text dạng KEY=VALUE nạp vào môi trường; nó nằm trong .gitignore và không bao giờ được commit.
.get("KEY") → None nếu thiếu; .get("KEY", mặc định) cho thiết lập tùy chọn. Key bắt buộc thì raise thật to.
Tùy chọn kèm mặc định, hoặc bắt buộc kèm crash rõ ràng — không bao giờ là bí mật im lặng.
import os
DB_URL = os.environ.get("DB_URL", "sqlite:///dev.db") # optional
SECRET = os.environ.get("SECRET") # required
if SECRET is None: # fail loudly, at startup
raise RuntimeError("SECRET not set")
App từ chối chạy nếu thiếu thứ cần thiết — thà crash khi khởi động còn hơn bí ẩn lúc 3 giờ sáng.
Tài nguyên (file, kết nối CSDL) là đi mượn. with cam kết chúng sẽ được trả lại.
Thứ nó mở sẽ được đóng khi khối kết thúc — kể cả khi lỗi xảy ra giữa chừng.
with open(path) as f: — không còn quên f.close(), không file ghi dở sau crash.
Cùng mẫu hình ở khắp nơi: kết nối phải được trả về, không thì rò rỉ, lần một lần hai, cho tới khi app chết.
with X as y: ... ở bất cứ đâu = 'y được mượn, và trả khi xong'. Bạn sẽ không bao giờ đọc sai nữa.
Đọc và ghi, hai dòng nghi thức, không lo dọn dẹp.
# 'with' = the file closes for you, even on error
with open("data.csv") as f: # read (default mode)
for line in f: # a file is a list of lines
print(line.strip())
with open("out.txt", "w") as f: # 'w' = write (erase first)
f.write("hello\n")
# <- already closed; no f.close() needed
w xóa trước, a ghi thêm, r đọc. with khiến việc đóng file thành tự động.
Thói quen 'chạy trên máy tôi' phổ biến nhất — và cách sửa.
Ổn khi phát triển: kiểm tra nhanh trên máy bạn. Nó in ra console rồi biến mất.
Không timestamp, không mức, không kiểm soát nơi nó đi. Trong production, output print thường bị mất hoàn toàn.
Các mức (DEBUG / INFO / WARNING / ERROR), timestamp, và ghi ra file hay dịch vụ log để tra lại sau.
Log là cuốn nhật ký còn lại: chuyện gì, khi nào, mức độ nào — nơi đầu tiên bạn xem khi có gì hỏng.
Đặt mức một lần; rồi app tự kể chuyện của mình.
import logging
logging.basicConfig(level=logging.INFO)
logging.info("server starting on port 9151")
logging.warning("low disk space")
logging.error("failed to reach API")
# 2026-10-27 00:36:59,123 INFO __main__: server starting...
Mỗi dòng mang timestamp và mức độ — tra được, sắp xếp được, lọc được. print không bao giờ làm được.
Class gói data + hành vi. Đủ để đọc một model — chứ không phải thiết kế framework.
Khái niệm 'lớn' duy nhất — và thực ra chỉ là một kiểu có tên cho một loại thứ.
Class định nghĩa một loại thứ: trường của nó (data) và phương thức (nó làm được gì). Một ý tưởng, một mái nhà.
User, Order, Invoice — dáng dấp của thế giới thật, mỗi thứ một class. Đó là 80% mục đích của OOP ở phía backend.
Chạy khi bạn tạo: User("An", "an@x.com") điền vào các trường. Bạn sẽ thấy nó trong mọi model.
Trong phương thức, self chỉ đến instance cụ thể: me.name đọc tên của ME, không phải của ai khác.
Mười dòng: data trong __init__, một hành vi, một instance. Đó là toàn bộ mẫu hình.
class User:
def __init__(self, name, email): # runs on User(...)
self.name = name # data on THIS user
self.email = email
def is_admin(self): # a method: behavior
return self.email.endswith("@corp.com")
me = User("An", "an@corp.com")
print(me.name, me.is_admin()) # An True
class = bản vẽ · User(...) = một thứ cụ thể · me.name / me.is_admin() = data và hành vi của nó.
Bạn không thiết kế framework — nhưng sẽ mở models.py mỗi ngày. Đây là cách lướt nhanh.
self.name, self.email: thứ đó biết gì. Các thuộc tính CHÍNH là schema — cho biết data nào tồn tại và dưới tên gì.
Lướt __init__ trước: nó liệt kê mọi trường theo thứ tự đọc.
user.is_admin(): thứ đó làm được gì — thường là thuộc tính cộng chút logic.
Thuộc tính + phương thức = toàn bộ hợp đồng của model. Giờ bạn đọc được mọi model AI viết.
Mỗi phần, nơi nó nằm trong một app Flask thật — và cách bạn đọc & mở rộng với AI.
Mở app của chính khóa học và mọi mẫu hình trong deck này đều có ở đó.
os.environ ở đầu app: bí mật & thiết lập nằm trong môi trường, không bao giờ trong code.
Mọi điểm rủi ro bọc trong try/except: đọc file, gọi API, đầu vào người dùng. App giảm năng lực — không chết.
Comprehension + json: đọc dữ liệu đi ngang, xếp lại thành dict & list, gửi về. requests lo chuyện nói chuyện với bên ngoài.
Class cho User / Order; with giữ file & kết nối trung thực; logging giữ cuốn nhật ký. Đó là toàn bộ lớp ứng dụng.
Tờ ghi nhớ để đọc mọi dự án backend.
| Phần | Nơi bạn sẽ thấy nó |
|---|---|
| import / module | Mọi file .py; main.py kéo routes, models, utils vào |
| venv + requirements.txt | Gốc dự án: thư mục .venv/; máy mới = activate + install -r |
| try / except / raise | Quanh đọc file, gọi API, đầu vào người dùng — app không được chết |
| comprehension | Xếp data trước khi nó rời đi: list cho bảng, dict cho tra cứu |
| json + requests | Nói chuyện với API bên ngoài: r.json() vào, json.dumps ra |
| os.environ | Cấu hình & bí mật: URL CSDL, API key — không bao giờ trong code |
| with / context manager | Đọc ghi file, mở kết nối CSDL |
| logging | Nhật ký của app: chuyện gì, khi nào, mức độ nào |
| class | Model: User, Order, Slide — data + hành vi cho mỗi thực thể |
Vòng lặp hằng ngày, giờ vốn từ đã lớn hơn.
Quét code của AI trước khi hỏi: import? try/except? with open? một class? Giờ bạn biết mỗi kiểu dùng để làm gì.
Dán khối code: 'giải thích đơn giản đoạn này.' So câu trả lời của AI với cách bạn hiểu — chỗ chênh là nơi bạn học.
Chỉ đích một dòng hoặc một hành vi. Diff nhỏ kiểm tra được; yêu cầu lớn che lỗi.
Output và log là sự thật. Hỏng gì? Dán traceback — giờ bạn biết lỗi thật nằm ở đâu.
AI có thể viết code. Người đọc, đánh giá và mở rộng được code đó mới là shipper.
Tiếp theo: Flask & HTTP — cùng bộ công cụ này, giờ trả lời request web. Trước đó: mở bất kỳ code nào và nhờ AI giải thích từng dòng.