→ From Coder to Shipper
From Coder to Shipper · Nền tảng backend

Python — vượt qua những điều cơ bản

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

Toàn cảnh

Từ 'nó chạy không?' sang 'bạn đọc được nó không?'

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.

Bạn đọc, AI viết

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.

Mẫu hình, không phải cú pháp

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.

Tất cả là một ứng dụng

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.

Bản đồ

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.

01
Phần 1

Module & môi trường

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.

Phần 1 · module

Một file là một module

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.

Mọi file .py là một module

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 kéo nó vào

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.

from ... import ...

Chỉ lấy phần cần dùng: from utils import clean_name — chỗ gọi ngắn và sạch hơn.

Dự án của bạn là các module

main.py + routes.py + models.py + utils.py. Import là cách các file này nói chuyện với nhau.

Phần 1 · module

import trong một file thật

Ba kiểu: cả module, phần riêng, và module của chính dự án.

app/main.py
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.

Phần 1 · môi trường

venv — một dự án, một Python

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.

Căn phòng riêng cho dự án

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.

Không còn xung đột

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.

Tái lập được theo thiết kế

venv + requirements.txt = cấu hình giống hệt trên laptop, server, và mọi sandbox AI.

Bạn kích hoạt nó

source .venv/bin/activate — từ đó, pip chỉ cài vào DỰ ÁN NÀY. Hủy kích hoạt khi xong.

Phần 1 · môi trường

venv trên thực tế

Mọi dự án Python đều bắt đầu bằng bốn lệnh này.

ops@vps — zsh
$ python -m venv .venv
$ source .venv/bin/activate
(venv) $ pip install requests
Collecting requests
Successfully installed requests-2.32.3
(venv) $ pip freeze > requirements.txt
(venv) $ pip install -r requirements.txt # rebuild on another machine
(venv) $ deactivate
Phần 1 · pip

pip & requirements.txt

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 = trình cài gói

pip install requests tải gói từ PyPI (chợ ứng dụng của Python) vào venv của bạn.

requirements.txt = danh sách mua sắm

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.

Xây lại ở bất cứ đâu

Máy mới: tạo venv → activate → pip install -r requirements.txt. Cùng dự án, cùng cách chạy.

AI cũng dùng nó

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.

02
Phần 2

Xử lý lỗi

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.

Phần 2 · vì sao

Thế giới không đáng tin

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.

Nó sẽ xảy ra

Người dùng gõ chữ vào ô số. File bị thiếu. API bị timeout. Đĩa đầy.

Không xử lý = chết

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.

Xử lý những gì bạn dự đoán được

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ế.

Báo cáo những gì không xử lý được

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.

Phần 2 · kiểu

try / except / finally

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.

app/files.py
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.

Phần 2 · ném lỗi

raise — dừng lại kèm lý do

Đôi khi cách đúng là từ chối: ném một ngoại lệ gọi tên vấn đề.

app/guard.py
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.

Phần 2 · phán đoán

Bắt cụ thể; crash có chủ đí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.

Bắt cụ thể, không bắt tất cả

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 nào xử lý vs để nó crash

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ờ.

03
Phần 3

Dữ liệu gọn & thế giới bên ngoài

Comprehension, JSON, và gọi API của người khác bằng requests.

Phần 3 · comprehension

List comprehension — vòng lặp một dòng

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.

app/comps.py
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ả.

Phần 3 · comprehension

Dict comprehension — xây bảng tra

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.

app/comps2.py
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.

Phần 3 · JSON

JSON — định dạng truyền dẫn của web

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í.

JSON = text cho dict & list

{"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.

json.loads — đi vào

Text JSON → dict/list Python. Đây là việc bạn làm với body của response API.

json.dumps — đi ra

dict/list → text JSON, sẵn sàng gửi về trình duyệt hay một dịch vụ khác.

Kiểu dữ liệu của bạn đã biết nói

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.

Phần 3 · JSON

json.loads / json.dumps

Hai hàm, hai chiều — toàn bộ JSON bạn sẽ cần.

app/jsondemo.py
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.

Phần 3 · API

Gọi API của người khác

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.

requests.get / requests.post

Một lời gọi = một request HTTP. get = lấy, post = gửi/tạo. URL là đối số.

r.json() — phần tải

Giải mã body của response thành dict/list JSON ngay — không cần tự json.loads.

status_code — phán quyết

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.

headers — phong bì

Token xác thực và content type đi kèm: headers={"Authorization": ...} ở mỗi lời gọi có bảo vệ.

Part 3 · APIs

Một lời gọi API, đầu cuối

Đúng kiểu bạn sẽ thấy trong một nửa code backend của mọi dự án.

app/api.py
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.

04
Phần 4

Hạ tầng của ứng dụng thật

Cấu hình nằm ngoài code, file tự dọn dẹp sau mình, và log thay cho print.

Phần 4 · cấu hình

Biến môi trường — đưa bí mật ra khỏi code

Cùng một code phải chạy trên laptop, staging và production — với các giá trị khác nhau.

Cấu hình sống ngoài code

URL CSDL, API key, token: đọc từ os.environ lúc chạy, không bao giờ viết cứng trong file .py.

Vì sao quan trọng

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 .env

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.

os.environ.get

.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.

Phần 4 · cấu hình

Đọc cấu hình từ môi trường

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.

app/config.py
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.

Phần 4 · context manager

with — dọn dẹp được đảm bảo

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.

with = luôn được trả

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.

File

with open(path) as f: — không còn quên f.close(), không file ghi dở sau crash.

CSDL & socket

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.

Nhận ra kiểu

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.

Phần 4 · file

Đọc ghi file bằng with

Đọc và ghi, hai dòng nghi thức, không lo dọn dẹp.

app/files.py
# '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.

Phần 4 · log

print là cho bạn; logging là cho app

Thói quen 'chạy trên máy tôi' phổ biến nhất — và cách sửa.

print — cho bạn, ngay bây giờ

Ổ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.

logging — cho app, luôn luô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.

Phần 4 · log

logging trong năm dòng

Đặt mức một lần; rồi app tự kể chuyện của mình.

app/logdemo.py
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.

05
Phần 5

OOP — nhẹ nhàng

Class gói data + hành vi. Đủ để đọc một model — chứ không phải thiết kế framework.

Phần 5 · class

Class gói data + hành vi

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ứ.

Data + hành vi, một hộp

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à.

Mô hình hóa thực thể thật

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.

__init__ — hàm tạo

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.

self — cái này cụ thể

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.

Phần 5 · class

Class hữu ích nhỏ nhất

Mười dòng: data trong __init__, một hành vi, một instance. Đó là toàn bộ mẫu hình.

app/models.py
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ó.

Phần 5 · đọc model

Đọc một model trong 30 giây

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.

Thuộc tính = dữ liệu

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.

Phương thức = hành vi

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.

06
Phần 6

Toàn cảnh

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.

Phần 6 · bộ công cụ

Đây là cách một app Flask thật vẫn sống

Mở app của chính khóa học và mọi mẫu hình trong deck này đều có ở đó.

Cấu hình từ bên ngoài

os.environ ở đầu app: bí mật & thiết lập nằm trong môi trường, không bao giờ trong code.

Lỗi bị giữ trong tầm

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.

Dữ liệu được xếp gọn

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.

Model phản chiếu thực tế

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.

Phần 6 · bản đồ

Mỗi phần xuất hiện ở đâu

Tờ ghi nhớ để đọc mọi dự án backend.

PhầnNơi bạn sẽ thấy nó
import / moduleMọi file .py; main.py kéo routes, models, utils vào
venv + requirements.txtGốc dự án: thư mục .venv/; máy mới = activate + install -r
try / except / raiseQuanh đọc file, gọi API, đầu vào người dùng — app không được chết
comprehensionXếp data trước khi nó rời đi: list cho bảng, dict cho tra cứu
json + requestsNói chuyện với API bên ngoài: r.json() vào, json.dumps ra
os.environCấ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
loggingNhật ký của app: chuyện gì, khi nào, mức độ nào
classModel: User, Order, Slide — data + hành vi cho mỗi thực thể
Phần 6 · quy trình

Đọc & mở rộng code với AI

Vòng lặp hằng ngày, giờ vốn từ đã lớn hơn.

  1. 1

    Nhận ra mẫu hình

    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ì.

  2. 2

    Hỏi nó đang 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.

  3. 3

    Mỗi lần đổi một thứ

    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.

  4. 4

    Chạy, đọc output

    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.
— khóa học

Module. Lỗi. JSON. Cấu hình. Class. Đó là Python backend thực chiến.

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.