Baby Monitor Timmy เริ่มจากแนวคิดง่าย ๆ คือ เบบี้มอนิเตอร์ที่เคารพความเป็นส่วนตัวในบ้าน ไม่มีการบันทึกบนคลาวด์ และไม่มีการส่งข้อมูลออกจากห้องเด็กโดยไม่จำเป็น สิ่งที่หลายคนอาจไม่เห็นคือ ผมพัฒนา Timmy คนเดียวจากซูริก และ GitHub Copilot เป็นเพื่อนร่วมเขียนโค้ดที่ทำงานไวมากของผม
เวิร์กโฟลว์ระหว่างคนกับ AI
บทบาทแบ่งกันชัดเจน: ผมกำหนดฟีเจอร์ จัดลำดับความสำคัญ และตัดสินใจเรื่องสถาปัตยกรรม Copilot ช่วยด้านการลงมือทำ: เขียนโค้ด เพิ่มการทดสอบ ไล่หาบั๊กให้แคบลง และเตรียมขั้นตอนรีลีส
สปรินต์ปกติของผมโดยคร่าว ๆ เป็นแบบนี้:
- รายละเอียดฟีเจอร์: ผมอธิบายว่าฟีเจอร์ควรทำอะไร รวมถึงกรณีพิเศษและข้อจำกัด
- การพัฒนา: Copilot เสนอโค้ดและทำตามรูปแบบที่ใช้อยู่ในโปรเจกต์
- การทดสอบ: การทดสอบ end-to-end อัตโนมัติทำงานบนอีมูเลเตอร์สองเครื่อง และตรวจสอบการเชื่อมต่อจริงระหว่างอุปกรณ์ฝั่งเด็กกับอุปกรณ์ฝั่งผู้ปกครอง
- การเผยแพร่: เมื่อการทดสอบผ่าน จะเตรียมบิลด์ Android และ iOS สำหรับสโตร์และช่องทางทดสอบที่เหมาะสม
วงจรนี้เกิดซ้ำกับฟีเจอร์และการแก้บั๊ก ผมไม่ได้เขียนทุกบรรทัดเอง แต่ผมตัดสินใจว่า อะไร จะถูกสร้างขึ้น ทำไม จึงต้องสร้างมันขึ้นมา และข้อเสนอใดเหมาะกับ Timmy
จากแนวคิดสู่ WebRTC
งานเทคนิคหลักชัดเจนตั้งแต่แรก: เสียงและวิดีโอแบบเรียลไทม์ระหว่างโทรศัพท์สองเครื่อง WebRTC คือทางเลือกที่ชัดเจน แต่การผสานเข้ากับ Flutter ไม่ใช่เรื่องง่าย: ICE candidates, การเจรจา SDP, TURN fallback และ DataChannels ต้องทำงานร่วมกันตามลำดับที่ถูกต้อง
Copilot ช่วยผมประกอบส่วนต่าง ๆ เหล่านี้ทีละขั้น: ตั้งค่า peer connection, รักษาลำดับสำคัญให้ถูกต้อง (DataChannel ก่อน offer, onTrack ก่อน setRemoteDescription) และวาง signaling ไว้บน Firebase Firestore ทุกส่วนต้องทำงานบนอีมูเลเตอร์สองเครื่องก่อนที่ผมจะไปต่อ
การจับคู่ที่ปลอดภัยด้วย ECDH
หนึ่งในฟีเจอร์ที่สำคัญที่สุดคือ ระบบจับคู่ที่ปลอดภัย อุปกรณ์สองเครื่องต้องสร้างความเชื่อถือซึ่งกันและกันโดยไม่ต้องพึ่งเซิร์ฟเวอร์กลางมายืนยัน ตัวตนของอุปกรณ์ วิธีแก้คือการแลกเปลี่ยนคีย์ ECDH P-256 ผ่าน Firebase ร่วมกับ รหัสยืนยันที่มองเห็นได้ (SAS) ซึ่งช่วยตรวจจับการโจมตีแบบ man-in-the-middle
Copilot ช่วยพัฒนาลำดับการเข้ารหัส: การสร้างคีย์ การแลกเปลี่ยน public key การสร้าง shared secret การคำนวณ SAS และการเข้ารหัส AES-256-GCM สำหรับข้อมูล signaling ทั้งหมดในภายหลัง ผมไม่ส่ง pairing key ไปยังแบ็กเอนด์ แต่ใช้เพียงแฮช SHA-256 ของมันเป็นตัวระบุเอกสาร Firestore
การตรวจสอบความปลอดภัย: ค้นหาและแก้ไขช่องโหว่
สำหรับผม การพัฒนาด้วย AI ไม่ได้แค่พิมพ์โค้ดได้เร็วขึ้น แต่ยังช่วยค้นหาบั๊กอย่างเป็นระบบด้วย ในสปรินต์ตรวจสอบความปลอดภัยแบบเจาะจง Copilot วิเคราะห์โค้ดเบสและพบ ปัญหา 6 ข้อ ที่ผมต้องแก้:
- ไม่มีการตรวจสอบความถูกต้องของข้อมูลนำเข้าในข้อมูล signaling
- อาจเกิด race condition ในการจัดการ ICE candidate
- ข้อมูลเซสชันเก่าที่ไม่ได้ถูกล้างอย่างเหมาะสม
- กฎความปลอดภัย Firestore ที่อนุญาตกว้างเกินไป
- ยังไม่ได้พิจารณา certificate pinning
- การจัดการข้อผิดพลาดในขั้นตอน TURN credential ยังไม่เพียงพอ
ทั้ง 6 ข้อได้รับการแก้ไขในสปรินต์เดียว นี่คือจุดที่ Copilot ทำได้ดี: อ่านไฟล์จำนวนมาก เปรียบเทียบรูปแบบ และชี้จุดที่ผมต้องตรวจสอบอย่างละเอียด
สปรินต์แบบวนซ้ำ: แอปพัฒนาอย่างไร
Timmy เติบโตผ่านสปรินต์ที่รวดเร็วแต่มีขอบเขตชัดเจน หมุดหมายบางส่วน:
- v1.8: ออกแบบระบบจับคู่ใหม่ทั้งหมด — รหัส 4 อักขระและ ECDH P-256 ผ่าน Firebase เข้ามาแทนแนวทาง direct-key แบบเดิม
- v1.10: สปรินต์เสริมความปลอดภัย — ตรวจสอบและแก้ไขช่องโหว่ทั้ง 6 ข้อ
- v1.11: โหมดมืดในทุกหน้าจอ พร้อมหน้าแรกและบล็อกที่คุณกำลังอ่านอยู่ตอนนี้
- v1.12: ยกเครื่องหน้าจอผู้ปกครองครั้งใหญ่ โหมดมองเห็นกลางคืน และการตรวจจับการเคลื่อนไหวด้วยการวิเคราะห์เฟรมจากกล้อง
ทุกสปรินต์ใช้รูปแบบพื้นฐานเดียวกัน: อธิบายเป้าหมาย ตรวจทานข้อเสนอ ทดสอบอัตโนมัติ แล้วส่งให้ผู้ทดสอบ
การทดสอบ E2E ข้ามอุปกรณ์
คุณไม่สามารถทดสอบเบบี้มอนิเตอร์ได้อย่างเหมาะสมด้วยอุปกรณ์เพียงเครื่องเดียว ผมต้องมีอุปกรณ์ฝั่งเด็กหนึ่งเครื่องและอุปกรณ์ฝั่งผู้ปกครองหนึ่งเครื่อง โปรเจกต์เริ่มจากอีมูเลเตอร์ Android สองเครื่องที่ทำงานพร้อมกัน และตอนนี้เสริม กระบวนการดังกล่าวด้วยการตรวจสอบบน iOS Simulator ในเครื่องและอุปกรณ์จริง ส่วนสคริปต์ทดสอบ Android อัตโนมัติ ยังคงทำสิ่งต่อไปนี้:
- ติดตั้งแอปบนอีมูเลเตอร์ทั้งสองเครื่อง
- ดำเนินขั้นตอนการจับคู่บนอุปกรณ์ทั้งสองเครื่อง
- ตรวจสอบว่าเชื่อมต่อเสียงและวิดีโอได้แล้ว
- ทดสอบ push-to-talk การควบคุมกล้อง และฟีเจอร์อื่น ๆ
เนื่องจากอีมูเลเตอร์ทั้งสองเครื่องใช้ IP address เดียวกัน (10.0.2.15) การเชื่อมต่อแบบ peer-to-peer โดยตรงผ่าน STUN จึงเป็นไปไม่ได้ การทดสอบทุกครั้งต้องผ่าน TURN relay ของ Cloudflare แม้จะน่ารำคาญ แต่ก็มีประโยชน์ เพราะเส้นทางการเชื่อมต่อที่ซับซ้อนที่สุดจะได้รับการทดสอบทุกครั้ง
สิ่งที่ผมได้เรียนรู้
การสร้างแอปที่สมบูรณ์ด้วยเพื่อนร่วมเขียนโค้ด AI สอนผมหลายเรื่อง:
- สถาปัตยกรรมสำคัญกว่าที่เคย รูปแบบที่ชัดเจนและโค้ดเบสที่มีเอกสารครบถ้วน ช่วยให้ AI เสนอโค้ดที่สอดคล้องกัน ความคลุมเครือทำให้ต้นทุนเพิ่มขึ้นอย่างรวดเร็ว
- การทดสอบเป็นสิ่งที่ละเลยไม่ได้ โค้ดที่ AI สร้างต้องผ่านการทดสอบที่เข้มงวด เช่นเดียวกับโค้ดที่คนเขียน การทดสอบ E2E อัตโนมัติพบปัญหาที่พลาดได้ง่าย หากทดสอบด้วยตนเอง
- มนุษย์ยังต้องมีส่วนร่วมในกระบวนการ ทุกการตัดสินใจด้านสถาปัตยกรรม ทุกการแลกเปลี่ยนด้านความปลอดภัย และทุกขอบเขตของผลิตภัณฑ์ยังเป็นหน้าที่ของผม AI เร่งการพัฒนาได้ แต่แทนที่วิจารณญาณไม่ได้
- ความเร็วช่วยให้เกิดคุณภาพ เมื่อปล่อยฟีเจอร์ได้ภายในไม่กี่ชั่วโมงแทนที่จะเป็นหลายวัน ก็เหลือรอบการปรับแต่งและแก้บั๊กมากขึ้น เร็วไม่ได้แปลว่าดีโดยอัตโนมัติ
มองไปข้างหน้า
Baby Monitor Timmy ยังคงพัฒนาต่อไป รีลีส iOS ใกล้พร้อมแล้ว; หลังจากนั้นจะมีฟีเจอร์เซ็นเซอร์เพิ่มเติมและการเสริมความปลอดภัยอย่างต่อเนื่อง เวิร์กโฟลว์ยังคล้ายเดิม: ผมกำหนดทิศทางและขอบเขต ส่วน Copilot ช่วยพัฒนาและตรวจสอบได้รวดเร็ว
ส่วนประกอบที่เกี่ยวข้องกับความปลอดภัยตอนนี้ถูกจัดวางโดยมี ขอบเขตที่ชัดเจน ในรีโพซิทอรีสาธารณะ baby-monitor-timmy-core. ที่นั่นยังบันทึกการตัดสินใจด้านสถาปัตยกรรมเกี่ยวกับการจับคู่ signaling และอินเทอร์เฟซแบ็กเอนด์ไว้ด้วย