การเขียนโค้ดสำหรับมนุษย์ยากกว่าการเขียนโค้ดให้คอมพิวเตอร์
(erikbern.com)- เครื่องมือสำหรับนักพัฒนายากกว่า เพราะต้องออกแบบไม่เพียงตรรกะที่คอมพิวเตอร์จะรัน แต่ยังรวมถึง โมเดลทางความคิด ที่คนอื่นจะเข้าใจและนำไปใช้ได้ด้วย
- การ onboarding ที่รวดเร็วไม่ใช่ฟีเจอร์เสริม แต่แทบจะเป็น ตัวผลิตภัณฑ์เอง ต้องลดแรงเสียดทานจากการตั้งค่า, API token และการรันครั้งแรก เพื่อให้ลองใช้บนแล็ปท็อปได้ภายในไม่กี่นาที
- ผู้ใช้เรียนรู้แพตเทิร์นได้จากการลองปรับ ตัวอย่างที่ทำงานได้จริง มากกว่าการอ่านคำอธิบายแนวคิดหลักยาว ๆ และยิ่งมีจุดเริ่มต้นที่ใกล้กับปัญหามากเท่าไร โอกาสสำเร็จก็ยิ่งสูงขึ้น
- ข้อความ error, จำนวนแนวคิด, การตั้งชื่อ, วิธีตั้งค่า, ค่า default, magic และ syntax sugar ล้วนเปลี่ยนเส้นทางสู่ความสำเร็จของผู้ใช้ จึงต้องออกแบบให้ อ่านง่ายและปรับแต่งได้
- ประสบการณ์นักพัฒนาที่ดีไม่ใช่การตัดฟีเจอร์ให้เหลือน้อยลงเฉย ๆ แต่คือการลด ความซับซ้อนที่ต้องรู้ ลงอย่างมาก โดยยังคงขอบเขตสิ่งที่สร้างได้ไว้
โค้ดสำหรับมนุษย์ต้องดูแลไปถึงโมเดลทางความคิด
- โค้ดสำหรับคอมพิวเตอร์คือการแตกเป้าหมายทางธุรกิจขนาดใหญ่ออกเป็นประโยคเชิงตรรกะ เพื่อให้คอมพิวเตอร์ทำตามได้
- โค้ดที่มนุษย์ต้องใช้งานโดยตรง เช่น framework, library, API, SDK, DSL, embedded DSL และภาษาโปรแกรมมิง แค่รันได้ยังไม่พอ
- โค้ดแบบนี้ต้องทั้งสั่งคอมพิวเตอร์ และดูแลไปพร้อมกันว่าผู้ใช้จะอ่านและเข้าใจโค้ดนั้นอย่างไร
- การออกแบบเครื่องมือสำหรับนักพัฒนาต้องอาศัยทั้ง วิทยาการคอมพิวเตอร์ และความเข้าใจเชิงจิตวิทยาว่าผู้ใช้ให้เหตุผลอย่างไร
ประสบการณ์เริ่มต้นก็คือตัวผลิตภัณฑ์
- feedback ของเครื่องมือสำหรับนักพัฒนามักมาจาก power user ที่ใช้ผลิตภัณฑ์บ่อย
- ผู้ใช้ที่ติดตั้งแต่ช่วงเริ่มต้นมักไม่ทิ้ง feedback ไว้ จึงเกิด survivorship bias
- เช่นเดียวกับที่ผลิตภัณฑ์ผู้บริโภคปรับ funnel การ onboarding ให้เหมาะสม เครื่องมือสำหรับนักพัฒนาก็ควรมองกระบวนการจนถึงการรันครั้งแรกว่าเป็นแกนหลักของผลิตภัณฑ์
- เพื่อให้ onboarding ได้รวดเร็ว การปรับโครงสร้างตัวผลิตภัณฑ์เองก็เป็นเรื่องคุ้มค่า
- ตัดการตั้งค่าที่จำเป็นออก
- ทำให้การตั้งค่า API token ง่ายมาก
- ลดแรงเสียดทานช่วงเริ่มต้น
- ทำให้ผู้ใช้ลองใช้ผลิตภัณฑ์บนแล็ปท็อปของตัวเองได้ภายในไม่กี่นาที
- ในสภาพแวดล้อมที่มีเครื่องมือสำหรับนักพัฒนามากเกินไป ผู้ใช้มักไม่มีพลังหรือความอดทนพอที่จะทำความเข้าใจความแตกต่างของแพ็กเกจ LRU cache บน NPM ตัวใดตัวหนึ่งอย่างลึกซึ้ง
ตัวอย่างสอนได้เร็วกว่าแนวคิดหลัก
- มนุษย์ต่างจากคอมพิวเตอร์ที่ทำตามคำสั่งอย่างเคร่งครัด เพราะมนุษย์เก่งเรื่อง การจับคู่แพตเทิร์น
- เอกสารเครื่องมือสำหรับนักพัฒนาหลายตัวเริ่มจากการอธิบาย data model หลัก, ความสัมพันธ์, แนวคิดระดับอะตอม, การตั้งค่า และวิธีรัน แต่คนเราเรียนรู้ได้ดีกว่าจากการปรับกรณีที่ทำงานได้จริงแล้วดูผลลัพธ์
- ตัวอย่างหลาย ๆ ชิ้นอาจมีประโยชน์มากกว่าคำอธิบาย “core concepts” ยาว 5,000 คำ
- ผู้ใช้เรียนรู้วิธีทำงานของเครื่องมือจากการดูตัวอย่าง
- คนที่มีปัญหาที่ต้องการแก้สามารถหาจุดเริ่มต้นที่ใกล้พอได้
- ยิ่งมีจุดเริ่มต้นมากเท่าไร โอกาสเจอตัวอย่างที่ใกล้กับสิ่งที่ต้องการก็ยิ่งสูงขึ้น
ผลักผู้ใช้ให้ตกลงไปในหลุมแห่งความสำเร็จ
- สถานะพื้นฐานของการเขียนโปรแกรมใกล้เคียงกับการแก้ error บางประเภทไปเรื่อย ๆ
- ผู้ใช้อาจใช้เวลาส่วนใหญ่กับเครื่องมือไปกับการหาว่า “อะไรไม่ทำงาน”
- ถ้านักพัฒนาสำเร็จได้เร็วขึ้น พวกเขาจะชอบเครื่องมือ แต่ถ้าติด error ซ้ำแล้วซ้ำเล่า พวกเขาจะโทษเครื่องมือ
- error ทุกครั้งคือโอกาสที่จะพาผู้ใช้กลับสู่ happy path
- ใส่ code snippet ใน exception message
- แสดง warning ที่มีประโยชน์เมื่อมีโอกาสที่ผู้ใช้อาจทำอะไรแปลก ๆ
- ให้ขั้นตอนที่จำเป็นต่อความสำเร็จของผู้ใช้
ลดภาวะแนวคิดล้นเกิน
- แนวคิดใหม่แต่ละอย่างที่ต้องเข้าใจก่อนใช้เครื่องมือจะกลายเป็น จุดเสียดทาน
- แนวคิด 2–3 อย่างยังพอรับได้ แต่ผู้ใช้ที่อยากเรียนแนวคิดใหม่ 8 อย่างมีไม่มาก
- Kubernetes ไม่ได้ต้องใช้ทุกแนวคิดตั้งแต่เริ่มต้น แต่ยิ่งแนวคิดใหม่มีมาก ภาระก็ยิ่งสูงขึ้น
- framework ที่ทรงพลังแต่มีแนวคิดเพียง 3–5 อย่างมีความสง่างามในตัว
- เมื่อเริ่มใช้ React หากผ่านเนินเชิงแนวคิดหลังจากหนึ่งหรือสองชั่วโมงแรกไปได้ ก็อาจให้ความรู้สึกว่าสามารถสร้างโครงสร้างขนาดใหญ่จาก building block ง่าย ๆ เพียงไม่กี่ชิ้น
- เป้าหมายไม่ใช่แค่ลดจำนวนแนวคิด แต่คือการลดแนวคิดที่ผู้ใช้ต้องรู้ โดยยังรักษา ขอบเขตสิ่งที่สร้างได้ ไว้
- เครื่องมือที่ยอดเยี่ยมสามารถลดความซับซ้อนได้ 90% โดยยังคงความสามารถไว้เหมือนเดิม
- เครื่องมือที่ลดความซับซ้อนได้ 90% และลดความสามารถลงเพียง 10% ก็ไม่เลวเช่นกัน
หลักการเป็ดเชิงแนวคิด
- ถ้าใน framework มีองค์ประกอบที่รับค่าแล้วคำนวณค่าใหม่ เรียกมันว่า function จะดีกว่าการตั้งชื่อใหม่อย่าง “compute node”, “valuator” หรือ “frobniscator”
- หลักการที่ว่า หากบางสิ่งเดินเหมือนเป็ดและร้องก๊าบเหมือนเป็ด ก็มีแนวโน้มสูงที่จะเป็นเป็ด สามารถนำมาใช้กับการออกแบบแนวคิดได้เช่นกัน
- แม้จะมีความแตกต่างเล็กน้อย หรือค่าถูก cache ไว้ แต่ถ้ามันใกล้เคียงกับ function มากพอ ก็เรียกว่า function ได้
- การใช้คำศัพท์เดิมช่วยเชื่อมกับ โมเดลทางความคิด ที่ผู้ใช้มีอยู่แล้ว และลดปริมาณสิ่งที่ต้องอธิบายลงได้มาก
ทำให้โปรแกรมได้
- ผู้ใช้จะทำสิ่งที่คาดไม่ถึงกับ codebase และอาจใส่องค์ประกอบของ framework ไว้ใน for-loop, ในฟังก์ชัน หรือในโครงสร้างอื่น ๆ
- ดังนั้นแทบทุกอย่างใน framework ควร โปรแกรมได้
- แนวทางการออกแบบที่เกี่ยวข้องเชื่อมโยงกันอยู่
- ทำให้เรียกจากโค้ดได้โดยตรงโดยไม่ต้องผ่าน CLI
- ลดไฟล์ตั้งค่าและเปลี่ยนเป็น SDK หรือ API
- อย่าทำให้สร้างได้แค่อย่างเดียว แต่ทำให้ parameterize แล้วสร้างได้ n รายการ
- การออกแบบแบบนี้อาจทำให้ผู้ใช้ค้นพบกรณีใช้งานใหม่ ๆ
- หากใช้ประโยชน์จากความอยาก “hack” บน framework แม้จะเกิดความสับสนบ้างเล็กน้อย ก็อาจนำไปสู่การค้นพบที่คาดไม่ถึง
ต้องระวังกับ magic, ค่า default และ syntax sugar
- สมมติว่ามีฟังก์ชัน
run_notebookที่รัน Jupyter notebook บนคลาวด์ และผู้ใช้ต้องระบุว่าจะใช้ container image ใด - ตัวเลือกที่เป็นไปได้มีหลายแบบ
- บังคับรับอาร์กิวเมนต์
image=...เสมอ - มี image ค่า default ที่ติดตั้งไลบรารี data science ส่วนใหญ่ไว้ แล้วให้ผู้ใช้ override ได้
- ตรวจสอบโค้ดใน cell แล้วเลือก image แบบ “magic” ตาม dependency ที่จำเป็น
- นอกจากวิธี magic แล้ว ยังให้ผู้ใช้เลือก image เฉพาะได้ด้วย
- บังคับรับอาร์กิวเมนต์
- หากต้องการลดปริมาณ input และรองรับกรณีใช้งานที่กว้างที่สุด ตัวเลือกสุดท้ายอาจดูดี
- แต่หากไม่เลือกตัวเลือกแรก ก็ยังมีปัญหาเหลืออยู่
- magic พังได้ในบางสถานการณ์
- ผู้ใช้ที่อ่านโค้ดซึ่งพึ่งพาค่า default อาจไม่ทันสังเกตว่าสามารถปรับแต่งได้
- หากค่า default ไม่ถูกนำไปใช้ได้มากกว่า 97% และ magic ไม่ถูกต้องมากกว่า 99% ก็ควรระมัดระวังอย่างยิ่ง
- การเขียนโค้ดไม่ใช่ golf และงานของผู้ให้บริการเครื่องมือไม่ใช่แค่การทำให้จำนวนโค้ดที่ผู้ใช้เขียนน้อยที่สุด
- Perl ถูกปรับให้เหมาะกับโค้ดสั้นอย่างมาก แต่โปรแกรมอาจดูเหมือนลำดับของอักขระพิเศษ ส่วน Python แม้โค้ดยาวกว่า 50% ก็อ่านง่ายและเข้าใจง่ายกว่า
- ผู้คน อ่านโค้ดมากกว่าเขียนโค้ด 10 เท่า ดังนั้นความอ่านง่ายจึงสำคัญ
- syntax sugar ก็ควรพิจารณาด้วยเกณฑ์เดียวกัน
- อาจอยากใส่ syntax พิเศษให้กรณีใช้งานที่พบบ่อย
- แต่อาจทำให้ความสอดคล้องลดลง และทำให้วิธีปรับแต่งไม่ชัดเจนนัก
- หาก syntax sugar ใช้ได้ไม่เกิน 99% ของกรณี อาจดีกว่าที่จะไม่ใส่เข้ามา
หลักการออกแบบสำหรับผู้ใช้ครั้งแรก
- การเขียนโค้ดสำหรับมนุษย์ยังมีปัญหาการออกแบบอีกมาก
- ส่วนใหญ่ควรเป็น immutable แต่ไม่ใช่ทั้งหมด
- หลีกเลี่ยง scaffolding หรือการสร้างโค้ดอัตโนมัติ
- ทำให้ feedback loop เร็วมาก
- ทำให้ผู้ใช้รับมือกับฟีเจอร์ที่จะถูกยกเลิกได้ง่าย
- ใช้ automated test กับ code snippet ในเอกสารและตัวอย่าง
- การออกแบบประสบการณ์ผู้ใช้ครั้งแรกคล้ายกับการทำเพลงป็อป
- แม้โปรดิวเซอร์จะฟังเพลงเป็นพันครั้ง แต่ตอนฟังครั้งที่ 999 ก็ต้องจินตนาการว่าคนที่ฟังครั้งแรกจะได้ยินอย่างไร
- ในเครื่องมือสำหรับนักพัฒนาก็เช่นกัน คนที่สร้างซ้ำ ๆ ย่อมจินตนาการประสบการณ์ของ ผู้ใช้ครั้งแรก ได้ยากมาก
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
แต่ละคนมีวิธีเรียนรู้ต่างกัน สำหรับผม ก่อนจะเข้าไปดูตัวอย่าง ผมต้องการ แนวคิดหลัก ก่อน โดยเฉพาะถ้าแนวคิดหลักนั้นไม่ได้เรียบง่ายมาก ๆ
ทิวทอเรียลจำนวนมากคล้ายกับการจับมือประกอบเลโก้ “นี่คือชิ้นส่วนเลโก้ ลองทำตามผมสร้างโปรเจกต์ของเล่นนี้ แล้วพอหมดวันคุณก็จะเล่นเลโก้เป็น” ประมาณนั้น
วิธีแบบนี้ไม่ค่อยเข้ากับผม ผมอยากรู้ว่าการตัดสินใจต่าง ๆ เกิดขึ้นอย่างไรและทำไมถึงตัดสินใจแบบนั้น และอยากมองจากมุมของผู้เขียน ผมอยากรู้ว่าชิ้นส่วนเลโก้แต่ละชิ้นให้ความรู้สึกอย่างไร เชื่อมต่อกันอย่างไร และไปถึงการออกแบบแบบหนึ่งได้อย่างไร
การทำตามทิวทอเรียลโดยไม่มีคำอธิบายแนวคิดระดับสูงขั้นต่ำสุดเลย ให้ความรู้สึกเหมือนกำลัง วิศวกรรมย้อนกลับ สิ่งที่ไม่ควรต้องทำแบบนั้น เวลาเจอไลบรารีหรือเฟรมเวิร์กใหม่ ผมมักอ่านบทนำแล้วข้ามตัวอย่างโค้ด “เริ่มต้นใช้งาน” ไป โดยทั่วไปส่วน “ขั้นสูง” จะมีการอภิปรายแนวคิดมากกว่า ผมจึงเริ่มจากตรงนั้น จากนั้นไปดูเอกสารอ้างอิง API เพื่อจับอินเทอร์เฟซสำคัญ ๆ แล้วค่อยย้อนกลับมาดูตัวอย่างโค้ดพื้นฐานช่วงต้นของทิวทอเรียลเป็นลำดับสุดท้าย
ทุกวันนี้ผมมักกระโดดเข้าไปทำงานกับตัวอย่างทันทีมากกว่าเดิมมาก และรู้สึกว่ามีผลิตภาพสูงขึ้นด้วย ส่วนหนึ่งเป็นเรื่องของความเชื่อใจ คือเชื่อว่าคนที่สร้างซอฟต์แวร์คุณภาพดีคงคิดมามากพอแล้วที่จะทำให้อินเทอร์เฟซเข้าใจง่ายในกรณีการใช้งานทั่วไป โดยไม่ต้องขุดลึกเข้าไปดูภายใน
แน่นอนว่าผมก็ยังเจออุปสรรคที่ต้องลงลึกอยู่บ่อย ๆ แต่สถานการณ์แบบนั้นเกิดขึ้นเพราะก่อนหน้านั้นมีอีก 10 อย่างที่ผมผ่านไปได้สำเร็จด้วยความประทับใจระดับผิวเผินเท่านั้น ดังนั้นเวลาที่ต้องลงลึกจริง ๆ โดยมากผมจึงมองว่าไม่ใช่การเสียเวลา
เครื่องมือแบบนี้สร้างโครงสร้างโฟลเดอร์ ไฟล์เทมเพลต และเครื่องมือที่ตั้งค่าไว้ล่วงหน้าแบบเฉพาะเจาะจง ถ้าไม่เข้าใจทันทีในระดับสูงว่าไฟล์ที่สร้างขึ้นทำอะไร และทำไมถึงถูกสร้างมาแบบนั้น ก็จะรู้สึกอึดอัดเพราะมีเวทมนตร์ที่ไม่เข้าใจมากเกินไป
ทุกครั้งที่มีสิ่งใหม่เข้ามา ผมต้องการบทนำระดับสูงที่เชื่อมโยงมันกับแนวคิดที่รู้อยู่แล้วและอธิบายจุดประสงค์ของมัน อย่างน้อยจนกว่าจะเข้าใจคร่าว ๆ ถึงอินเทอร์เฟซหลักของกล่องดำเสียก่อน ผมไม่สบายใจที่จะจัดการกับกล่องดำวิเศษ ๆ แบบนั้น เช่น ถ้าผมเรียน create-react-app ตั้งแต่ต้น ก็คงเริ่มสืบค้นจุดประสงค์ของเครื่องมือที่มันตั้งค่าไว้ให้ อย่าง Babel หรือ ESLint ทันที
หลายปีต่อมา หลังจากได้เห็นตัวอย่างเชิงปฏิบัติที่ดีจำนวนมาก ถึงเริ่มเข้าใจว่าแนวคิดนั้นกำลังพูดถึงอะไร หลังจากความเข้าใจนั้น ผมจึงปรับวิธีเรียน
เริ่มจากกวาดตามองแนวคิดหลักก่อน แล้วลองตัวอย่างหลาย ๆ แบบจนเข้าใจว่าทำไมต้องมีแนวคิดนั้น จากนั้นจึงอ่านแนวคิดหลักอย่างละเอียดเพื่อกำจัด กรณีขอบ ที่ตัวอย่างแบบไร้เดียงสาหลงเหลือไว้
อย่างไรก็ตาม ผมคิดว่าการเริ่มจากตัวอย่างอาจช่วยในการออกแบบ API ที่ดีได้ ถ้าออกแบบ API แบบ “แนวคิดหลักมาก่อน” สุดท้ายมักกลายเป็น API ที่ต้องเข้าใจแนวคิดหลักเสียก่อนถึงจะใช้ได้ ซึ่งไม่ดีสำหรับผู้ใช้ที่ใช้งานแค่นาน ๆ ครั้ง
ตามสไตล์แฮ็กเกอร์ ไม่มีการอ้างอิง ผมดูด้านการศึกษาศาสตร์มาเพียงเล็กน้อย แต่มันเป็นสาขาวิชาขนาดใหญ่และเติบโตเต็มที่แล้ว ซึ่งดึงหลักการสมัยใหม่มาจากจิตวิทยาประสบการณ์นิยมของ Dewey และ Piaget มีเรื่องให้พูดมากกว่าที่บทความบล็อกหนึ่งบท—ยิ่งไม่ต้องพูดถึงแค่หนึ่งส่วนของบทความบล็อก—จะครอบคลุมได้มากนัก
ปัญหาใหญ่ที่สุดคืออย่างที่ชี้ไว้ คือแต่ละคนแตกต่างกัน ปัญหาใหญ่อันดับถัดมาคือเราไม่แน่ชัดด้วยซ้ำว่าความแตกต่างนั้นเกิดจากอะไร และคงที่แค่ไหนเมื่อเวลาผ่านไป ตัวบทความเองเขียนได้ดีและเจาะลึกประโยชน์เชิงปฏิบัติของกลยุทธ์การสอนบางแบบได้ดี แต่ผมอยากให้มี ความถ่อมตน มากกว่านี้สักหน่อย
ไม่ถึง 2 สัปดาห์ก่อนก็มีบทความคล้าย ๆ กัน: https://news.ycombinator.com/item?id=41566097
สุดท้ายแล้ว การเขียนเพื่อมนุษย์ย่อเหลือทักษะ 2 อย่าง คือ ความเห็นอกเห็นใจ และ การเขียน
มีความแตกต่างอย่างมากระหว่างการเขียนโค้ดนิดหน่อย กับการเขียนแอปพลิเคชันหรือผลิตภัณฑ์ บทความนี้ก็พูดเรื่องนั้นในท้ายที่สุด เพียงแต่ไม่โจ่งแจ้งเท่า เหตุผลที่ความเห็นอกเห็นใจสำคัญก็เพราะมันสร้างความต่างระหว่างการยึดตัวเองเป็นศูนย์กลางกับการมองออกไปภายนอก
นักพัฒนาที่เอาตัวเองเป็นศูนย์กลางมักสนใจความง่าย ความสะดวก ความอวดโค้ด และเกณฑ์เชิงอัตวิสัยอื่น ๆ สุดท้ายก็วัดแค่ความพยายามในการสื่อสารของตัวเอง ส่วนนักพัฒนาที่มองออกไปภายนอกมักสนใจสถาปัตยกรรมและการทำเอกสาร เพราะมองว่าความสำเร็จขึ้นอยู่กับว่าคนอื่นรับผลงานของตนอย่างไร
ความเรียบง่ายสำคัญกว่าความง่าย เพราะนักพัฒนาที่มองออกไปภายนอกอ่านใจคนอื่นไม่ได้ และไม่รู้ว่าอะไรที่คนอื่นจะรู้สึกว่าง่าย แต่รู้วิธีลดจำนวนขั้นตอนและทำให้โค้ดเล็กไว้ได้
ในมุมมองของผลิตภัณฑ์ทั้งตัว การเขียนแอปพลิเคชันในสมองแล้วไม่ต่างจากการเขียนเรียงความ บทความ หรือหนังสือ แก่นคือการจัดระเบียบและฟังก์ชัน โค้ดเป็นสิ่งที่ตามมาทีหลัง เหมือนคำบนหน้ากระดาษ คนที่เขียนแค่ชิ้นส่วนโค้ดจะไม่ได้พัฒนาทักษะการจัดระเบียบระดับสูงที่ผูกทุกอย่างเข้าด้วยกัน
เพราะอย่างนั้นจึงเกลียดเฟรมเวิร์กมาก เฟรมเวิร์กพรากการฝึกฝนที่นักพัฒนาต้องมีเพื่อเขียนซอฟต์แวร์ที่เป็นต้นฉบับของตนเอง และผลคือทำให้ไม่สามารถพัฒนาทักษะการจัดระเบียบได้ เป็นช่องว่างขนาดใหญ่ที่เจ้าตัวมองไม่เห็น แต่สำหรับคนที่มองเห็นแล้วชัดเจนอย่างยิ่ง
แต่คนอื่น ๆ ตอนนี้ต้องเรียนรู้ นามธรรม ของพวกเขา และยิ่งห่างจากแนวคิดพื้นฐานออกไปเท่านั้น นั่นอาจทำให้ยากขึ้นที่จะฝึกทักษะแกนหลักที่จำเป็นต่อการก้าวข้ามเฟรมเวิร์ก ตอนเรียน Rails ก็รู้สึกแบบนั้น และสุดท้ายพอรู้ว่ามันซ่อนอะไรไว้มากเกินไปก็เลิกใช้ แล้วลองทำตั้งแต่ต้นเอง
การตระหนักว่านี่เป็นทักษะที่ต่างกันโดยสิ้นเชิงเป็นเรื่องเปิดหูเปิดตามาก พูดได้ว่าตอนนี้มันกลายเป็นสิ่งที่ไม่รู้ซึ่งเรารู้แล้วว่ามีอยู่
โค้ดนี้จะดูเป็นอย่างไรในสายตาของคนที่กำลังถูกหัวหน้าบีบคอ หรือคนที่แก้ปัญหาระบบปฏิบัติการตอนตี 2? คุณจะไม่รู้ว่าคำตอบนั้นมีค่าแค่ไหนจนกว่าจะต้องใช้มันจริง ๆ และทันทีที่ต้องใช้ คุณจะยอมจ่ายเงินก้อนใหญ่เพื่อคำตอบนั้น ถ้าหาคนที่ทำแบบนั้นเป็นเจอได้ล่ะก็ ซึ่งคนแบบนั้นหายาก
ไม่เห็นด้วยกับคำว่า “มนุษย์เรียนรู้จากตัวอย่าง ไม่ใช่จากแนวคิดแกนกลาง” อาจเป็นการจับผิดก็ได้ แต่ไม่ใช่มนุษย์ทุกคนที่ทำงานแบบนั้น
คนที่ชอบไล่จากภาพทั่วไปไปสู่รายละเอียดเฉพาะเจาะจงถูกระบบการศึกษาประถมและมัธยมเพิกเฉยเป็นส่วนใหญ่อยู่แล้ว และอาจเพิ่งเริ่มเข้าที่ในระดับอุดมศึกษา พวกเขาถูกทำให้แปลกแยกมากพอแล้ว จึงไม่จำเป็นต้องถึงขั้นปฏิเสธการมีอยู่ของพวกเขา
ผมไม่เข้าใจนัยละเอียดว่าตอนไหนต้องทำอะไร อะไรต้องทำพร้อมกันเป๊ะ และอะไรต้องทำตามมาทันที ตอนนั้นพ่อของแฟนอธิบายสั้น ๆ ว่าคลัตช์ทำอะไรจริง ๆ และการเชื่อมต่อระหว่างล้อกับเครื่องยนต์ส่งผลต่อทั้งสองฝั่งอย่างไร
ทันทีนั้นผมเข้าใจเลย และไม่ต้องมีใครสั่งว่าต้องทำอะไรในสถานการณ์เฉพาะอีก ประมาณ 20 นาทีต่อมา ผมก็ขับรถออกตัวบนทางลาดที่เอียงไปด้านหลังด้วยเบรกมือได้ ซึ่งว่ากันว่ายากที่สุดอย่างหนึ่งของเกียร์ธรรมดา สำหรับบางคน การเข้าใจวิธีทำงานจาก หลักการพื้นฐานข้อแรก มีประโยชน์กว่ามาก และผมคิดว่าในหมู่วิศวกรซอฟต์แวร์มี “บางคน” แบบนั้นอยู่ไม่น้อย
ถ้ามีส่วนที่น่าประหลาดใจในตัวอย่าง แปลว่าโมเดลของผมยังไม่สมบูรณ์ หรือไม่ก็ตัวอย่างผิด
ไม่ใช่ “สิ่งที่เราพยายามทำให้สำเร็จคือสิ่งนี้ สิ่งนี้ทำงานแบบนี้ และเราทำแบบนี้” แต่สิ่งที่คนปฏิบัติงานเห็นเสมอมีแค่ “เราทำแบบนี้” พอแตกต่างไปแม้เพียงเล็กน้อย ก็ไม่สามารถให้เหตุผล ปรับแก้ และแก้ปัญหาได้
งานที่ทำบ่อย ๆ ก็มีเอกสารอยู่บ้าง แต่ส่วนใหญ่มักเก่าหรือไม่สมบูรณ์ และไม่ใช่วิกิ จึงไม่ใช่ว่าใครจะแก้เมื่อไรก็ได้ การจะแก้เอกสารต้องผ่านกระบวนการน่ารำคาญ สุดท้ายจึงไม่ถูกอัปเดต พอมาคิดดูแล้วก็คล้ายกับสมัยอยู่ในกองทัพไม่น้อย
ตอนนี้ก็เจอแบบนั้นอยู่ขณะหาเวลาเรียน Drizzle ORM เอกสารที่เจอแรก ๆ ทั้งหมดเป็น “ตัวอย่าง query หกแบบ” แล้วก็หงุดหงิดว่าทำไมต้องใช้ไวยากรณ์นั้น ทางเลือกอื่นมีอะไรบ้าง ผมปิดสื่อแบบนั้น แล้วอ่านทุกหน้าของเอกสารก่อนค่อยลงมือทำ ซึ่งเป็นวิธีที่สบายกว่าสำหรับผมมาก
ตอนนี้ก็ยังไม่แน่ใจว่าจะทำแบบเรียลไทม์ได้ไหม วิธีนี้ใช้รอบการคิดค่อนข้างมาก ดังนั้นตอนนี้ความเร็วที่เหมาะกว่าคืออ่านบทความหรือหยุดวิดีโอไว้แล้วประมวลผล
บ่อยครั้งผมต้องสอนคนที่ยังไม่เข้าใจแบบทันที ถ้ามีทฤษฎีเกี่ยวกับระบบอยู่ ก็จะตอบคำถามที่เพื่อนร่วมชั้นซึ่งผ่านพ้นการท่องจำแบบพื้น ๆ มาได้อย่างหวุดหวิดตอบไม่ได้
เป็นประโยคจาก Code Complete: “ส่วนเล็ก ๆ ของงานเขียนโปรแกรมคือการเขียนโปรแกรมให้คอมพิวเตอร์อ่านได้ ส่วนที่ใหญ่กว่าคือการเขียนให้มนุษย์คนอื่นอ่านได้” หน้า 733
ยังจำได้ติดใจมาเกือบ 20 ปี
เป็นคำในคำนำของ Structure and Interpretation of Computer Programs ฉบับพิมพ์ครั้งแรก โดย Abelson และ Sussman และเกิดก่อน Code Complete 10 ปี
เป็นคติพจน์ที่ผมพยายามยึดถือ แต่น่าแปลกที่นายจ้างดูเหมือนจะยืนกรานอยู่เสมอกับส่วนที่คอมพิวเตอร์นำไปประมวลผล
อาจจะออกนอกประเด็นไปหน่อย แต่เมื่อไม่กี่วันก่อนตอนทำเกม Unity อยู่ ผมเกิดคิดขึ้นมาว่า IDE แทบไม่ได้พัฒนาขึ้นมากนักเลยในช่วง 10–20 ปีที่ผ่านมา
IntelliSense พื้นฐานดีขึ้นมากจริง ๆ แต่ถ้าไม่นับจุดเล็ก ๆ น้อย ๆ อีกไม่กี่อย่าง แนวคิดทั้งหมดของการเขียนโค้ดก็ดูแทบเหมือนเดิมกับเมื่อก่อน
การเปลี่ยนแปลงเชิงบวกที่ใหญ่ที่สุดอยู่นอกตัวเอดิเตอร์ เราเข้าถึงไลบรารีและเอกสารได้ง่ายขึ้นมาก มีคำถามและคำตอบจากผู้ใช้จำนวนมหาศาล และบางครั้งก็มีเครื่องมือใหม่อย่าง ChatGPT ที่รวบรวมคำตอบเหล่านั้นมาให้คำตอบที่ดูเข้าท่าได้
แต่โดยรวมแล้ว การเขียนโค้ดดูเหมือนจะหยุดนิ่งอยู่กับที่ ดังนั้นตอนนี้ผมหยุดงานเกมไว้ชั่วคราว แล้วกำลังทดลองบางอย่างอยู่ ผมไม่ได้อยากสร้างภาษาใหม่ แต่อยากโยนงานจุกจิกทั้งหมดเท่าที่เป็นไปได้ให้คอมพิวเตอร์ เพื่อจะได้โฟกัสกับการสร้างสรรค์
สามอย่างแรกที่อยากทดสอบมีดังนี้ ทำไมผมต้องใส่ใจกับรายละเอียดเล็ก ๆ ของภาษาอย่างวงเล็บหรือตัวปิดคำสั่งด้วย เครื่องมือเติมให้อัตโนมัติไม่ได้หรือ ตัวปรับแต่งอย่าง private-public access chain หรือ unsafe เครื่องมือก็ตัดสินชุดที่มีประสิทธิภาพที่สุดให้อัตโนมัติไม่ได้หรือ เวลาผมโฟกัสกับเมธอดสัก 5 ตัวที่โต้ตอบกันอยู่ ผมอยากเห็นทั้งหมดบนหน้าจอเดียวโดยไม่ต้องเปิดหลายหน้าต่างและสู้กับแถบเลื่อนแนวนอน/แนวตั้งของ VS ถ้าผมสร้าง HashSet ไว้แล้วต้องเปลี่ยนเป็น Dictionary หรือ Tuple ก็อยากให้มันเปลี่ยนให้เลย แล้วแสดงเฉพาะจุดที่ต้องตัดสินใจเพื่อให้ผมอนุมัติหรือแก้เอง ใน Unity ผมก็อยากให้สามารถคลิกเมธอดหรือชุดข้อมูล แล้วสั่งให้แปลงเป็น Burst Job พร้อมชุด NativeData ที่เกี่ยวข้องได้ด้วย
แต่สุดท้ายแล้วทั้งหมดก็คือ abstraction และเราก็แค่เขียนคำสั่งให้เครื่องจักรที่โง่มาก ๆ คำนวณข้อมูลเท่านั้น
คุณบอกว่าเครื่องมือก็น่าจะเติมวงเล็บหรือตัวปิดคำสั่งให้อัตโนมัติได้ แต่คอมพิวเตอร์เป็นสิ่งที่เรียบง่ายจริง ๆ และภาษาโปรแกรมคือช่องทางถ่ายทอดความคิดในหัว ตัวคั่นเหล่านั้นสำคัญพอ ๆ กับคีย์เวิร์ดของภาษา เพราะมันเป็นส่วนหนึ่งของกฎ หากจะเติมมันให้อัตโนมัติ ก็ต้องมีกฎมากขึ้นและตัวคั่นมากขึ้น
ถ้าอยากเห็นเมธอดหลายตัวที่โต้ตอบกันอยู่บนหน้าจอเดียว ก็มี Vim กับ Emacs หรือ IDE ของ Smalltalk อย่าง Pharo
การแปลงข้อมูลทำได้ด้วยมาโครของ Vim และ Emacs แต่ความจริงคือการเข้ารหัสข้อมูลสำคัญมาก สำหรับคอมพิวเตอร์แล้วทุกอย่างคือบิต และเราเป็นคนกำหนดความหมายให้บิตเหล่านั้น แล้วสร้างกฎสำหรับจัดการตามความหมายนั้น หากจะเปลี่ยนรูปจากชุดกฎหนึ่งไปเป็นอีกชุดกฎหนึ่ง ก็ต้องมีกฎเพิ่มขึ้นอีก
แนะนำให้ลองใช้ สภาพแวดล้อมการเขียนโปรแกรมแบบไลฟ์ เช่น SLIME ของ Common Lisp, Pharo ของ Smalltalk, หรือ web inspector ของ JavaScript มันให้ความรู้สึกเหมือนทำงานบนเรือที่อยู่กลางทะเล แทนที่จะวางเรือไว้บนบกแล้วจินตนาการว่าการเดินเรือจะรู้สึกอย่างไร
ส่วนที่ยากที่สุดของการเขียนโปรแกรมคือ การคิดและการเรียนรู้ การพิมพ์ให้เร็วขึ้นไม่ได้ช่วยอะไรมากนัก
ตัวอย่างเช่น ตอนเขียนโปรแกรม C สามารถให้ “f” ขยายเป็น “for (=; <=; ++) {;}” หรือรูปแบบการย่อหน้าที่ชอบได้
เอดิเตอร์สำหรับเขียนโปรแกรมสมัยใหม่จำนวนมากก็รองรับการตั้งค่าแบบคล้ายกัน แต่น่าเสียดายที่หลายครั้งขั้นตอนกลับซับซ้อนกว่าเมื่อสมัยนานมาแล้วมาก
ถ้าเป็นภาษาโปรแกรมที่ไวยากรณ์ยืดยาว ผมคิดว่าควรสละเวลานิยาม เทมเพลต เพื่อให้เขียนโครงสร้างโปรแกรมใด ๆ ได้อย่างรวดเร็วด้วยจำนวนการกดแป้นพิมพ์น้อยที่สุดในเอดิเตอร์
ปัญหาอย่างควรใช้ HashSet, Dictionary หรือ Tuple มีผลต่อประสิทธิภาพ และในเชิงนามธรรมก็ไม่ได้ชัดเจนเสมอว่าควรใช้อันไหน ภาษาที่ explicit อย่าง Java และน่าจะ C# ด้วย อาจรีแฟกเตอร์ให้เมธอดคอลรับชนิดอื่นได้ จากนั้นก็เปลี่ยนเมธอดหนึ่งตัวแล้วรีแฟกเตอร์การเรียกทั้งหมด
ผมลอง Gemini pro กับ ChatGPT o1 แล้ว ทั้งคู่เขียนโค้ด Python และ JavaScript ได้แย่มาก เขียนโค้ดที่มีบั๊ก และบ่อยครั้งพยายามแก้บั๊กหนึ่งแต่ใส่อีกบั๊กเข้ามา ทั้งคู่ให้ความรู้สึกว่ารีบตอบมากกว่าคิดถึงข้อกำหนด ผมคิดว่าเรายังห่างจากเครื่องมือที่ “อ่านใจ” หรือเข้าใจว่าอะไรสำคัญและไม่สำคัญในแบบที่เราต้องการอยู่พอสมควร
สิ่งที่อาจแย่กว่านั้นคือข้อมูลฝึก เนื่องจากโค้ดส่วนใหญ่ถูกสร้างโดยคนเขียนโค้ดระดับต่ำกว่าค่าเฉลี่ยและระดับเฉลี่ย เครื่องมือเหล่านี้จึงรับรูปแบบความคิดของคนเขียนโค้ดระดับเฉลี่ยมา แม้จะฝึกด้วยโค้ดคุณภาพสูงสุดเท่านั้น ก็ยังไม่ชัดเจนว่าคนเขียนโค้ดส่วนใหญ่จะสามารถพรอมป์ได้ถูกต้องหรือไม่ ดังนั้นถ้าคุณเขียนโค้ดมา 10–20 ปีแล้ว ก็มีโอกาสสูงทีเดียวที่จะผิดหวังกับเครื่องมืออยู่เสมอ หากคาดหวังเวทมนตร์แบบทันที
ถึงอย่างนั้น เครื่องมือวิเคราะห์แบบสถิต ที่ไม่ใช่ AI ก็ยอดเยี่ยมมานานแล้วและจะดีขึ้นอีก การเพิ่ม AI เข้าไปอาจทำให้มันดียิ่งขึ้นได้ ถ้ามองเครื่องมือว่าเป็นสิ่งที่ช่วยให้ผมเป็นศิลปิน ไม่ใช่ศิลปินที่โยนสเปกเข้าไปแล้วรับผลลัพธ์พอใช้กลับมา ผมคิดว่าจะได้ประสบการณ์ที่ยอดเยี่ยม
การทดลองบอก AI ว่าอยากให้เอดิเตอร์ทำอะไรเพิ่ม แล้วให้ช่วยตั้งค่า ก็น่าสนุกได้เช่นกัน ในปลั๊กอินมีเครื่องมือที่ไม่ใช่ AI อยู่มากมาย การใช้โมเดลภาษาขนาดใหญ่เพื่อเลือกปลั๊กอินที่เข้ากับวิถีการทำงานของตัวเอง อาจเป็นวิธีที่คุ้มค่าที่สุด
https://haystackeditor.com/
ยังไม่ได้ลองใช้เอง แต่ตั้งใจว่าจะลอง
ชื่อบทความมีข้อให้ถกเถียงได้ เพราะโค้ดถูกเขียนขึ้นเพื่อมนุษย์เท่านั้น คอมพิวเตอร์ไม่ต้องการ “โค้ด” และโดยเฉพาะ โค้ดระดับสูง ยิ่งไม่จำเป็นเลย คอมพิวเตอร์มีเพียงคำสั่งภาษาเครื่องก็เพียงพอแล้ว
เหตุผลที่เราเขียนโค้ดก็เพราะคำสั่งภาษาเครื่องยากเกินไปสำหรับมนุษย์ที่จะเขียน และยากยิ่งกว่าสำหรับการอ่าน
ไม่ควรมองโค้ดว่าเป็นวิธีโต้ตอบกับคอมพิวเตอร์ โค้ดคือวิธีที่มนุษย์ทำให้ความคิดเป็นรูปแบบทางการ จนไม่กำกวมพอที่แม้แต่เครื่องจักรก็ยังทำตามได้
ขอโปรโมตบล็อกโพสต์ที่เขียนและแชร์เมื่อสัปดาห์ก่อนแบบเห็นแก่ประโยชน์ส่วนรวม
Move Fast & Document Things [1]
ไม่ได้ตั้งใจจะเขียนบทความเชิงปรัชญา แต่ตั้งใจจะแชร์เคล็ดลับที่ใช้ได้จริงว่า ทีมเล็ก ๆ ของเรา [2] บังคับใช้ วัฒนธรรมการเขียนโค้ด เพื่อตัวเราเองและเพื่อกันและกัน ผ่านการรีวิวที่ลึกและยาก ไม่ใช่ผ่าน automation หรือ AI อย่างไร
เพื่อนส่วนตัวที่เป็น engineering leader ในองค์กรอื่น ๆ ต่างก็บอกว่า “พวกเราก็ทำแบบเดียวกัน แต่คุณเป็นคนที่เขียนมันออกมาเป็นบทความจริง ๆ” ถ้ามันมีคุณค่า ก็ขอให้ช่วยแนะนำด้วย
[1] https://olshansky.substack.com/p/move-fast-and-document-thin...
[2] https://github.com/pokt-network/poktroll/graphs/contributors
“หนังสือและบทเรียนสอนเขียนโปรแกรมจำนวนมากเกินไปมักเป็นแนว ‘มาสร้างบ้านตั้งแต่ต้นด้วยอิฐทีละก้อนกันเถอะ’ แต่สิ่งที่ผมต้องการคือ ‘นี่คือบ้านที่ใช้งานได้อยู่แล้ว ลองเปลี่ยนอะไรสักอย่างแล้วดูว่าเกิดอะไรขึ้น’”
ผมเรียนเขียนโปรแกรมด้วยตัวเองด้วยวิธีแบบนั้น ใช้เวลาหลายปีกับการใช้โปรแกรมเล็ก ๆ เรียบง่าย และค่อนข้างแย่ให้ได้คล่อง
ภายหลังจึงพบว่าตัวเองไม่เหมาะกับงานพัฒนาซอฟต์แวร์ที่ดีกว่า เพราะไม่มีพื้นฐานเรื่อง การออกแบบซอฟต์แวร์, ภาษาโปรแกรมมิง หรือคอมพิวเตอร์เลย การเดินออกจากห้องสัมภาษณ์แล้วตระหนักว่าตัวเองไม่รู้อีกมากแค่ไหน เพราะไม่ได้เรียนด้วยวิธีที่น่าเบื่อ เป็นประสบการณ์ที่ทำให้ถ่อมตัวลง
ควรอ่านคู่มือทั้งหมดเสมอ และควรเรียนพื้นฐานเสมอ
โค้ดทั้งหมดของผมเขียนขึ้น เพื่อมนุษย์
ไม่ว่ามนุษย์คนนั้นจะเป็นตัวผมเอง หรือใครสักคนที่น่าสงสารซึ่งต้องมาทำความเข้าใจเจตนาของผมในอีกหลายปีข้างหน้าก็ตาม
ผมมองว่าการเขียนโค้ดเองไม่ใช่เรื่องยาก ส่วนที่แสดงให้เห็นถึงพรสวรรค์คือการให้เหตุผลกับปัญหาอย่างครอบคลุม การร่วมมือกับผู้มีส่วนได้ส่วนเสียคนอื่น ๆ เพื่อค้นหาเส้นทางที่ดีที่สุดและนำพาพวกเขาไป การเรียนรู้ทักษะเฉพาะทางอย่างคณิตศาสตร์ใหม่ ๆ หรือแนวปฏิบัติของอุตสาหกรรม การคิดค้นอัลกอริทึมที่มีประสิทธิภาพ และการสื่อสารให้โครงสร้างกับแพตเทิร์นของโปรแกรมมีขอบเขตที่ชัดเจนและสง่างาม
ท้ายที่สุด หลายส่วนขึ้นอยู่กับ การสื่อสารและความชัดเจน
ส่วนใหญ่ของบทความนี้เกี่ยวกับการทำเอกสาร และคงช่วยได้มากถ้าอ้างถึงโมเดล 4doc: https://docs.divio.com/documentation-system/
โดยพื้นฐานแล้วคืออย่าให้แค่เอกสารอ้างอิง แต่ให้เอกสารวิธีใช้งานด้วย และเพราะนั่นมักเป็นส่วนที่ผู้ใช้โดยทั่วไปอยากดูก่อนในเอกสารทั้งหมด จึงควรวางมันไว้ข้างหน้า
แน่นอนว่านี่เป็นเรื่องโดยทั่วไป ส่วนตัวผมมักตรงไปที่เอกสารอ้างอิงเลย แต่ก็ไม่ใช่เสมอไป
ไม่ได้หมายความว่า 4doc เป็นคำตอบสารพัดปัญหาหรือกฎธรรมชาติ Hillel Wayne ก็อธิบายปัญหาของมันไว้ดีที่นี่: [https://www.hillelwayne.com/post/problems-with-the-4doc-mode...](https://www.hillelwayne.com/post/problems-with-the-4doc-model/