หกเดือนกับการสร้าง Bookverse เพียงลำพัง
มีเวอร์ชันโรแมนติกของการสร้างซอฟต์แวร์ตามลำพัง ที่คุณมีโต๊ะสะอาดสะอ้าน กาแฟเอสเพรสโซหนึ่งแก้ว แล้วคุณก็ปล่อยของที่ผู้คนรักออกมาก่อนมื้อเที่ยง แต่เวอร์ชันที่ผมใช้ชีวิตจริงตลอดหกเดือนที่ผ่านมานั้นเหมือนกับ “เขียนไฟล์ คอนฟิก Nginx เดิมๆ สี่รอบ เพราะ edge case ที่เจ็ดเพิ่งกัดคุณ” มากกว่า
นี่คือบันทึกความคิด — ไม่ใช่เช็กลิสต์ของสิ่งที่ปล่อยออกไป ไม่ใช่บทเรียนสอน แค่บันทึกหลังจากหกเดือนเท่านั้น
ทำไมผมถึงเริ่ม
ผมใช้แอปเรียนภาษามาเรื่อยๆ บ้างไม่บ้างมาหลายปี และสังเกตเห็นอะไรบางอย่างที่ เฉพาะเจาะจง: ผมทำสตรีค 30 วันบนแอปเกมิฟิเคชันยอดนิยมจนครบได้ แต่ก็ยังอ่านข้อความของเจ้าของภาษาแม้แค่ย่อหน้าเดียวไม่ได้ ลูปป้อนกลับนั้นแน่น เกมิฟิเคชันก็ลื่นไหล แต่หน่วยของความก้าวหน้ากลับเป็น ประโยคหนึ่ง บางทีก็ คำหนึ่ง ส่วนการอ่านภาษา — การอ่านมันจริงๆ — เกิดขึ้นที่ระดับของหน้ากระดาษ
ผมอยากได้แอปที่ปฏิบัติต่อตำราเรียนเหมือนตำราเรียน เปิดบทที่หนึ่ง อ่านมัน ฟังมัน พูดมันกลับ ศึกษาสิ่งที่ติดอยู่ พรุ่งนี้ บทที่สอง
รูปร่างของผลิตภัณฑ์ชัดเจนมาตั้งแต่วันแรก กับดักก็คือ “รูปร่างชัดเจนตั้งแต่ วันแรก” รับผิดชอบประมาณ 5% ของการสร้างซอฟต์แวร์เท่านั้น อีก 95% ที่เหลือคือ ช่วงกลางที่ไม่หรูหราเลย
ช่วงกลางที่ไม่หรูหรา
รายการที่ยังไม่ครบถ้วนของสิ่งที่ผมทำในไตรมาสนี้ ที่ไม่อยู่ใน release note ไหนเลย:
- เปลี่ยนชื่อทรีพาธของ monorepo ทั้งต้นจาก
app_v54_01เป็นbookverse(แล้วเปลี่ยนชื่อใหม่อีกครั้งตอนที่ผมอยากได้ขีดล่างในชื่อ unit ของ systemd) - เขียนคอนฟิก Terraform สำหรับ Nginx vhost สี่เวอร์ชัน ก่อนจะค้นพบว่า
trigger ของ
null_resourceต้องรวม hash ของเทมเพลตที่ render แล้วเข้าไป ด้วย ไม่ใช่แค่ตัวแปรอินพุต - ใช้เวลาหนึ่งสัปดาห์ตามหาว่าทำไมค่าตัวแปรสภาพแวดล้อม
EXPO_PUBLIC_*ถึง resolve เป็นสตริงว่างใน web bundle แต่ทำงานปกติบน iOS (preset-expoของ Babel ข้ามการ inline สำหรับnode_modulesใช้ฟังก์ชัน factory ไม่ใช่ ตัวแปรสภาพแวดล้อม ในแพ็กเกจที่ publish แล้ว) - รีแฟกเตอร์จุดที่เรียกใช้ helper ของ API URL ถึง 17 จุด เพราะผมรวมศูนย์มัน ผิดตั้งแต่ครั้งแรก
ไม่มีสิ่งใดในนี้ที่จะอยู่ในโพสต์การตลาดเกี่ยวกับแอป แต่ทั้งหมดนี้คืองานจริง
สิ่งที่ไม่มีใครเตือนคุณเกี่ยวกับการพัฒนาคนเดียวก็คือ ไม่มีใครมาช่วยซับครึ่งที่ น่าเบื่อ คุณพูดไม่ได้ว่า “ทีมแพลตฟอร์มดูแลการ deploy” เพราะ คุณคือทีม แพลตฟอร์ม คุณสวมหมวกทุกใบอย่างห่วยๆ จนกว่าจะสวมมันนานพอที่จะสวมได้ดี
หลุมกระต่ายของการรู้จำเสียงพูด
ช่วงเวลาที่ให้บทเรียนมากที่สุดในหกเดือนที่ผ่านมา คือการทำให้การฝึกพูดทำงาน ได้บนโทรศัพท์ในประเทศจีน
แผนคือ: แตะที่บรรทัดหนึ่ง ฟังเจ้าของภาษาพูดมัน แตะอีกครั้ง อัดเสียงตัวเอง
แล้วดูว่าคุณใกล้เคียงแค่ไหน การรู้จำเสียงพูดบนมือถือแบบมาตรฐาน —
expo-speech-recognition บน iOS, บริการของระบบบน Android, จบ
ความเป็นจริง: ผู้ชมเป้าหมายส่วนหนึ่งใช้โทรศัพท์ Lenovo Motorola ที่เป็น
ระบบปฏิบัติการเวอร์ชันสำหรับจีน (โอเวอร์เลย์ gmsconfig.china) ซึ่งตัด
Google Mobile Services ออกไปทั้งหมด ไม่มี speech-to-text ของระบบ แอปทำงาน
ได้สมบูรณ์แบบใน emulator แต่บน Moto XT2507 ในกรุงปักกิ่ง มันแครชแบบเงียบๆ
การแก้ไขใช้เวลาประมาณสามสัปดาห์:
- สร้าง adapter pattern เพื่อให้แอปสลับ backend เสียงพูดได้ตามแพลตฟอร์ม — การรู้จำของระบบในที่ที่มี และการถอดเสียงบนคลาวด์เป็นตัวสำรอง
- เพิ่ม backend คลาวด์แบบ Whisper (
qwen3-asr-flashของ Alibaba ผ่าน DashScope เพราะอุปกรณ์เวอร์ชันจีนเข้าถึงมันได้โดยไม่ต้องใช้ VPN) - ปรับจูนการตรวจจับการทำงานของเสียง (voice activity detection) ให้การ อัดเสียงหยุดเองเมื่อคุณพูดจบ (เกณฑ์ -25 dBFS, หน้าต่างความเงียบ 600ms ปรับให้เรียบผ่านห้าตัวอย่าง — ทุกชุดที่เหลือไม่ตัดคุณกลางคำ ก็ไม่หยุดเลย)
- เปลี่ยนจากการอัปโหลด
FormDataของ React Native ไปเป็นuploadAsyncของexpo-file-systemเพราะFormDataของ RN มีปัญหากับ blob เสียงบน อุปกรณ์เดียวกันนั้น
ครึ่งหนึ่งของสี่ข้อนั้นเป็นโค้ดที่มากกว่าที่ผมเขียนในหนึ่งสัปดาห์ปกติ ไม่มี ข้อไหนเป็น “ฟีเจอร์” เลย แต่ทั้งหมดจำเป็นเพื่อให้ฟีเจอร์มีอยู่จริงสำหรับคนที่ ต้องการมัน
บทเรียน — ซ้ำแล้วซ้ำเล่าโดยประมาณทุกสองสัปดาห์ — คือ ส่วนที่ยากนั้นแทบไม่ เคยเป็นส่วนที่คุณคิดว่าจะยาก ผมตั้งงบไว้หนึ่งวันสำหรับการรู้จำเสียงพูด มัน ใช้เวลายี่สิบวัน
วินัยของการทำให้เสร็จ
สิ่งล่อใจตอนสร้างคนเดียวคือการไล่ตามของใหม่ที่แวววาว ฟีเจอร์ใหม่นั้นน่า ตื่นเต้น การปรับครั้งที่ 47 ของ flow ที่มีอยู่แล้วนั้นไม่ คุณก็เลยสะสมฟีเจอร์ ที่ไม่มีอันไหนเสร็จจริงๆ สักอัน แล้วผลิตภัณฑ์ก็ให้ความรู้สึกเหมือนสุสานของ งานที่สร้างค้างไว้ครึ่งๆ กลางๆ
วินัยที่ผมต้องเรียนรู้ใหม่อยู่เรื่อยๆ: ทำสิ่งหนึ่งให้เสร็จอย่างเหมาะสมก่อน จะเริ่มสิ่งต่อไป ภาษาจีนกลางก่อน ทำให้ภาษาจีนกลางดีจริงๆ แล้วค่อยภาษา เกาหลี แล้วค่อยภาษาอังกฤษ แพลตฟอร์มถูกสร้างมาเพื่อส่งมอบคอร์สที่มีโครงสร้าง — แต่คอร์ส แรก ต้องเป็นคอร์สเรือธง คอร์สที่ผู้เรียนสามารถเรียนจนจบได้จริง
คอร์สภาษาเกาหลีที่วางแผนไว้แต่สร้างไป 5% ไม่ช่วยใครเลย คอร์สภาษาจีนกลางที่ เสร็จสมบูรณ์ ที่ใครสักคนอ่านได้ตั้งแต่ต้นจนจบ ต่างหากที่เป็นผลิตภัณฑ์จริง
กับฟีเจอร์ก็เช่นกัน การฝึกพูดอยู่ใน roadmap มาสามเดือนก่อนจะปล่อยออกมา มัน ปล่อยช้า แต่มันปล่อยออกมา อย่างถูกต้อง — การจัดการเคสของภูมิภาคจีนเป็น องค์ประกอบรับน้ำหนักที่ทำให้มันใช้งานได้ ไม่ใช่รายละเอียดขัดเงาที่จะเลื่อนไป ทีหลัง
สิ่งที่ผมอยากบอกตัวเองเมื่อเดือนพฤศจิกายน
สามอย่าง ถ้าผมส่งโปสต์การ์ดกลับไปหาเวอร์ชันของผมที่เริ่มเรื่องนี้ได้
เลิกเปลี่ยนชื่อได้แล้ว ชื่อเป็นสิ่งที่ตั้งผิดในวันแรกได้สบายๆ การเปลี่ยน ชื่อใหม่ส่วนใหญ่แล้วมีต้นทุนมากกว่าคุณค่าของมัน (ตอนนี้ผมทำแบบนี้ไปแล้วสี่ครั้ง พอดี)
ซื้อหนังสือที่มีโครงสร้าง ผมเสียเวลาหลายสัปดาห์เด้งไปเด้งมาระหว่างรายการ คำศัพท์จากฟอรัมต่างๆ มาตรฐาน HSK 3.0 เป็นเอกสารที่ตี พิมพ์จริง การซื้อหนังสือที่มีโครงสร้างแล้วทำตามนั้น เร็วกว่าการ reverse-engineer ว่ามันครอบคลุมอะไรบ้าง
ผู้ชมนั้นอดทน ผมเอาแต่คิดว่าต้องปล่อยให้เร็ว ไม่อย่างนั้นคนจะหมดความสนใจ ความจริงตรงกันข้าม: ผู้เรียนภาษาตามนิยามแล้วอดทนอยู่แล้ว — พวกเขามุ่งมั่นกับ การฝึกฝนทั้งปีอยู่แล้ว พวกเขาไม่ต้องการเดโมในวันที่สาม พวกเขาต้องการอะไรที่ ทำงานได้ในวันที่สามร้อย
ต่อไปคืออะไร
ภาษาจีนกลางเติบโตขึ้นเรื่อยๆ — Band 1 และ Band 2 ใช้งานได้แล้ว Band 3 กำลังถูกตรวจสอบอยู่ตอนนี้ การเขียนเนื้อหาคอร์สภาษาเกาหลีจะเริ่มหลังจากภาษา จีนกลาง Band 3 ปล่อยออกมา ฝั่งแพลตฟอร์มสงบกว่าเมื่อหกเดือนก่อน ซึ่งหมายความ ว่าเวลามากขึ้นถูกทุ่มลงไปในเนื้อหาการเรียนรู้จริงๆ — ซึ่งเป็นที่ที่มันควรอยู่
ถ้าคุณกำลังใช้ Bookverse: ขอบคุณครับ ถ้ายังไม่ได้ใช้: ลองเปิดสักบท ดูบ้างนะ บทแรกฟรี