ousterhout-quality-program

โดย Rob Zappยังไม่มีการติดตั้งยังไม่มีการถูกใจอัปเดตเมื่อ 21 กันยายน 2569หมวดหมู่: รีวิวโค้ด

ทำอะไรได้บ้าง

ใช้เมื่อใดก็ตามที่โค้ดที่เขียนหรือทบทวนสร้างหรือเปลี่ยนแปลงขอบเขต — โมดูลใหม่, คลาส, คอมโพเนนต์, ตัวช่วย, ฮุก, เซอร์วิส หรือวอปเปอร์; การสกัดหรือรวมโค้ดที่ใช้ร่วมกัน; ช่วงเวลาที่คิดว่า "มาทำให้ใช้ซ้ำได้" — และเมื่อทำการทบทวน, รีแฟกเตอร์ หรือออกแบบโมดูลอย่างชัดเจน ตัดสินว่าแอบสแตรกชันนั้นคุ้มค่าหรือไม่: ความลึกของโมดูล, ว่าควรซ่อนการตัดสินใจออกแบบหรือไม่, ว่าโค้ดที่ซ้ำกันนั้นปกป้อง invariant ที่ใช้ร่วมกันหรือแค่คล้ายกัน, ว่าอินเทอร์เฟซนั้นเสถียรหรือไม่ ป้องกันการใช้ SOLID/Clean Code แบบกลไกที่สร้างคลาสตื้นๆ จำนวนมาก นอกจากนี้ยังนิยามการทดสอบค่าใช้จ่ายของผู้อ่าน (โค้ดที่อ่านและแก้ไขได้ง่ายสำหรับมนุษย์และเอเจนต์) และขั้นตอนการรีแฟกเตอร์ฐานโค้ดที่มีอยู่ให้เป็นไปตามมาตรฐานนี้

การติดตั้งจะเปิดรายการนี้ในแอปเดสก์ท็อป AgentsRoom ของคุณ หากยังไม่ได้ติดตั้งแอป ระบบจะพาคุณไปยังหน้าดาวน์โหลด

SKILL.md

---
name: ousterhout-quality-program
description: ใช้เมื่อใดก็ตามที่โค้ดที่เขียนหรือทบทวนสร้างหรือเปลี่ยนแปลงขอบเขต — โมดูลใหม่, คลาส, คอมโพเนนต์, ตัวช่วย, ฮุก, เซอร์วิส หรือวอปเปอร์; การสกัดหรือรวมโค้ดที่ใช้ร่วมกัน; ช่วงเวลาที่คิดว่า "มาทำให้ใช้ซ้ำได้" — และเมื่อทำการทบทวน, รีแฟกเตอร์ หรือออกแบบโมดูลอย่างชัดเจน ตัดสินว่าแอบสแตรกชันนั้นคุ้มค่าหรือไม่: ความลึกของโมดูล, ว่าควรซ่อนการตัดสินใจออกแบบหรือไม่, ว่าโค้ดที่ซ้ำกันนั้นปกป้อง invariant ที่ใช้ร่วมกันหรือแค่คล้ายกัน, ว่าอินเทอร์เฟซนั้นเสถียรหรือไม่ ป้องกันการใช้ SOLID/Clean Code แบบกลไกที่สร้างคลาสตื้นๆ จำนวนมาก นอกจากนี้ยังนิยามการทดสอบค่าใช้จ่ายของผู้อ่าน (โค้ดที่อ่านและแก้ไขได้ง่ายสำหรับมนุษย์และเอเจนต์) และขั้นตอนการรีแฟกเตอร์ฐานโค้ดที่มีอยู่ให้เป็นไปตามมาตรฐานนี้
---

# Ousterhout Quality Program

## ภาพรวม

งานของโมดูลคือการซ่อนความซับซ้อนไว้เบื้องหลังอินเทอร์เฟซขนาดเล็ก ตัวชี้วัดหลักคือ **ความลึก**: โมดูลที่ลึกจะมีอินเทอร์เฟซที่เรียบง่ายเหนือฟังก์ชันการทำงานที่มีนัยสำคัญ; โมดูลตื้นอินเทอร์เฟซจะซับซ้อนเกือบเท่ากับการใช้งานจริง ดังนั้นจึงไม่คุ้มค่า ความซับซ้อนคือสิ่งที่คุณรู้สึกเมื่อการเปลี่ยนแปลงบังคับให้คุณต้องเข้าใจหรือแตะต้องโค้ดที่คุณไม่คาดคิด — Ousterhout ระบุแหล่งที่มาสองอย่าง: **dependencies** (คุณไม่สามารถเปลี่ยน A โดยไม่เปลี่ยน B) และ **obscurity** (ข้อมูลสำคัญไม่ชัดเจน)

Ousterhout เพียงอย่างเดียวบอกคุณว่าโมดูลที่ดี *รู้สึก* อย่างไร มันจะแข็งแกร่งที่สุดเมื่อรวมกับเลนส์อื่นๆ ที่บอกคุณว่าขอบเขตควรอยู่ที่ไหนและวิธีเคลื่อนที่ไปอย่างปลอดภัย ทักษะนี้คือเลนส์ที่รวมกันนั้น

## จุดที่การรีวิวผิดพลาดจริงๆ

ความล้มเหลวสองประการที่ทักษะนี้มีไว้แก้ไข — ที่พบซ้ำๆ ในโค้ดที่เขียนโดยเอเจนต์ — อยู่ที่ **การแก้ไข** ไม่ใช่คำตัดสินแยก/ไม่แยก:

1. **การแก้ไขตื้นๆ** เมื่อเจอการแปลง `as unknown as` หกครั้ง ผู้รีวิวที่ไม่มีเครื่องมือช่วยจะรวบรวมเป็น helper ทั่วไป `castRows<T>()` — ดูเรียบร้อยขึ้น แต่ความคลุมเครือยังคงอยู่ การแก้ไขที่ลึกคือการแมปแถว→โดเมนที่มีการพิมพ์และมีการทดสอบที่กำหนดไว้ก่อน (ใช้หลัก Parnas: การแปลงเป็นกลิ่นของขอบเขตที่ขาดหาย; Beck: พิสูจน์การแมปก่อนย้าย) การทำให้กลิ่นดูดีขึ้นไม่ใช่การกำจัดมัน
2. **การแยกแบบสะท้อนกลับ** เมื่อมีตรรกะการอัปเดตซ้ำในสามคอมโพเนนต์พี่น้อง ผู้รีวิวทุกคนที่ไม่มีเครื่องมือช่วยพูดว่า "แยก helper ร่วม" — สะท้อน DRY กฎของโปรแกรมนี้ ขยาย Metz: รอ invariant ไม่ใช่ครั้งที่สามที่เหมือนกัน — รวมศูนย์เมื่อโค้ดปกป้องกฎร่วม ไม่ใช่แค่เมื่อมันคล้องจอง

เมื่อคุณแนะนำการแก้ไข ให้ตรวจสอบทั้งสองข้อ: มันกำจัดความคลุมเครือหรือแค่ย้ายที่ และการแยกนั้นปกป้อง invariant หรือแค่ลดการทำซ้ำรูปแบบ?

## ประตูสัดส่วน

ข้ามเลนส์เมื่อการเปลี่ยนแปลงไม่เพิ่มชื่อที่ส่งออก/นำเข้าใหม่, ไม่สร้างโมดูล/คลาส/คอมโพเนนต์/helper/hook/service/wrapper ใหม่ และไม่รวมศูนย์อะไรเลย การเปลี่ยนชื่อบริสุทธิ์, codemod แบบกลไก, แก้ไข config/data และการแก้ไขบรรทัดเดียวได้รับการยกเว้น เมื่อสงสัย ให้รันแค่สองการทดสอบหลัก (ความลึก, invariant) แล้วหยุด

## กฎเต็มรูปแบบ

โค้ดที่สร้างหรือรีวิวทุกชิ้นต้องผ่านเลนส์ Ousterhout ก่อนงานจะถือว่าสำเร็จ — ไม่ใช่แค่การรีวิวออกแบบอย่างชัดเจน — ยกเว้นการเปลี่ยนแปลงที่อยู่ต่ำกว่าประตูสัดส่วน (ไม่มีขอบเขตใหม่, ไม่มีการรวมศูนย์: การเปลี่ยนชื่อ, codemod, แก้ไข config) สองการทดสอบ: (1) **ความลึก** — อินเทอร์เฟซใหม่ต้องซ่อนมากกว่าที่เปิดเผยอย่างมีนัยสำคัญ; อินเทอร์เฟซที่ซับซ้อนเท่ากับสิ่งที่มันห่อหุ้มไม่คุ้มค่า (2) **Invariant** — แยกโค้ดร่วมเฉพาะเมื่อมันปกป้องกฎร่วม ไม่ใช่เพราะสามที่เหมือนกัน; และการแก้ไขต้องกำจัดความคลุมเครือ ไม่ใช่แค่ย้ายมัน (รวมศูนย์การแปลงหกครั้งเป็น helper เดียวยังคงเป็นหกการแปลง) เมื่อการเปลี่ยนแปลงสร้างหรือปรับรูปร่างขอบเขต ให้หาวิธีที่ผลิตภัณฑ์ที่มีอยู่แก้ปัญหารูปแบบและขนาดนี้และนำแนวทางของมันมาใช้ เว้นแต่จะมีเหตุผลที่ระบุไว้ไม่ให้ใช้ (รูปแบบที่จำได้จากการฝึกอบรมคือคำกล่าวอ้าง ไม่ใช่แหล่งที่มา) จากนั้นรันการตรวจสอบด้านล่าง

## เมื่อใดควรใช้

- ตัดสินใจว่าคลาส/ฟังก์ชัน/hook ใหม่คุ้มกับอินเทอร์เฟซของมันหรือเป็นแค่การส่งผ่านตื้นๆ
- ไฟล์ข้ามเกณฑ์ขนาดและคุณกำลังตัดสินใจ *วิธี* แยกมัน ไม่ใช่แค่ควรแยก
- โค้ดซ้ำๆ ล่อให้คุณแยก helper ร่วม
- ออกแบบหรือรีวิวขอบเขตรอบกฎธุรกิจ (การตรวจสอบขอบเขตการอนุญาต, กฎเงิน/การปัดเศษ, การป้องกันการเปลี่ยนสถานะของเครื่องจักรสถานะ, กฎการเก็บข้อมูล)
- อินเทอร์เฟซกำลังจะเพิ่มพารามิเตอร์หรือกรณีพิเศษ
- นำฐานโค้ดที่มีอยู่มาใช้ตามมาตรฐานนี้ — ดู "Refactoring an Existing Codebase to This Standard" ด้านล่าง

**ไม่เหมาะสำหรับ:** การแก้ไขกลไกเล็กน้อย หรือเมื่อมีข้อกำหนดโครงสร้างของโครงการอยู่แล้ว — ดูประตูสัดส่วนด้านบน ให้เลื่อนออกไปใช้ `karpathy-guidelines` สำหรับวินัยการเปลี่ยนแปลงแบบผ่าตัดและทักษะการพัฒนาที่ขับเคลื่อนด้วยการทดสอบสำหรับตาข่ายความปลอดภัยของการรีแฟกเตอร์ เมื่อมีให้ใช้

## เลนส์

แต่ละเลนส์เพิ่มคำถามเพียงข้อเดียว Ousterhout เป็นกระดูกสันหลัง; อื่นๆ แก้ไขจุดบอดของมัน

| เลนส์ | คำถามเดียวที่เพิ่มเข้ามา | เมื่อใดที่เลนส์นี้มีอำนาจเหนือกว่า |
|---|---|---|
| **Ousterhout** — โมดูลลึก | อินเทอร์เฟซนี้ซ่อนอะไรไว้มากกว่าที่เปิดเผยหรือไม่? | กระดูกสันหลังเริ่มต้น |
| **Parnas** — การซ่อนข้อมูล | การตัดสินใจออกแบบใด (ที่มีแนวโน้มจะเปลี่ยนแปลง) ที่โมดูลนี้ซ่อนไว้? | *เหตุผล* ที่โมดูลควรลึก ถ้าไม่ซ่อนอะไรที่เปลี่ยนแปลงได้ ความลึกก็เป็นแค่ความสวยงาม |
| **Brooks** — สิ่งจำเป็นกับสิ่งบังเอิญ | สิ่งนี้ช่วยลดความซับซ้อนที่ไม่จำเป็น หรือแค่ย้ายความซับซ้อนที่จำเป็นในโดเมนไปที่อื่น? | หยุด "รีแฟกเตอร์" ที่แค่ย้ายความยุ่งเหยิงโดยไม่ลดขนาด |
| **Evans** — Domain-Driven Design | ขอบเขตนี้ถูกตั้งชื่อด้วยภาษาของโดเมน ไม่ใช่ภาษาทั่วไปหรือไม่? | เปลี่ยนชื่อ `utils`/`helpers` — ตั้งชื่อขอบเขตตาม invariant ที่รีโปนี้มีจริง |
| **Fowler** — รีแฟกเตอร์ / กลิ่นโค้ด | ขั้นตอนเล็กที่สุดที่ปลอดภัยในการก้าวสู่การออกแบบที่ลึกขึ้นคืออะไร? | เปลี่ยน "ควรจะลึกขึ้น" ให้เป็นขั้นตอนที่ชัดเจนหลังจากผ่านการทดสอบ |
| **Beck** — การออกแบบเรียบง่าย, ทดสอบก่อน | ฉันพิสูจน์พฤติกรรมปัจจุบันก่อนที่จะทำให้รอยต่อลึกขึ้นหรือยัง? | เป็นเบรกสำหรับสถาปัตยกรรมที่เกิดก่อนเวลา ทำให้มันทำงานและผ่านการทดสอบก่อน แล้วค่อยลึกลงในรอยต่อที่ถูกต้อง |
| **Hickey** — เรียบง่ายกับง่าย | สิ่งนี้ผสมผสานแนวคิดที่ไม่เกี่ยวข้องกัน หรือเป็นแนวคิดเดียวจริงๆ? | ผู้ช่วยตื้นมักจะ *ง่าย* (ใกล้เคียง, เร็ว) ไม่ใช่ *เรียบง่าย* (มีแนวคิดผสมผสานน้อย) ให้เลือกเรียบง่าย |
| **Metz** — การทำซ้ำดีกว่าการสกัดที่ผิด | โค้ดที่ทำซ้ำนี้ปกป้อง invariant ร่วม หรือแค่ดูเหมือนกัน (กฎของโปรแกรมนี้ ขยายจาก Metz)? | Metz: การทำซ้ำถูกกว่าการสกัดที่ผิด — ใส่สกัดที่ผิดกลับเข้าไปแทนที่จะบิดมัน โปรแกรมนี้ขยายความ: อย่า *รวมศูนย์* เพราะมันทำซ้ำ; รวมศูนย์เฉพาะเมื่อมันปกป้อง invariant จริง ทนต่อการทำซ้ำจนกว่า invariant จะปรากฏตัว |
| **กฎของ Hyrum** — พฤติกรรมที่สังเกตได้ | ผู้เรียกจะพึ่งพาพฤติกรรมที่เกินกว่าสัญญาของอินเทอร์เฟซนี้หรือไม่? | สนับสนุนอินเทอร์เฟซขนาดเล็กและเสถียร: ทุกพฤติกรรมที่สังเกตได้ในที่สุดจะกลายเป็นสิ่งที่ต้องพึ่งพา |

## สูตรการผสมผสาน

ใช้ตามลำดับนี้ — เลนส์ที่ตามมาจะมีความสำคัญก็ต่อเมื่อเลนส์ก่อนหน้านี้ผ่าน:

1. **Metz — ประตูรับเข้า.** ขอบเขต/การสกัดนี้สมควรมีอยู่จริงหรือไม่? กฎของโปรแกรมนี้ ขยายจาก Metz: สกัดเฉพาะเมื่อโค้ดปกป้องกฎร่วม — สามตัวที่ดูเหมือนกันไม่ใช่ invariant ที่เปิดเผย ถ้าไม่ใช่ หยุดที่นี่
2. **Parnas / Ousterhout** — ซ่อนการตัดสินใจที่เปลี่ยนแปลงได้ (ขอบเขตการอนุญาต, กฎการปัดเศษ, การป้องกันการเปลี่ยนสถานะ, กฎการเก็บรักษา) ไว้หลังโมดูลลึก
3. **Evans** — ตั้งชื่อโมดูลนั้นด้วยภาษาของโดเมน ไม่ใช่ `utils`
4. **Beck / Fowler** — สำหรับโค้ดที่มีอยู่ ยึดพฤติกรรมปัจจุบันด้วยการทดสอบ แล้วรีแฟกเตอร์ไปในทิศทางนั้นด้วยขั้นตอนเล็กๆ ที่ปลอดภัย สำหรับโค้ดที่สร้างใหม่ไม่มีพฤติกรรมปัจจุบันให้ยึด — เขียนการทดสอบที่กำหนดพฤติกรรมที่ตั้งใจแทน
5. **Hickey** — ปฏิเสธอินเทอร์เฟซที่ผสมผสานแนวคิดที่ไม่เกี่ยวข้องกันเพียงเพราะเวิร์กโฟลว์ดูคล้ายกัน

## รูปแบบโครงสร้างที่ไม่ดี

**SOLID / Clean Code แบบกลไกผลิตโมดูลตื้นๆ.** การอ่านแบบเคร่งครัด — หนึ่งคลาสต่อความรับผิดชอบ, สกัดทุกฟังก์ชัน, ทำให้ทุกอย่างเล็ก — ทำให้เกิดกลุ่มคลาสที่อินเทอร์เฟซซับซ้อนเท่ากับเนื้อใน เมื่อกฎบอกว่า "แยกสิ" ถามว่าการแยกนั้นซ่อน *การตัดสินใจ* อะไร (Parnas) และซ่อนมากกว่าที่เปิดเผยหรือไม่ (Ousterhout) ถ้าไม่ซ่อนอะไรที่เปลี่ยนแปลงได้ อย่าแยก การป้องกันนี้สำคัญที่สุดเมื่อมีแรงกดดันให้รีแฟกเตอร์ ("ทำความสะอาด", "ไฟล์นี้ใหญ่เกินไป") — ในการวิเคราะห์อย่างใจเย็น ผู้ตรวจสอบมักต่อต้านมัน; ระหว่างรีแฟกเตอร์ที่มีคำสั่งให้เปลี่ยนแปลงที่มองเห็นได้ คือเวลาที่ไฟล์ตื้นๆ จำนวนมากถูกเขียนขึ้น

## ความผิดพลาดทั่วไป

- **แยกตามขนาดอย่างเดียว.** โมดูลคิวรี 400 บรรทัดที่ซ่อนการตัดสินใจที่สอดคล้องกันหนึ่งอย่าง อาจลึกกว่าสี่โมดูล 100 บรรทัดที่แต่ละอันรั่วไหลการเชื่อมต่อเดียวกัน
- **ตั้งชื่อแยกเป็น `helpers`/`utils`.** ถ้าคุณตั้งชื่อด้วยภาษาของโดเมนไม่ได้ (Evans) ขอบเขตนั้นน่าจะผิด
- **สกัดเมื่อเจอครั้งที่สอง.** กฎของโปรแกรมนี้ ขยายจาก Metz: รอ invariant ไม่ใช่ตัวที่สามที่ดูเหมือนกัน
- **ลึกก่อนยึดพฤติกรรม.** Beck: ถ้าไม่มีการทดสอบพิสูจน์พฤติกรรมปัจจุบัน รีแฟกเตอร์ "ลึกขึ้น" คือการเขียนใหม่
- **นับการส่งผ่านเป็นโมดูล.** ตัวห่อที่ส่งผ่านอาร์กิวเมนต์เพิ่มอินเทอร์เฟซแต่ไม่ซ่อนอะไร — ตื้นโดยนิยาม
- **เข้าใจผิดว่าเสียงสัมผัสเป็น invariant.** หลักฐานที่ดีที่สุดของ invariant ร่วมคือการเปลี่ยนแปลงร่วม: สำเนาถูกแก้ไขหรือเปลี่ยนพร้อมกันในประวัติ (บั๊กเดียวกันแก้สองที่) สิ่งที่ดูเหมือนกันแต่เปลี่ยนแปลงแยกกันคือเสียงสัมผัส; ปล่อยให้ทำซ้ำ
- **ทำความสะอาดกลิ่นแทนที่จะลบมัน.** การรวมการแปลงหกครั้งเป็นผู้ช่วยแปลงทั่วไปคือเวอร์ชันที่เรียบร้อยของความคลุมเครือเดียวกัน การแก้ไขลึกคือตั้งชื่อขอบเขตที่การแปลงนั้นปกปิด

## ค่าใช้จ่ายของผู้อ่าน: การทดสอบที่สาม

ความลึกและ invariant ตัดสินว่าควรมีขอบเขตหรือไม่ ค่าใช้จ่ายของผู้อ่านตัดสินว่าโค้ดรอบๆ นั้นเปลี่ยนแปลงได้ง่ายหรือไม่ ผู้อ่านคนถัดไป ไม่ว่าจะเป็นมนุษย์หรือเอเจนต์ จ่ายสำหรับทุกบรรทัดที่ต้องโหลดเพื่อเปลี่ยนแปลงอย่างปลอดภัย เอเจนต์จ่ายด้วยโทเคนและนำทางด้วยการค้นหาข้อความ, การอ่านบางส่วน และลูปตรวจสอบประเภท/ทดสอบ ดังนั้นข้อบกพร่องเดียวกันจึงมีค่าใช้จ่ายมากขึ้นสำหรับพวกเขา ถามว่า:

- **ค้นหาได้ไหม?** ใช้ชื่อเดียวต่อแนวคิด สะกดเหมือนกันทุกที่ ค้นหาได้ด้วยข้อความธรรมดา ข้อบกพร่อง: ชื่อประกอบจากสตริง, การเชื่อมโยงโดยผลข้างเคียงของการนำเข้า, โซ่การส่งออกซ้ำที่ซ่อนคำจำกัดความ, สองชื่อสำหรับแนวคิดเดียวกัน
- **ผู้อ่านหยุดได้เร็วไหม?** สัญญาอยู่ด้านบนของไฟล์หรือเหนือการส่งออก: สิ่งที่สัญญา สิ่งที่ซ่อน สิ่งที่ไม่เคยทำ ข้อบกพร่อง: สัญญาสามารถได้มาโดยการอ่านเนื้อหาเท่านั้น
- **ตรวจสอบโดยเครื่องได้ไหม?** ประเภทที่แม่นยำเข้าและออกจากทุกขอบเขต เพื่อให้การตรวจสอบประเภทแทนที่การอ่านผู้เรียก ข้อบกพร่อง: `any`, พจนานุกรมเปล่า, ธงบูลีนที่ความหมายอยู่ในเนื้อหา
- **การเชื่อมโยงเห็นได้ชัดไหม?** สถานที่ที่ต้องเปลี่ยนพร้อมกันถูกบังคับ (ประเภทที่ใช้ร่วมกัน, การทดสอบ, แหล่งเดียว) หรือถ้าไม่ได้ ให้ทำเครื่องหมายทั้งสองที่ หลักฐานของการเชื่อมโยงที่ซ่อนอยู่คือการเปลี่ยนแปลงร่วมในประวัติที่ไม่มีอะไรในโค้ดกล่าวถึง
- **ไม่มีเสียงรบกวนไหม?** ไม่มีคอมเมนต์ที่กล่าวซ้ำโค้ด, ไม่มีโค้ดที่ถูกคอมเมนต์ออก, ไม่มีสาขาที่ตายแล้ว, ไม่มีคอมเมนต์ประวัติการเปลี่ยนแปลง, ไม่มีเส้นทางล้าสมัยเก็บไว้ข้างๆ ตัวแทน
- **คาดเดาได้ไหม?** การจัดวางตามรูปแบบที่มีอยู่ในรีโป; การทดสอบอยู่ที่ที่ผู้อ่านจะมองหาและรันได้เอง

ขนาดไฟล์ไม่ได้ระบุโดยเจตนา ไฟล์ขนาดใหญ่มากเป็นเหตุผลในการมองหาการตัดสินใจที่ซ่อนอยู่ครั้งที่สอง ไม่ใช่เหตุผลในการตัด: ผู้อ่านสามารถค้นหาและอ่านช่วง และการแยกที่ไม่ซ่อนอะไรเพิ่มอินเทอร์เฟซโดยไม่ลดภาระ

สำหรับเครื่องหมายในโค้ดและแผนที่โค้ดของรีโป ให้ใช้ `context-audit` เมื่อมี: แองเคอร์ `AIDEV-NOTE:` (ข้อเท็จจริงที่ไม่สามารถกู้คืนได้หนึ่งข้อพร้อมการอ้างอิงแหล่งที่มา ไม่เกินสองบรรทัด ที่ไซต์) เป็นข้อบังคับสำหรับการเชื่อมโยงที่ไม่สามารถบังคับได้

## การปรับโครงสร้างโค้ดที่มีอยู่ให้เป็นมาตรฐานนี้

การปรับปรุงย้อนหลังถูกตัดสินเหมือนโค้ดใหม่ สิ่งที่แตกต่างคือลำดับและความระมัดระวัง ส่วนใหญ่ของฐานโค้ดควรปล่อยไว้ตามเดิม

1. **สำรวจ, อ่านอย่างเดียว.** รายการขอบเขต (โมดูล, บริการ, ตัวช่วยที่ใช้ร่วมกัน) สำหรับแต่ละรายการ: การตัดสินใจที่ซ่อนอยู่ หรือ "ไม่มี"; ขนาดอินเทอร์เฟซเทียบกับเนื้อหา; คู่เปลี่ยนร่วมจากประวัติ; ข้อบกพร่องค่าใช้จ่ายผู้อ่าน ยังไม่เปลี่ยนแปลงอะไร
2. **จัดอันดับตามการเปลี่ยนแปลง ไม่ใช่ความน่าเกลียด.** ลำดับความสำคัญคือความถี่ที่โค้ดเปลี่ยนคูณกับค่าใช้จ่ายในการอ่าน โค้ดเย็นที่ทำงานอยู่คงไว้ตามเดิม แม้จะตื้นเขิน ความซับซ้อนโดเมนที่จำเป็นคงไว้ที่เดิม (Brooks)
3. **กำหนดวิธีแก้ไขหนึ่งอย่างต่อการค้นพบ:**
   - ชั้นผ่านหรือตัวห่อที่ไม่ซ่อนอะไร: ลบมัน ผู้เรียกใช้สิ่งที่มันห่อไว้;
   - นามธรรมผิดที่บิดเบี้ยวโดยธงและกรณีพิเศษ: แทรกกลับ (Metz) แล้วมองหาค่าคงที่จริง;
   - พี่น้องตื้นที่แชร์การตัดสินใจหนึ่งอย่าง: รวมพวกเขาไว้หลังอินเทอร์เฟซเดียว;
   - การตัดสินใจรั่วไหล (ผู้เรียกรู้รูปแบบ กฎ หรือสคีมา): ดึงลงไปในโมดูลที่เป็นเจ้าของ;
   - ชื่อทั่วไป (`utils`, `helpers`, `manager`): เปลี่ยนชื่อให้ตรงกับการตัดสินใจที่ซ่อน หรือสลายเป็นผู้เรียก;
   - ขอบเขตไม่มีประเภท: กำหนดประเภท และแทนที่การแปลงด้วยแมปเปอร์ที่เคยปกปิด;
   - การเชื่อมโยงที่ซ่อน: บังคับใช้ หรือทำเครื่องหมายทั้งสองที่;
   - เสียงรบกวน: ลบมัน

   คำคล้องจองที่เปลี่ยนแปลงอย่างอิสระไม่มีวิธีแก้ไข
4. **ตรึงพฤติกรรมก่อน.** ไม่มีวิธีแก้ไขใดเริ่มจนกว่าการทดสอบจะพิสูจน์พฤติกรรมปัจจุบันของโค้ดที่แตะต้อง (Beck) การปรับโครงสร้างรักษาพฤติกรรมไว้ การเปลี่ยนแปลงพฤติกรรมเป็นคอมมิตแยกต่างหาก
5. **แบ่งงานเป็นหน่วยที่เอเย่นต์หนึ่งคนทำเสร็จได้เอง.** หนึ่งขอบเขตต่อหน่วย หน่วยแต่ละหน่วยตั้งชื่อไฟล์ที่เป็นเจ้าของ สัญญาที่ต้องรักษา และคำสั่งที่พิสูจน์ได้เอง ไม่มีสองหน่วยพร้อมกันเขียนไฟล์เดียวกัน ไฟล์ที่ใช้ร่วมกัน (บาร์เรล, รีจิสทรี, ตารางเส้นทาง) มีเจ้าของเดียวหรือรอการรวม อินเทอร์เฟซที่หลายหน่วยพึ่งพาลงก่อนเป็นหน่วยของตัวเอง
6. **วัดผลลัพธ์.** เลือกการเปลี่ยนแปลงตัวแทนก่อนเริ่มและนับไฟล์กับบรรทัดที่ผู้อ่านต้องโหลดเพื่อทำมัน; นับอีกครั้งหลังทำ ชื่อที่ส่งออกและจำนวนบรรทัดควรลดลงหรือตัวเดิม การปรับโครงสร้างที่เพิ่มอินเทอร์เฟซต้องมีเหตุผลที่ระบุ
7. **หยุด** เมื่อสิ่งที่เหลือเป็นโค้ดเย็น จำเป็น หรือคำคล้องจอง

ทักษะที่เกี่ยวข้อง เมื่อมี: `repo-review` (ประเภทการออกแบบ) ผลิตสำรวจเป็นเอกสารแนะนำเท่านั้น; `design-cleanup` ทำวงจรแก้ไขและสแกนซ้ำสำหรับความซับซ้อนโดยบังเอิญ; `context-audit` เพิ่มแองเคอร์และแผนที่โค้ด; `ousterhout-build-deep` เป็นรายการตรวจสอบเวลาผู้เขียนสำหรับเอเย่นต์ที่ทำหน่วย

## ตำแหน่งของทักษะนี้

ทักษะนี้เป็นชั้นการตรวจสอบและตัดสินใจ: ใช้เพื่อตัดสินว่า abstraction ลึก, ตั้งชื่อสำหรับการตัดสินใจที่ถูกต้อง, และคุ้มค่าที่จะสกัดออก `find-shared-code` ใช้เป็นการทดสอบรับเข้าเมื่อสแกนประวัติใหม่สำหรับโค้ดที่คุ้มค่าแบ่งปัน ภาคผนวกด้านล่างให้เหตุผลของแต่ละผู้เขียน

---

## ภาคผนวก: เลนส์ในเชิงลึก

โหมดล้มเหลวที่แต่ละผู้เขียนจับได้ และการเคลื่อนไหวหนึ่งอย่างที่แต่ละคนให้ ตารางด้านบนเป็นการอ้างอิงด่วน นี่คือเหตุผลเบื้องหลัง

### Ousterhout — โมดูลลึก (กระดูกสันหลัง)

*ปรัชญาการออกแบบซอฟต์แวร์.*

- **ความลึก** = ประโยชน์ (ฟังก์ชันที่ซ่อน) ÷ ค่าใช้จ่าย (ความซับซ้อนของอินเทอร์เฟซ) โมดูลลึกซ่อนมากมายไว้เบื้องหลังเล็กน้อย โมดูลตื้นอินเทอร์เฟซซับซ้อนเกือบเท่ากับเนื้อหา จึงไม่คุ้มค่า
- **ความซับซ้อน** คือสิ่งใดๆ ในระบบที่ทำให้เข้าใจหรือแก้ไขยาก แหล่งที่มา 2 อย่าง:
  - **การพึ่งพา** — คุณไม่สามารถเปลี่ยนชิ้นส่วนหนึ่งโดยไม่แตะอีกชิ้น
  - **ความคลุมเครือ** — ข้อมูลสำคัญไม่ชัดเจนจากโค้ด
- **อาการ:** การขยายการเปลี่ยนแปลง (การตัดสินใจหนึ่งอย่าง แก้ไขหลายจุด), ภาระทางปัญญา (ต้องถือไว้ในหัวมากแค่ไหน), สิ่งที่ไม่รู้ไม่รู้ (ไม่สามารถบอกได้ว่าการเปลี่ยนแปลงจะกระทบโค้ดไหน)
- **การเคลื่อนไหวสำคัญ:** ดึงความซับซ้อน *ลงล่าง* — โมดูลดูดซับกรณียากเพื่อให้ผู้เรียกไม่ต้องทำ พารามิเตอร์การกำหนดค่าและชั้นผ่านผลักความซับซ้อน *ขึ้น* ไปยังผู้เรียก; นั่นคือตื้น

จับได้: อินเทอร์เฟซที่รั่วไหลการใช้งาน; ตัวช่วยที่ไม่ช่วย

### Parnas — การซ่อนข้อมูล (ทำไมความลึกจึงสำคัญ)

*เกี่ยวกับเกณฑ์ที่ใช้ในการแยกระบบเป็นโมดูล (1972).*

- แยกส่วนรอบๆ **การตัดสินใจออกแบบที่มีแนวโน้มจะเปลี่ยนแปลง** ไม่ใช่รอบๆ ขั้นตอนของ
  การคำนวณแต่ละขั้นตอน โมดูลแต่ละตัวซ่อนการตัดสินใจแบบนี้ไว้หนึ่งอย่าง
- นี่คือบรรพบุรุษโดยตรงของโมดูลลึก โมดูลจึงลึก *เพราะ* มันซ่อนการตัดสินใจที่
  มิฉะนั้นจะส่งผลกระทบไปยังผู้เรียกใช้งาน

ข้อควรระวัง: "โมดูล" ที่ไม่ซ่อนอะไรที่เปลี่ยนแปลงได้ — ความลึกของมันเป็นเพียง
ความสวยงาม ถามว่า: มีอะไรเปลี่ยนแปลงหลังอินเทอร์เฟซนี้ที่ผู้เรียกไม่เคยเห็นไหม?
ถ้าคำตอบคือ "ไม่มี" ขอบเขตนี้ก็เป็นเพียงการตกแต่ง

### Brooks — ความซับซ้อนที่จำเป็นกับที่ไม่จำเป็น

*ไม่มีวิธีแก้ปัญหาที่วิเศษ.*

- ความซับซ้อน **ที่จำเป็น** เป็นสิ่งที่มีอยู่ในโดเมน (การประเมินค่าจริงๆ แล้วซับซ้อนขนาดนี้)
  ความซับซ้อน **ที่ไม่จำเป็น** คือสิ่งที่เครื่องมือและโครงสร้างของเรากำหนด
- มีเพียงความซับซ้อนที่ไม่จำเป็นเท่านั้นที่สามารถลบออกได้ การรีแฟกเตอร์ที่
  "ทำความสะอาด" โดยย้ายความซับซ้อนที่จำเป็นของโดเมนจากไฟล์หนึ่งไปอีกไฟล์
  ไม่ได้ช่วยอะไรเลย

ข้อควรระวัง: การจัดเรียงใหม่ที่แอบอ้างว่าเป็นการทำให้เรียบง่าย ถามว่า: ความซับซ้อน
ทั้งหมดลดลงจริงหรือแค่ย้ายที่ไปเท่านั้น?

### Evans — Domain-Driven Design

*Domain-Driven Design.*

- ขอบเขตควรถูกตั้งชื่อด้วย **ภาษาที่ใช้กันทั่วไปในโดเมน** ไม่ใช่ด้วยคำศัพท์
  ทั่วไปที่ใช้ในเครื่องมือ โมดูลที่ชื่อ `helpers` ไม่ได้ตั้งชื่ออะไรเลย;
  โมดูลที่ชื่อ `AccessScope` หรือ `PricingPolicy` ตั้งชื่อสิ่งที่ไม่เปลี่ยนแปลง
- Bounded contexts ป้องกันไม่ให้ invariant ทางธุรกิจรั่วไหลข้ามขอบเขต

ข้อควรระวัง: การแยกส่วนที่ถูกต้องแต่ตั้งชื่อไร้ความหมาย ถ้าคุณตั้งชื่อโมดูล
ในภาษาของโดเมนไม่ได้ แสดงว่าคุณอาจตัดขอบเขตผิดที่

### Fowler — Refactoring and Code Smells

*Refactoring.*

- ให้การเคลื่อนไหวที่เป็นรูปธรรม ปลอดภัย และมีชื่อ (Extract Function, Move Field,
  Replace Conditional with Polymorphism) เพื่อเปลี่ยนจากการออกแบบปัจจุบันไปสู่
  การออกแบบที่ลึกกว่า
- ทุกการเคลื่อนไหวรักษาพฤติกรรมและมีขนาดเล็ก เพื่อให้สามารถย้อนกลับได้

ข้อควรระวัง: ช่องว่างระหว่าง "สิ่งนี้ควรลึกกว่า" กับการรู้ว่าการคอมมิตถัดไปคืออะไร
Ousterhout กำหนดเป้าหมาย; Fowler คือเส้นทาง

### Beck — Simple Design, Test-First

*Test-Driven Development; XP.*

- กฎสี่ข้อของการออกแบบที่เรียบง่าย ตามลำดับที่ Beck เผยแพร่: ผ่านการทดสอบ,
  ไม่มีการทำซ้ำ, เปิดเผยเจตนา, มีองค์ประกอบน้อยที่สุด โปรแกรมนี้ใช้ลำดับใหม่ของ
  Fowler/Haines — เจตนาก่อนการทำซ้ำ — เพราะมันสอดคล้องกับกฎ invariant ที่
  ขยายโดย Metz (ดู Metz ด้านล่าง): อย่าดำเนินการกับการทำซ้ำจนกว่าคุณจะตั้งชื่อ
  เจตนาที่มันปกป้องได้
- การทดสอบก่อนเป็นเบรกป้องกันสถาปัตยกรรมที่เกิดก่อนเวลา ทำให้มันทำงานและ
  พิสูจน์พฤติกรรม *ก่อน* แล้วจึงลึกซึ้งขอบเขตที่การทดสอบปกป้อง

ข้อควรระวัง: สถาปัตยกรรมที่สร้างก่อนพฤติกรรมถูกกำหนด หากไม่มีการทดสอบ
พิสูจน์พฤติกรรมปัจจุบัน การรีแฟกเตอร์เพื่อ "ลึกซึ้ง" เป็นการเขียนใหม่ที่ไม่ได้รับการยืนยัน

### Hickey — Simple vs Easy

*Simple Made Easy.*

- **Simple** = ไม่ถักทอ: แนวคิดเดียว ไม่ปะปนกับอย่างอื่น (วัตถุประสงค์)
- **Easy** = อยู่ใกล้มือ คุ้นเคย เข้าถึงได้เร็ว (สัมพันธ์กับคุณ)
- สองอย่างนี้เป็นอิสระกัน ผู้ช่วยตื้นๆ มักจะ *ง่าย* — เขียนเร็ว ใกล้มือ — แต่ไม่ *เรียบง่าย*
  ถ้ามันถักทอความกังวลที่ไม่เกี่ยวข้องกัน

ข้อควรระวัง: ความสะดวกที่ปลอมตัวเป็นการออกแบบ ชอบโครงสร้างที่รักษาแนวคิดให้
ไม่ถักทอ แม้ว่าการถักทอจะพิมพ์ได้เร็วกว่า

### Metz — Prefer Duplication Over the Wrong Abstraction

*"The Wrong Abstraction" (2016).* 

- การทำซ้ำถูกกว่าการใช้ abstraction ที่ผิดมาก abstraction ที่ถูกดึงออกมาเร็วเกินไป
  บังคับให้ผู้เรียกใช้งานในอนาคตทุกคนต้องปรับตัวตามสมมติฐานที่ไม่เคยเป็นจริง
  สำหรับทุกคน
- เมื่อ abstraction ผิดพลาด วิธีแก้ของ Metz คือฝังมันกลับเข้าไปและปล่อยให้การทำซ้ำ
  กลับมา แทนที่จะบังคับให้มันเหมาะกับกรณีที่มันไม่เคยถูกสร้างมาเพื่อ
- **กฎของโปรแกรมนี้ที่ขยาย Metz: อย่ารวมศูนย์เพราะโค้ดซ้ำซ้อน รวมศูนย์เมื่อมัน
  ปกป้อง invariant ที่แท้จริงและใช้ร่วมกัน** จนกว่า invariant จะปรากฏตัว
  ยอมรับการทำซ้ำ

ข้อควรระวัง: การรวมศูนย์มากเกินไป — ผู้ช่วยที่ใช้ร่วมกันตื้นๆ ที่ทุกคนต้องทำงาน
รอบๆ นี่คือแรงต้านต่อ "DRY ทุกกรณี"

### Hyrum's Law — Observable Behavior Becomes Contract

*"ด้วยจำนวนผู้ใช้ที่เพียงพอ พฤติกรรมที่สังเกตได้ทุกอย่างของระบบของคุณ
จะถูกพึ่งพาโดยใครบางคน"*

- ไม่ว่าอินเทอร์เฟซจะ *เกิดขึ้น* ทำอะไร — การเรียงลำดับ, เวลา, ข้อความผิดพลาด —
  สักวันจะมีคนพึ่งพา ดังนั้นผิวหน้าที่คุณเปิดเผยจึงใหญ่กว่าผิวหน้าที่คุณบันทึกไว้
- นี่สนับสนุนความชอบของ Ousterhout สำหรับ **อินเทอร์เฟซขนาดเล็กและเสถียร**:
  ยิ่งเปิดเผยน้อย ยิ่งมีโอกาสน้อยที่จะกลายเป็นภาระโดยไม่ตั้งใจ

ข้อควรระวัง: อินเทอร์เฟซกว้างที่จะกลายเป็นแข็งตัว พฤติกรรมที่สังเกตได้ทุกอย่าง
กลายเป็นข้อจำกัดในอนาคต

### วิธีที่พวกเขาเข้ากัน

- **Parnas → Ousterhout:** ซ่อนการตัดสินใจที่เปลี่ยนแปลงได้ → โมดูลนั้นลึก
- **Brooks:** ยืนยันว่าความลึกช่วยลดความซับซ้อน ไม่ใช่แค่ย้ายไปที่อื่น
- **Evans:** ตั้งชื่อขอบเขตด้วยภาษาของโดเมน
- **Beck → Fowler:** กำหนดพฤติกรรม จากนั้นรีแฟกเตอร์ด้วยการเคลื่อนไหวเล็กๆ ที่ปลอดภัย
- **Metz:** ต่อต้านการรวมศูนย์จนกว่า invariant จะเป็นจริง
- **Hickey:** รักษาอินเทอร์เฟซให้เป็นแนวคิดเดียว
- **Hyrum:** รักษาอินเทอร์เฟซให้เล็กเพื่อให้เสถียร

อันตรายคือการผสม Ousterhout กับการอ่าน SOLID หรือ Clean Code แบบกลไก:
ซึ่งจะสร้างคลาสและฟังก์ชันเล็กๆ จำนวนมากที่มีอินเทอร์เฟซตื้นๆ — ตรงกันข้ามกับ
โมดูลลึก Ousterhout พร้อมกับ Metz เป็นแรงต้านคือยารักษา

แท็ก

designarchitecturereviewrefactoringousterhout

เจาะลึกเพิ่มเติม

ดาวน์โหลด AgentsRoom

รันเอเจนต์ AI ทั้งหมดของคุณในทุกโปรเจกต์ จากหน้าต่างเดียว

ฟรีดาวน์โหลด AgentsRoom

แอปคู่หู: ตรวจสอบเอเจนต์ของคุณได้ทุกที่

นำของคุณเอง: Claude, Codex, Antigravity CLI, หรือผู้ให้บริการ AI อื่น ๆ

รับส่วนขยาย
Chrome Web Store

ส่งข้อบกพร่องและคำขอไปยังแบ็คล็อกสาธารณะของคุณโดยตรง

หลายโปรเจกต์
ผู้ให้บริการหลายราย
หลาย agents
สถานะสด
ไฟล์ diff & commit
คู่หูมือถือ
ตัวอย่างสด
ทีมเอเจนต์
การทำงานอัตโนมัติในเบราว์เซอร์
การพัฒนาที่ขับเคลื่อนด้วย backlog
ห้องสมุดคำสั่ง
ห้องสมุดทักษะ
ดูฟีเจอร์ทั้งหมด