Review checklist
นี่คือ checklist ที่ควรไล่ดู — ด้วยตา บน diff ไหนก็ได้ ของคุณเองหรือของ AI — หลังจากเครื่องมือใน Verification Tools รันแล้วสะอาด มันจงใจทำให้อยู่ในหนึ่งหน้ากระดาษพิมพ์: checklist ที่ไม่มีใครเปิดดูจริงตอนรีวิวไม่มีค่าอะไรเลย ทุกบรรทัดลิงก์ไปบทที่อธิบายว่า ทำไม และลิงก์ไปหมายเลข pattern ที่ตรงกันใน AI Bug Catalog ที่มันช่วยป้องกัน
Mutability
หัวข้อที่มีชื่อว่า “Mutability”- default argument ใช้ mutable literal (
= [],= {},= set()) แทนที่จะเป็น= Noneหรือเปล่า? Catalog #1 ดู Functions lambdaหรือ nested function ใน loop close over ตัวแปร loop เอง แทนที่จะเป็นค่าของมันในแต่ละรอบหรือเปล่า? Catalog #2 ดู Closures- function ที่ดูเหมือน read-only จริง ๆ mutate argument ที่เป็น
list/dictในที่เดิม (.sort(),.append(),x[i] = ...) โดยไม่บอกไว้เลยหรือเปล่า? Catalog #3 ดู Lists & Tuples [[...] * n] * m(หรือ container ของ container ที่ทำซ้ำด้วย*) สร้าง inner object ที่แชร์กันตัวเดียว แทนที่จะเป็นตัวอิสระต่อกันหรือเปล่า? Catalog #4 ดู Lists & Tuples- class attribute ที่ประกาศระดับ class (ไม่ใช่ใน
__init__) เป็น mutable object ที่ถูกแชร์ทุก instance หรือเปล่า? Catalog #15 ดู Classes
- มีการใช้
isในจุดที่ควรเป็น==— เทียบค่าแทนที่จะเช็ค singleton (None/True/False) หรือเปล่า? Catalog #5 ดู Variables & Types - มี bare
except:,except Exception: passหรือ (ใน async code)except BaseExceptionคั่นอยู่ระหว่างawaitกับ.cancel()ของ task หรือเปล่า? Catalog #6 ดู Exceptions - มี
raise eอยู่ในexcept ... as e:ที่ตั้งใจใช้ bareraiseหรือเปล่า? Catalog #7 ดู Exceptions assertvalidate external input (argument, user input, API response) แทนที่จะเป็น internal invariant หรือเปล่า? มันหายไปภายใต้-OCatalog #8 ดู Exceptions
Async & concurrency
หัวข้อที่มีชื่อว่า “Async & concurrency”- ทุก call ที่คืน coroutine ถูก
awaitจริงหรือถูกส่งให้asyncio.create_task/asyncio.gatherหรือเปล่า? Catalog #9 ดู asyncio basics - ผลลัพธ์ของ
asyncio.create_task(...)ทุกตัวถูกเก็บไว้ที่ไหนสักแห่งที่มี lifetime ยาวกว่า function ที่สร้างมันหรือเปล่า? Catalog #10 ดู Tasks & gather - มี
async deffunction ตัวไหนเรียก blocking function (time.sleep, synchronous HTTP client, blocking file I/O) แทนที่จะเป็นเวอร์ชันawait-based ของมันหรือเปล่า? Catalog #11 ดู GIL & threads - shared mutable state ถูกอ่านแล้วเขียนคาบเกี่ยว
awaitโดยไม่มีasyncio.Lockหรือเปล่า? Catalog #12 ดู Tasks & gather - ทุกการเรียก
multiprocessing.Pool/Processอยู่หลังif __name__ == "__main__":หรือเปล่า? Catalog #13 ดู multiprocessing
- class นิยาม
__eq__โดยไม่นิยาม__hash__ด้วย (หรือยอมรับโดยตั้งใจว่า instance จะ unhashable) หรือเปล่า? Catalog #14 ดู Dunder methods - มีการใช้
Anyในจุดที่มี type จริงให้ใช้ได้ หรือมี# type: ignoreที่ไม่มี error code เฉพาะเจาะจงหรือเปล่า? Catalog #16 ดู Type hints - parameter หรือ return value ที่เป็น
X | None(หรือOptional[X]) ถูกใช้โดยไม่มีการเช็คNoneก่อนหรือเปล่า? Catalog #17 ดู Type hints list[Subtype]ถูกส่งไปในจุดที่คาดหวังlist[Supertype]หรือเปล่า — และSequence/Iterable(covariant) ควรเป็น parameter type ที่ถูกต้องแทนlistหรือเปล่า? Catalog #18 ดู Type hints
Resources & security
หัวข้อที่มีชื่อว่า “Resources & security”- โค้ดเรียก
datetime.utcnow()(deprecated, naive) แทนที่จะเป็นdatetime.now(timezone.utc)หรือเปล่า? Catalog #19 ดู stdlib tour - ทุกการเรียก
open()อยู่ในwithblock และส่งencoding=อย่างชัดเจนหรือเปล่า? Catalog #20 ดู stdlib tour - มีการเรียก
subprocessตัวไหนใช้shell=Trueกับ string ที่สร้างจาก external input หรือเปล่า? Catalog #21 ดู stdlib tour - ทุก
@patch("...")target string ตรงกับที่โค้ดที่ถูกทดสอบ import ชื่อนั้นมาจริง ๆ หรือเปล่า — module ที่เรียกใช้ ไม่ใช่ module ที่นิยาม? Catalog #22 ดู pytest - ทุก dependency ถูก pin หรือเปล่า และ
pip-audit/เครื่องมือ audit ของuvถูกรันกับ lockfile เมื่อไม่นานมานี้หรือเปล่า? มีการติดตั้งด้วยsudo pipที่ไหนหรือเปล่า? Catalog #23 ดู venv, pip & uv
Tooling & style
หัวข้อที่มีชื่อว่า “Tooling & style”- โค้ดที่เป็น library เรียก
print()ในจุดที่ควรเป็นlogging— อะไรก็ตามที่ผู้เรียกต้อง filter, redirect หรือปิดเสียงได้ — หรือเปล่า? Catalog #24 ดู Logging
🐛 หา bug ในโค้ดนี้
def get_config(overrides: dict | None = None) -> dict: config = {"retries": 3, "timeout": 10} if overrides: config |= overrides return config
base = get_config()custom = get_config({"retries": 5})print(base)print(custom)เฉลย
โค้ดนี้ถูกต้องอยู่แล้ว — มันอยู่ตรงนี้เพื่อทดสอบว่าคุณไล่ checklist แบบท่องจำหรืออ่านโค้ดจริง overrides: dict | None = None ใช้ None เป็น default ไม่ใช่ mutable literal ดังนั้น catalog #1 ไม่เกี่ยวข้อง: config = {...} เป็น dict ใหม่ที่ถูกสร้างข้างใน function body ทุกครั้งที่เรียก และ config |= overrides merge เข้าไปใน dict ใหม่นั้น ไม่ใช่เข้าไปในอะไรที่แชร์ข้าม call การไล่ checklist ควรบอกคุณว่า ต้องดูตรงไหน ไม่ใช่ยัด bug ที่ไม่มีจริงให้คุณ — base พิมพ์ {'retries': 3, 'timeout': 10} และ custom พิมพ์ {'retries': 5, 'timeout': 10} อย่างถูกต้อง แยกจากกัน
เวอร์ชันสั้นแบบ copy-paste ได้
หัวข้อที่มีชื่อว่า “เวอร์ชันสั้นแบบ copy-paste ได้”Mutability: mutable default arg? loop-var closure? mutates caller's list? shared [[...]]*n rows? shared class attr?Errors: is vs ==? bare/broad except (incl. BaseException across await)? raise e vs raise? assert validating input?Async: every coroutine awaited/tasked? task reference kept? blocking call inside async def? shared state without Lock? Pool guarded by __main__?Typing: __eq__ without __hash__? bare Any / uncoded type: ignore? Optional narrowed before use? list invariance respected?Resources: utcnow()/naive datetime? open() with encoding=? subprocess shell=True? patch target matches import site? deps pinned + audited?Tooling: print() instead of logging in library code?