วิธีทำโปรเจกต์พังด้วยการคิดมากเกินไป ขยายขอบเขต และใช้ structural diff
(kevinlynagh.com)- โปรเจกต์มักแยกออกเป็นสองทางได้ง่าย ๆ คือ ลงมือทำให้เสร็จทันที กับทางที่การค้นคว้าและการออกแบบค่อย ๆ บานปลายจนหลุดจากปัญหาตั้งต้น และในแง่ความคืบหน้าจริง หลายครั้งฝั่ง ลองทำไปเลย กลับไปได้ไกลกว่า
- ชั้นวางของในครัวเสร็จภายในสุดสัปดาห์ แต่การสำรวจเวิร์กโฟลว์ structural diff และไอเดียเรื่องภาษา·CAD ที่ลากยาวมานาน แม้จะผ่านการค้นคว้าและทำต้นแบบมามาก ก็ยังไม่กลายเป็นผลงานที่แก้แรงจูงใจตั้งต้นได้โดยตรง
- ระหว่างทำ fuzzy path search สำหรับ Emacs, ฟีเจอร์เสริมของไลบรารีที่ดี กลับก่อให้เกิดความต้องการใหม่ ทำให้การออกแบบพองตัว สุดท้ายต้องทิ้งโค้ดฟังก์ชัน anchor ที่ไม่จำเป็นทั้งหมด และกลับมายืนยัน YAGNI อีกครั้ง
- ในการทำ code diff นั้น การเทียบแบบรายบรรทัดจับโครงสร้างระดับบนอย่างฟังก์ชันหรือ type ได้ไม่ดีนัก และแม้แต่เครื่องมือที่อิง treesitter ก็อาจอ่านยากได้ถ้าการจับคู่ entity คลาดเคลื่อน เพราะมันจะแสดงการลบและเพิ่มยาว ๆ แทน
- ทิศทางที่จำเป็นคือ เริ่มจากทำเครื่องมือขอบเขตเล็กที่เหมาะกับ การรีวิวผลลัพธ์ LLM แบบเป็นรอบ ก่อน โดยเริ่มจากการดึง entity สำหรับ Rust และการจับคู่ง่าย ๆ เพื่อดู ภาพรวมการเปลี่ยนแปลงระดับสูง ได้อย่างรวดเร็ว
การคิดมากเกินไปและการขยายขอบเขต
- โปรเจกต์มักแยกเป็นสองทางได้ง่าย: ทางหนึ่งคือ ลงมือทำแล้วปิดงานให้เสร็จ อีกทางคือขุดคุ้ยตัวอย่างเดิม ๆ จนขอบเขตบานปลาย สุดท้ายกลับไม่ได้แก้ปัญหาตั้งต้น
- ชั้นวางของในครัวที่ทำช่วงสุดสัปดาห์นั้น เริ่มจากนั่งดื่มกาแฟแล้วตัดสินใจเรื่องแบบ ปรับที่แขวนพิมพ์ 3D อยู่ไม่กี่รอบ จากนั้นใช้วัสดุและสีที่เหลืออยู่ทำจนเสร็จภายในสุดสัปดาห์
- CAD สำหรับที่แขวน Ikea bin เปิดเผยไว้ที่ OnShape CAD
- วัสดุนำของเหลือจากเวิร์กช็อปกลับมาใช้ใหม่ และลบมุมแบบกะสายตาด้วย palm sander
- สำหรับชั้นวางนี้ เกณฑ์ความสำเร็จหลักไม่ใช่การทำของที่พอดีกับครัวอย่างแม่นยำ แต่คือการ สนุกกับงานไม้ร่วมกับเพื่อน ทำให้ไม่ต้องกังวลกับเกณฑ์รายละเอียดมากเกินไป
- ตรงกันข้าม ตอนหาเครื่องมือ structural diff นั้น หลังรู้สึกว่าผลของ difftastic ยังไม่น่าพอใจ ก็ใช้เวลา 4 ชั่วโมงค้นหาเครื่องมือและเวิร์กโฟลว์ที่เกี่ยวข้อง แต่สุดท้ายก็ย้อนกลับมาที่เกณฑ์ตั้งต้นว่าแค่อยากได้เวิร์กโฟลว์ diff ที่ดีกว่าเดิมใน Emacs
- ความสนใจระยะยาวอย่างอินเทอร์เฟซสำหรับ hardware prototyping, ภาษาที่ผสม Clojure กับ Rust, หรือภาษาสำหรับ CAD ใช้เวลาหลายร้อยชั่วโมงกับการหาข้อมูลพื้นหลังและต้นแบบเล็ก ๆ แต่ก็ยังไม่ไปถึงผลงานที่แก้แรงจูงใจแรกเริ่มได้โดยตรง
- อินเทอร์เฟซสำหรับ hardware prototyping อยู่ใน กันยายน 2023, งานออกแบบภาษาอยู่ใน พฤศจิกายน 2023, และไอเดียเกี่ยวกับ CAD ต่อเนื่องไปยัง constraints, bidirectional editing, other dubious ideas
- ในโปรเจกต์ภาษาและ CAD นั้น เกณฑ์ความสำเร็จยังพร่าเลือน เช่น จะใช้แทน Rust หรือ Clojure หรือไม่ จะรับมือแค่บางปัญหาหรือเปล่า ถ้าเป็น playground เพื่อการเรียนรู้จะพอไหม จะไปแทน CAD เชิงพาณิชย์หรือไม่ หรือจำเป็นต้องมีประโยชน์กับคนอื่นด้วยหรือเปล่า
- คำถามเหล่านี้มีคุณค่าที่จะพิจารณา แต่แทนที่จะเอาเวลาไปพิจารณาหลายเรื่องมากเกินไป ผู้เขียนมองว่าการลงมือสร้างจริงให้มากกว่าน่าจะดีกว่า
- ต่อให้มองย้อนกลับไปแล้วได้ผลลัพธ์ที่ชัดเจนว่าไม่ดีนัก การ ลองทำไปเลย ก็ยังทำให้โดยรวมเดินหน้าได้มากกว่า
กฎอนุรักษ์ของการขยายขอบเขต
- เวลาที่ใช้ไปกับการทำแบบไม่คิดมากก็มีขีดจำกัดและต้องมีสมดุล และประสบการณ์ที่เคยปล่อยให้ LLM agent เขียนโค้ดจำนวนมากแล้วสุดท้ายต้องทิ้งทั้งหมด ก็ทำให้นึกถึง YAGNI อีกครั้ง
- ผู้เขียนอยากทำ fuzzy path search ทั้งระบบไฟล์ในสไตล์ Finda สำหรับใช้ใน Emacs และเคยเขียนฟังก์ชันแบบเดียวกันด้วยมือล้วนมาก่อน จึงคิดว่าถ้าคอยกำกับ LLM ก็น่าจะเสร็จได้ในไม่กี่ชั่วโมง
- ตอนแรก ในบทสนทนาเพื่อวางแผน ได้รับคำแนะนำให้ใช้ Nucleo ซึ่งออกแบบและทำเอกสารมาดี จึงเลือกใช้เพื่อให้ได้ฟีเจอร์ smart case และ Unicode normalization
- ตัวอย่างเช่น query
fooจะจับได้ทั้งFooและfooแต่Fooจะไม่จับfoo - การจัดการ
cafeและcaféก็อยู่ในบริบทเดียวกัน
- ตัวอย่างเช่น query
- ปัญหาไม่ใช่ตัวไลบรารีที่ดี แต่เป็นเพราะ Nucleo รองรับฟังก์ชัน anchor ด้วย
- เนื่องจากใน corpus ที่มีแต่ file path นั้น anchor ต้นบรรทัดดูไม่ค่อยมีประโยชน์ ผู้เขียนจึงพยายามตีความมันเป็น anchor ตาม path segment แทน
- เช่น
^fooควรจะจับ/root/foobar/แต่ไม่จับ/root/barfoo/
- เช่น
- ถ้าจะทำให้มีประสิทธิภาพ index ก็ต้องเก็บขอบเขตของแต่ละ segment และต้องตรวจสอบ query กับแต่ละ segment ได้อย่างรวดเร็ว
- ยังต้องรองรับ anchor query ที่มี slash อย่าง
^foo/barด้วย ซึ่งทำให้การตรวจสอบแค่ระดับ segment ไม่พอจะจับ path อย่าง/root/foo/bar/baz/ได้ถูกต้อง - ผู้เขียนใช้เวลาเพิ่มอีกหลายชั่วโมงกับการออกแบบนี้ แลกไอเดียกับ LLM เขียนโค้ดห่อ Nucleo type ต่าง ๆ แล้วสุดท้ายรู้สึกว่าโค้ดบวมเกินไปและไม่ชอบ จึงกลับมาเขียน wrapper ที่เล็กกว่าขึ้นใหม่เอง
- หลังพักไปแล้วก็พบว่านึกไม่ออกเลยว่าใน Finda เคยต้องใช้ฟังก์ชัน anchor เมื่อไร และใน corpus แบบ path นั้น การใส่
/ไว้หน้าหรือท้าย query ก็ใช้แทนบทบาท anchor ได้เกือบทั้งหมด- ข้อยกเว้นมีแค่ anchor สำหรับท้ายชื่อไฟล์
- สุดท้ายจึงลบโค้ดที่เกี่ยวกับ anchor ทิ้งทั้งหมด และก็ยังไม่แน่ใจว่ามันคุ้มกว่าการลงมือเขียนเองตั้งแต่แรกโดยไม่ต้องคุยกับ LLM หรือคนอื่นหรือไม่
- ดูเหมือนยิ่งความเร็วในการเขียนโปรแกรมสูงขึ้น ก็จะมีกฎอนุรักษ์บางอย่างที่ทำให้ ฟีเจอร์ไม่จำเป็น, rabbit hole, และ ทางอ้อม เพิ่มขึ้นตามไปด้วย
การทำ structural diff
- ในโค้ด คำว่า diff โดยทั่วไปหมายถึง สรุปการเปลี่ยนแปลงแบบรายบรรทัด ระหว่างไฟล์สองเวอร์ชัน โดยใน unified view จะแสดงการเพิ่มและการลบด้วย
+,- - diff เดียวกันสามารถแสดงแบบเทียบซ้ายขวาได้ด้วย และยิ่งการเปลี่ยนแปลงซับซ้อน รูปแบบนี้ก็อาจอ่านง่ายกว่า
- ปัญหาของ diff แบบรายบรรทัดคือมันไม่เข้าใจโครงสร้างระดับบนอย่างฟังก์ชันหรือ type และถ้าวงเล็บปีกกาดันลงตัวพอดี มันก็อาจละการแสดงผลแม้ว่าจะอยู่คนละฟังก์ชันกันก็ตาม
- difftastic พยายามลดปัญหานี้โดยใช้ concrete syntax tree จาก treesitter แต่การจับคู่ entity ระหว่างเวอร์ชันก็ไม่ได้เวิร์กเสมอไป
- ใน diff ที่เป็นชนวนโดยตรงนั้น
struct PendingClickไม่ถูกจับคู่กันระหว่างสองฝั่ง โดยฝั่งซ้ายถูกแสดงเป็นการลบ และฝั่งขวาเป็นการเพิ่ม - ผู้เขียนไม่ได้ขุดลงไปว่าทำไมการจับคู่ถึงล้มเหลว แต่ตัดสินใจว่าถึง diff โดยรวมจะยาวขึ้น ก็ยังดีกว่าถ้าได้เห็น
PendingClickRequestและPendingClickถูกจับคู่กันข้ามสองฝั่ง
เครื่องมือ structural diff และแหล่งอ้างอิง
- เครื่องมือ semantic diff ที่มองว่าสมบูรณ์และขัดเกลามากที่สุดคือ semanticdiff.com
- ให้บริการโดยบริษัทเล็ก ๆ จากเยอรมนี มีปลั๊กอิน VSCode ฟรี และมีเว็บแอปสำหรับแสดง GitHub PR diff
- แต่ไม่ได้มีไลบรารีโค้ดที่หยิบไปใช้เป็นฐานของเวิร์กโฟลว์ที่ต้องการได้
- บทความ semanticdiff vs. difftastic มีรายละเอียดที่เป็นประโยชน์มาก รวมถึงปัญหาที่ difftastic ไม่สามารถแสดงแม้แต่ การเปลี่ยนแปลง indentation ที่มีความหมาย ใน Python
- ผู้เขียนคนหนึ่งระบุใน คอมเมนต์ HN ว่าเคยใช้ treesitter กับงานเชิงความหมายก่อนจะเลิกใช้ไป เพราะ parsing ล้มเหลวได้จาก keyword ที่ขึ้นกับบริบทและพฤติกรรมของ lexer จนเครื่องมืออาจหยุดทำงานแม้กระทั่งเมื่อใช้ชื่ออย่าง
asyncเป็นพารามิเตอร์
- diffsitter เป็นเครื่องมือบนฐาน treesitter และมี MCP server รวมมาด้วย
- แม้จะมี GitHub star มาก แต่เอกสารดูไม่ค่อยดีนัก และหาเนื้อหาที่อธิบายวิธีทำงานได้ยาก
- ในวิกิของ difftastic ระบุว่าใช้ longest-common-subsequence กับ leaf ของ tree
- gumtree เป็นเครื่องมือจากพื้นหลังงานวิจัยและวิชาการในปี 2014
- ต้องใช้ Java จึงไม่เหมาะกับการใช้งานส่วนตัวที่อยากได้เครื่องมือเรียกใช้เร็วใน Emacs
- mergiraf เป็น merge-driver บนฐาน treesitter ที่เขียนด้วย Rust
- หน้า architecture overview จัดทำไว้ดี และภายในใช้ algorithm ของ Gumtree
- จากเอกสารและภาพประกอบ ให้ความรู้สึกว่าเป็นโปรเจกต์ที่ทำอย่างพิถีพิถัน
- ผู้เขียน semanticdiff.com ระบุใน คอมเมนต์ HN ว่า GumTree ให้ผลลัพธ์ได้เร็ว แต่แม้จะใส่การปรับปรุงจากงานวิจัยต่อ ๆ มา ก็ยังมีกรณีจำนวนไม่น้อยที่คืนการจับคู่แย่ ๆ อยู่เสมอ สุดท้ายจึงเปลี่ยนไปใช้ แนวทางแบบ dijkstra ที่ลดต้นทุนการแมปปิงให้ต่ำที่สุด
- weave เป็น merge-driver บนฐาน treesitter อีกตัวที่เขียนด้วย Rust
- ทั้ง landing page ที่หวือหวา จำนวน GitHub star และ MCP server ทำให้ภาพรวมดูโอเวอร์อยู่พอสมควร
- ผู้เขียนได้ลองดู crate สำหรับดึง entity อย่าง sem
- โค้ด diff หลักพอใช้ได้ แต่ค่อนข้างเยิ่นเย้อ และการจับคู่ entity ใช้ greedy algorithm
- data model ไม่สามารถตรวจจับการย้ายตำแหน่งภายในไฟล์ได้ ทั้งที่การย้ายลักษณะนี้อาจมีนัยสำคัญมาก
- ยังมีการวิเคราะห์ impact แบบ heuristic จำนวนมาก ซึ่งดูเหมือนต้องพึ่งการผสานเข้ากับภาษาอย่างแน่นหนากว่านี้จึงจะน่าเชื่อถือ
- ตอนรัน
sem diff --verbose HEAD~4ยังเจอบั๊กที่แสดงว่าบรรทัดที่ไม่ได้เปลี่ยนจริงกลับเปลี่ยน
- ตอนรัน
- มีฟีเจอร์เชิงสมมติที่น่าจะมีประโยชน์แต่ทำค้างไว้ราว 80% เยอะเกินไป จึงไม่เหมาะจะใช้เป็นฐาน แต่ก็ยังประเมินว่าสิ่งที่ทำได้ในเวลา 3 เดือนนั้นน่าชื่นชม
- diffast คำนวณ tree edit-distance ของ AST ตาม algorithm จากงานวิชาการปี 2008
- รองรับ Python, Java, Verilog, Fortran, C/C++ ผ่าน parser เฉพาะทาง
- หน้า example AST differences gallery จัดทำไว้อย่างดี
- ส่งออกข้อมูลในรูป tuple เพื่อนำไปใช้กับ datalog ได้
- autochrome เป็นเครื่องมือ diff สำหรับ Clojure โดยเฉพาะ และใช้ dynamic programming
- คำอธิบายเชิงภาพและตัวอย่าง walkthrough ทำได้ดีมาก
- บทความของ Tristan Hume เรื่อง Designing a Tree Diff Algorithm Using Dynamic Programming and A* เป็นแหล่งอ้างอิงที่มีคุณค่าสำหรับการออกแบบ algorithm ของ tree diff
เวิร์กโฟลว์ที่ต้องการและแผนขอบเขตต่ำสุด
- use case หลักคือ การรีวิวผลลัพธ์ LLM แบบเป็นรอบ และจะไม่ปล่อยให้ agent สร้างโค้ดเกินหมื่นบรรทัดในทีเดียวแบบสะเปะสะปะ
- ผู้เขียนต้องการมอบงานที่มีขอบเขตกำหนดไว้ให้ agent แล้วกลับมาดูในอีกไม่กี่นาทีเพื่อเห็นภาพรวมการเปลี่ยนแปลงทั้งหมด จากนั้นค่อยแก้เองใน Emacs หรือทิ้งทั้งหมดแล้วลองใหม่ หรือเขียนใหม่เองตั้งแต่ต้นก็ได้
- เวิร์กโฟลว์ที่ต้องการคือการได้เห็น ภาพรวมระดับสูง ก่อนว่า type, function, method ไหนถูกเพิ่ม ลบ หรือแก้ไข
- จากนั้นควรสามารถกางดู text diff ราย entity ได้อย่างรวดเร็ว ทำให้ขยายจากสรุปไปสู่ diff รายละเอียดได้อย่างเป็นธรรมชาติ
- อีกทั้งควรแก้การเปลี่ยนแปลงได้ตรงนั้นทันทีโดยไม่ต้องย้ายที่อื่น และต้องการ inline editing ที่ไม่ต้องสลับจากหน้าจอ diff ไปหน้าจอ file
- เป้าหมายคือย้าย เวิร์กโฟลว์ตรวจทานการเปลี่ยนแปลง·staging ของ Magit จากระดับไฟล์·บรรทัด ไปเป็นระดับ entity
- เพื่อให้สอดคล้องกับบทเรียนเรื่องขอบเขตต่ำสุดที่เพิ่งนึกขึ้นได้ รอบนี้จึงวางแผนจะรีบสร้าง framework สำหรับดึง entity บนฐาน treesitter ด้วยตัวเองก่อน โดยเริ่มจาก Rust เท่านั้น
- การจับคู่จะเริ่มจากวิธี greedy แบบง่ายก่อน และ diff จะเรนเดอร์บน command line
- ถ้าระดับนี้ให้ผลลัพธ์ที่ดีกว่า difftastic สำหรับ commit ที่มีปัญหาได้ ค่อยไปเชื่อมเข้ากับเวิร์กโฟลว์ Emacs ที่โต้ตอบได้มากขึ้นอย่าง Magit ในภายหลัง
- ถ้าเป็นไปได้ก็ยังเปิดทางเลือกไว้สำหรับการ reuse Magit เอง
- การรองรับภาษาใหม่จะค่อยเพิ่มตามความจำเป็น
- ต่อไปอาจสำรวจ การจับคู่แบบ global ที่อิงคะแนน แทน greedy แบบง่าย
- ถ้าออกมาน่าพอใจมากพอ ก็อาจเผยแพร่ต่อสาธารณะ แต่เป้าหมายไม่ใช่การเก็บ GitHub star หรือ HN karma และมันอาจคงเป็นเครื่องมือเงียบ ๆ ที่ใช้คนเดียวต่อไป
- ประโยคที่ว่าบางครั้งคุณก็แค่อยากได้ชั้นวางสักชิ้นหนึ่ง ใช้ปิดท้ายเพื่อย้ำท่าที สร้างเท่าที่จำเป็นแทนที่จะขยายเกินเหตุ
1 ความคิดเห็น
ความเห็นจาก Hacker News
ผมว่ามันแสดงให้เห็นความยากที่ใหญ่ที่สุดของงานวิจัยระดับ PhDได้ดีมาก
พอหยิบหัวข้อที่น่าสนใจขึ้นมาแล้วพยายามอ่านงานก่อนหน้าให้ได้มากที่สุด ก็จะเริ่มตระหนักว่ามีคนทำสิ่งที่เรากำลังจะทำไปแล้วมากแค่ไหน จนทำให้เกิดscope creepได้ง่าย
หลังจากใช้พลังและความตื่นเต้นช่วงต้นไปหมดแล้ว ก็ต้องฝืนดันอีก 20~30% ที่เหลือให้ไปถึงจุดที่พร้อมตีพิมพ์ให้ได้
พอถึงวันที่ 400 กลับเกือบจะอธิบายทฤษฎีแห่งสรรพสิ่งครบแล้ว และกำลังจะสร้างอุปกรณ์ทดลองในวงโคจรจุดลากร็องจ์เพื่อตรวจจับอนุภาคสากลที่เป็นตัวกลางของแรงทั้งหมดในเอกภพที่รู้จัก
เลยสงสัยว่าควรทำอย่างไรถึงจะบรรเทาเรื่องนี้ได้
ในความเป็นจริงมันมักเป็นงานประเภทเพิ่มความสามารถในการสังเกตระบบจาก 1% เป็น 1.001% และใกล้เคียงกับด่านผ่านสำหรับเข้าสู่อาชีพสายวิชาการมากกว่า
เพราะงั้นแทบไม่ค่อยเห็นวิทยานิพนธ์ที่น่าสนใจจริง ๆ ใหม่มากจริง ๆ หรือเอาไปใช้กับวิทยาศาสตร์โดยตรงได้จริง
แทบไม่เคยเห็นใครทำวิจัยแบบนั้นจริง ๆ ปกติอ่านสักสองสามเปเปอร์แล้วค่อยต่อยอดจากตรงนั้นมากกว่า
การไล่อ่านวรรณกรรมวิจัยอย่างลึกจริง ๆ ควรทำหลังเริ่มมีผลลัพธ์ออกมาแล้ว และกำลังเริ่มเขียนสรุปงานมากกว่า
ประโยคที่ว่า ให้มันดีกว่าเดิมก็พอ ผุดขึ้นมาในหัวตลอด
การปรับปรุงเล็ก ๆ สะสมไปตามเวลาได้ และก็ไม่มีอะไรใหม่เอี่ยมสมบูรณ์แบบมาตั้งแต่ต้นอยู่แล้ว ดังนั้นการนั่งพยายามออกแบบให้สมบูรณ์แบบตั้งแต่แรกกลับให้ผลเสียมากกว่า
คำที่ว่าอุปสรรคนี่แหละคือเส้นทางก็ดูเข้ากับเรื่องนี้ดี
เพื่อนร่วมงานคนหนึ่งเวลาเขาวิจารณ์การเปลี่ยนโค้ด ถ้ารู้สึกว่าตัวเองกำลังจับผิดเรื่องเล็กเกินไป เขาจะพูดว่า "ดีกว่าเมื่อก่อน"
มันช่วยให้ยังชี้จุดที่ควรปรับปรุงได้ แต่ก็ให้ไฟเขียวไปพร้อมกันว่าถ้ามีตำหนิเล็กน้อยเหลืออยู่ก็เดินหน้าต่อได้ และผมเห็นด้วยกับท่าทีแบบนี้มาก
เมื่อก่อนผมคิดว่าความสมบูรณ์แบบนิยมคือการฝืนไล่ล่าความสำเร็จที่สูงเกินจริงเท่านั้น แต่การยอมรับอะไรที่ไม่สมบูรณ์แบบไม่ได้จนเลิกไปทั้งที่ยังไม่คืบหน้า ก็อาจเป็นความสมบูรณ์แบบนิยมได้เหมือนกัน
การผัดวันประกันพรุ่งกับงานใหญ่ ๆ ก็มักมีรากเดียวกัน
ผมชอบคำพูดของ CEO ของ Rec Room
เขาบอกว่าทีมมักพูดเสมอว่าอยากให้โปรเจ็กต์สั้นกว่านี้ แทบไม่เคยมีใครบอกว่าอยากเลื่อนเปิดตัวออกไปอีก ทำให้มันซับซ้อนขึ้น และขัดเกลาเพิ่มขึ้น
มันอาจไม่ถูกกับทุกสถานการณ์ 100% แต่ถ้าจะพลาด ผมว่าพลาดแบบทำให้เล็กแล้วปล่อยให้เร็วดีกว่าขยายใหญ่เกินไปจนเสียเวลา
มนุษย์มีธรรมชาติที่คิดไอเดียคล้าย ๆ กันได้ง่าย ดังนั้นถ้าทำโปรเจ็กต์จนเสร็จโดยไม่รู้มาก่อน สุดท้ายมันก็มักจะกลายเป็นการคิดขึ้นใหม่ซ้ำอยู่บ้าง
ในทางกลับกัน ถ้าไปสำรวจก่อนก็อาจพบว่าส่วนหนึ่งเป็นแค่การทำสิ่งที่มีอยู่แล้วซ้ำอีก จนหมดไฟได้
ถึงอย่างนั้น การทำมันให้จบเพื่อการเรียนรู้ของตัวเองอาจเป็นสิ่งที่สำคัญที่สุดอยู่ดี
แน่นอนว่าถ้าต้องสร้างผลงานวิชาการใหม่จริง ๆ หรือหารายได้จากโปรเจ็กต์ที่เป็นเอกลักษณ์จริง ๆ มันก็ยากกว่า แต่แม้แต่ในพื้นที่แบบนั้น แค่บิดของเดิมนิดหน่อยก็มักได้รับการยอมรับมากกว่าที่คิด
ตอนนี้ผมกำลังเจอสถานการณ์นี้กับside projectพอดี
สายงานคือ Information Retrieval เลยยังมีประสบการณ์ไม่มาก และแน่นอนว่ามี prior art จำนวนมากที่เรียนรู้หรือเอามารวมได้
เพราะงั้นพออ่านโพสต์นี้แล้ว ผมเลยเอนเอียงไปทางทำของตัวเองก่อน แล้วค่อยดูงานก่อนหน้าเฉพาะตอนที่ตันหรือต้องการไอเดีย
อีกด้านหนึ่ง ถ้าดูสารคดี Clojure ที่เพิ่งออกมา Rich Hickey ก็ดูเหมือนจะใช้เวลานานกับการลงลึกในงานก่อนหน้า เปเปอร์ และภาษาอื่น ๆ ก่อนจะลงมือทำ
แต่เขาเองก่อนหน้านั้นก็เคยสร้างภาษาอื่นมาแล้ว ดังนั้นภาพใหญ่ก็คือทุกอย่างเริ่มจากการเรียนรู้ผ่านการลงมือทำอยู่ดี
อาจจะต้องไม่คิดนานเกินไป ลองสร้างมันก่อน สะสมบทเรียนจากของจริง ชนกำแพงให้พอ แล้วค่อยทำการค้นคว้าเชิงลึกเมื่อจำเป็นมากกว่า
แล้วยิ่งไปดู "Easy made Simple", "Hammock Driven Development" ต่อ ก็ยิ่งอยากเรียน Clojure
Clojure documentary on CultRepo channel: https://www.youtube.com/watch?v=Y24vK_QDLFg
Simple Made Easy: https://www.youtube.com/watch?v=SxdOUGdseq4
Hammock Driven Development: https://www.youtube.com/watch?v=f84n5oFoZBc
การตั้งกำหนดส่งช่วยแก้ปัญหา scope creep ได้เกือบทั้งหมดสำหรับผม
จากประสบการณ์ โปรเจ็กต์ที่มีเดดไลน์แข็งแบบ game jam หรือการแข่งขันเขียนโปรแกรมจะทำให้จบได้ง่ายกว่า ในขณะที่โปรเจ็กต์ที่เปิดปลายไว้จบยากกว่ามาก
มันดูเป็นบริบทเดียวกับที่มาตรฐาน C++ ออกทุก 3 ปี ไม่ได้รอให้ทุกฟีเจอร์ที่อยากได้พร้อมครบก่อนค่อยปล่อย
https://news.ycombinator.com/item?id=20428703
บทความน่าสนใจดี แต่ความคิดของผู้เขียนดูกระจัดกระจายไปหน่อย
สำหรับคนที่บอกว่าตัวเองโดน scope creep ถล่ม ผมกลับรู้สึกว่าเจ้าตัวเป็นคนที่ทำอะไรได้เยอะมาก เพราะท้ายบทความยังมีลิงก์สารพัดหัวข้อเต็มไปหมด
สุดท้ายก็ดูเป็นคนประเภทที่ชอบเรียนรู้และลองไปเรื่อย ๆ จริง ๆ และกระบวนการหลุดลง rabbit hole นั่นเองก็น่าจะกระตุ้นสมองให้สนุก
ในฐานะคนทำคนเดียว ผมมีข้อสังเกตหนึ่งที่ช่วยได้มาก
สิ่งที่ดูเหมือนเป็นabstraction ที่จำเป็นส่วนใหญ่นั้น จริง ๆ แล้วเป็นแค่ scope creep ที่เปลี่ยนชื่อ
ผมเคยติด flag ให้ฟีเจอร์ใหม่ทุกตัว จนเริ่มเห็นแพตเทิร์นในโค้ดตัวเอง เลยตั้งกฎข้อหนึ่งขึ้นมา
คือจะไม่ปล่อยฟีเจอร์ถ้ายังไม่มีการทดสอบพฤติกรรมตอนปิด flag
พอทำแบบนั้น ผมก็เริ่มมองว่า flag ไม่ใช่ทางหนี แต่เป็นส่วนหนึ่งของผลิตภัณฑ์ และฟีเจอร์สามตัวในแบ็กล็อกก็หายไปเองตามธรรมชาติเมื่อเริ่มคิดแบบนี้
แม้การวางแผนมากเกินไปและ scope creep จะเป็นปัญหาจริง แต่ในทางกลับกันก็ต้องระวังไม่เหวี่ยงไปทางพัฒนาแบบสด ๆ ตามใจมากเกินไป
โปรเจ็กต์ที่ประสบความสำเร็จที่สุดบางชิ้นของผม เกิดจากการวางแผนและทบทวนฟีเจอร์ส่วนใหญ่ล่วงหน้าระหว่างทำโมเดลข้อมูล ก่อนจะเริ่มสร้างซอฟต์แวร์ที่ใช้งานได้จริง
ในขั้นนั้นมักยังไม่รู้ว่าอะไรเกินจำเป็น และถ้าตัดฟีเจอร์ที่ผมหรือผู้ใช้น่าจะต้องการออกไป สุดท้ายจะเสียเวลาเยอะมากกับการออกแบบแกนโค้ดใหม่ในภายหลัง
แต่ถ้าคิดพลาด โปรเจ็กต์ก็จะใหญ่เกินไปจนถูกเรียกว่า scope creep
สุดท้ายแล้วการตัดสินนี้ขึ้นอยู่กับว่าคุณเข้าใจโดเมนนั้นดีแค่ไหน
ถ้ารู้โดเมนน้อยกว่าที่คิด ก็ต้องรีเวิร์กเยอะ แต่ถ้ารู้โดเมนมากกว่าที่คิด ก็อาจจริง ๆ แล้วไปได้ไกลกว่านั้นแต่กลับเสียเวลากับ baby step
ไม่ว่าจะไปทางไหนก็มีความเสียดายเหลืออยู่ เลยรู้สึกว่าสุดท้ายมันเป็นปัญหาเรื่องการตัดสินใจอย่างมาก
อย่าตกหลุมsunk cost fallacy และต่อให้ใช้เวลาหลายชั่วโมงไปกับการค้นเรื่องระดับหัวข้อ PhD ก็ไม่ได้แปลว่าต้องเอามันมาใส่ในโปรเจ็กต์เสมอไป
ถ้ามันไม่ตรงกับปัญหาตอนนี้จริง ๆ ก็ควรตัดทิ้งอย่างกล้า ๆ