1 คะแนน โดย GN⁺ 2024-06-10 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • สตาร์ทอัพที่เพิ่งเปิดใช้การสร้างรายได้ประสบปัญหาการชำระเงินค่าสมัครสมาชิก แต่ภายในทีมไม่สามารถจำลองปัญหาได้ ทำให้ การหาสาเหตุล่าช้าไป 5 วัน
  • ปัญหาเริ่มจากการคัดลอกรูปแบบการแปลง Prisma/TypeScript→Python/SQLAlchemy ที่ ChatGPT สร้างขึ้นมา โดยมี สตริง ID ที่ฮาร์ดโค้ดไว้ ถูกใส่เหมือนเป็นค่าเริ่มต้น แทนที่จะเป็นฟังก์ชันสร้าง UUID
  • ด้วยโครงสร้าง AWS ECS 8 tasks และแต่ละ task มี 5 instances ผู้ใช้จึงอาจไปเจอหนึ่งใน พูล ID ไม่ซ้ำกันสูงสุด 40 ชุด และในช่วงกลางวันปัญหาถูกกลบด้วยการ deploy บ่อยครั้ง
  • ตอนกลางคืนเมื่อการ deploy หยุดลง ID เดียวของแต่ละ server ถูกใช้จนหมด และหลังจากนั้นความพยายามสมัครสมาชิกใหม่ล้มเหลวเพราะ unique ID collision
  • จากการประเมินตามข้อร้องเรียนวันละ 50 รายการ เป็นเวลา 5 วัน และค่าสมัครสมาชิกเดือนละ $40 ความเสียหายอยู่ที่ $10,000 ต่อเดือน โดยการไม่มี test, logging, alert และการคัดลอก code ทำให้การรับมือ incident บานปลาย

ปัญหาการสมัครสมาชิกที่เผยออกมาทันทีหลังเปิดสร้างรายได้

  • สตาร์ทอัพเปิดใช้ การสร้างรายได้ เป็นครั้งแรกในเดือนพฤษภาคม และได้ลูกค้ารายแรกภายใน 1 ชั่วโมงหลังเปิดตัว
  • เช้าวันถัดมา ใน Gmail มีข้อร้องเรียนจากผู้ใช้สะสมมากกว่า 40 รายการ
    • ผู้ใช้ไม่สามารถสมัครสมาชิกให้เสร็จสมบูรณ์ได้
    • ผู้ใช้แจ้งว่าเมื่อกดปุ่มสมัครสมาชิก จะเห็น spinner โหลดไม่สิ้นสุด
  • ทีมสร้างบัญชีใหม่เพื่อตรวจสอบเอง แต่ภายในทีมการสมัครสมาชิกทำงานปกติ จึงจำลองสาเหตุไม่ได้
  • ในช่วงเวลาทำงานแทบไม่มีข้อร้องเรียน และปัญหามักสะสมขึ้นในช่วงกลางคืน

การทำระบบสร้างรายได้ภายใต้แรงกดดันด้านเวลา

  • เดือนพฤษภาคมเป็นช่วงเริ่มต้นของ YC S23 batch และทีมยังไม่มั่นใจว่าหลังเปิดตัวแล้วทิศทางใดเหมาะสมที่สุด
  • Dalton ซึ่งเป็น group partner ของ YC แนะนำให้ใช้ ผู้สมัครสมาชิกแบบจ่ายเงิน เป็นตัวชี้วัดสำหรับกำหนดทิศทาง และให้เพิ่มราคาต่อเดือนที่คิดไว้เป็นสองเท่า
  • ราคาสุดท้ายถูกกำหนดไว้ที่ $40 ต่อเดือน
  • เดิมโปรเจกต์เป็น full-stack NextJS แต่ในช่วงก่อนและหลังงานสร้างรายได้ ทีมกำลังย้ายไปใช้ Python/FastAPI
    • ใช้ ChatGPT ในกระบวนการย้ายระบบ
    • ทำ Stripe integration เสร็จแล้วด้วย
  • หลังจากนั้นเป็นเวลา 5 วัน ทีมได้นอนน้อยลงมาก และต้องจัดการอีเมลร้องเรียนวันละ 30–50 ฉบับ

รูปแบบการแปลงโมเดลที่ ChatGPT สร้างขึ้น

  • ในกระบวนการย้าย backend ทีมย้าย database model จาก Prisma/TypeScript ไปเป็น Python/SQLAlchemy
  • การแปลงโมเดลเป็นงานน่าเบื่อ และเมื่อเห็นว่า ChatGPT ทำได้ดี จึงใช้กับการ migration แทบทั้งหมด
  • code ที่สร้างขึ้นถูกคัดลอกและวาง จากนั้นตรวจสอบการทำงาน และใน production ก็ดูเหมือนปกติ จึงเดินหน้าต่อ
  • ตอนนั้นการ insert ลง database ยังคงเป็นหน้าที่ของ Next API ส่วน Python backend แค่อ่าน database เท่านั้น
  • เมื่อทำฟีเจอร์สมัครสมาชิก ทีมจึงเริ่ม insert DB record จาก Python เป็นครั้งแรก
    • SQLAlchemy model ใหม่สร้างเอง แต่คัดลอกรูปแบบที่ ChatGPT สร้างไว้จาก model เดิมมาใช้เหมือนเดิม
    • ปัญหาเดียวกันถูกใส่เข้าไปในวิธีสร้าง ID ของทุก model

สาเหตุจริงและทำไมกลางวันจึงมองไม่เห็น

  • ข้อผิดพลาดหลักคือไม่ได้ส่ง ฟังก์ชันหรือ lambda สำหรับสร้าง UUID แต่ส่งสตริง ID เดียวที่ฮาร์ดโค้ดไว้
  • เมื่อผู้ใช้คนหนึ่งสมัครสมาชิกสำเร็จด้วย ID นั้นบน backend instance ใด instance หนึ่ง ความพยายามสมัครสมาชิกครั้งต่อ ๆ ไปบน instance เดียวกันจะเกิด การชนกันของ ID ที่ต้องไม่ซ้ำ
  • โครงสร้าง backend ทำให้ปัญหานี้ถูกซ่อนไว้นานขึ้น
    • รัน ECS tasks 8 ตัวบน AWS
    • แต่ละ task รัน backend instances 5 ตัว
    • ผู้ใช้อาจไปถึงหนึ่งใน ID ที่แตกต่างกันได้ถึง 40 ค่า
  • ตอนกลางวันมีการ commit ตรงเข้า main branch วันละ 10–20 ครั้ง และทุกครั้งจะเกิดการ deploy backend ใหม่
    • ทุกครั้งที่มีการ deploy จะมี ID ใหม่ 40 ค่าให้ลูกค้าใช้งานได้
  • ตอนกลางคืน เมื่อการ commit และ deploy หยุดลง ID เดียวของแต่ละ server ก็ถูกใช้หมดอย่างรวดเร็ว
    • ช่วงแรกมี server ที่สมัครสมาชิกได้เกือบ 40 ตัว แต่เมื่อเวลาผ่านไปก็ลดลงจนเกือบเหลือ 0

ขนาดความเสียหายและการดำเนินการหลังจากนั้น

  • ประเมินความเสียหายจาก 50 emails/day x 5 days x $40/month เป็นรายได้ที่สูญเสีย $10,000 ต่อเดือน
    • การคำนวณนี้อิงเฉพาะผู้ใช้ที่ส่งข้อร้องเรียนมาเท่านั้น
  • การหาสาเหตุต้องใช้เวลา 5 วัน อีเมลจำนวนมาก Sentry logs หลายร้อยรายการ การคุย Discord ยาวนานกับวิศวกร Stripe และการตรวจสอบไฟล์สำคัญ 5 ไฟล์
  • หลังพบสาเหตุ Adam ก็ส่งแพตช์แก้ไขขึ้นอย่างรวดเร็ว
  • หลังจากนั้นได้เพิ่ม unit/integration tests ที่เข้มงวด, alerts และ logging
  • เหตุการณ์นี้แสดงให้เห็นว่า เมื่อความผิดพลาดของมนุษย์, test ที่ไม่เพียงพอ, การคัดลอก code และการ push ตรงเข้า main ซ้อนกัน แม้โค้ดเพียงบรรทัดเล็ก ๆ ก็อาจนำไปสู่การสูญเสียรายได้ครั้งใหญ่ได้

2 ความคิดเห็น

 
znjadong 2024-06-11

เอ๊ะ โค้ดที่ AI สร้างขึ้นมาแบบอัตโนมัติต้องรีวิวก่อนอยู่แล้วสิ ทำไมถึงเอาไปใช้ทั้งอย่างนั้นเลยล่ะ

 
GN⁺ 2024-06-10
ความคิดเห็นจาก Hacker News
  • สิ่งที่ทำให้เงิน 10,000 ดอลลาร์หายไปคือ การไม่มี monitoring แอปปล่อย exception จากฐานข้อมูลออกมาอย่างต่อเนื่องและเป็นจำนวนมาก แต่ไม่มีใครได้รับการแจ้งเตือน
    ถ้ามีการแจ้งเตือนแบบนั้น เรื่องนี้คงจบด้วย การตรวจสอบ 5 นาที ไม่ใช่การตรวจสอบ 5 วัน ถ้ายังไม่ได้แก้ระบบแจ้งเตือน ก็เท่ากับว่ายังไม่ได้แก้อะไรจริง ๆ

    • ใช่ ข้อความ log น่าจะบอกว่า ID ไม่ unique และนั่นคงลดเวลา debug ลงได้มาก
      ตอนทุกอย่างทำงานได้ดี การเขียนโปรแกรมก็ง่าย ส่วนที่ยากคือส่วนที่จัดการกับปัญหา
    • เรื่องแบบนี้พบได้บ่อยขึ้นเรื่อย ๆ บริษัทและผู้ก่อตั้งไม่คิดถึง infra เพราะเชื่อว่า cloud provider ที่เลือกไว้จะจัดการทุกอย่างให้เหมือนเวทมนตร์
      ตั้งแต่วินาทีที่มีลูกค้าจ่ายเงินเข้ามา ก็ต้องมีคนที่มีความรู้และประสบการณ์ในการจัดการ logging, monitoring, alerting, security ฯลฯ จะทำ DevOps แบบมือสมัครเล่นไม่ได้
    • การไม่มี test ก็แย่ และการเอาสิ่งที่ AI สร้างมาใช้โดยไม่ตรวจซ้ำสามรอบก็อันตราย
      แต่ส่วนที่บ้าจริง ๆ คือฐานข้อมูลไม่มี error logging และ alerting นี่ไม่ใช่โค้ด legacy อายุ 20 ปี แต่เป็นผลิตภัณฑ์ใหม่ และไม่ใช่โค้ดยุคที่ใช้ error ของ DB แทน data validation ด้วย
    • การ deploy แล้วเข้านอนทันทีดูเป็น สัญญาณอันตราย ในกรณีนี้ ควร deploy ประมาณ 9 โมงเช้า แล้ว monitor ปัญหาระหว่างเวลางาน
    • ดูเหมือน ChatGPT จะไม่ได้บอกว่าจำเป็นต้องมี monitoring
  • บทความในบล็อกขึ้น 404 เลยทิ้งลิงก์ Web Archive ไว้
    https://web.archive.org/web/20240610032818/https://asim.bear...
    ผู้เขียนได้เพิ่มการแก้ไขสำคัญไว้ว่า แนวปฏิบัติในเรื่องนี้แย่มากและน่าอายมาก และหลังจากนั้นได้เพิ่ม unit/integration test ที่แข็งแรง รวมถึง alerting/logging แล้ว สุดท้ายแล้วเป็นความผิดพลาดของมนุษย์ และเมื่อมองย้อนกลับไปก็เป็นเรื่องที่หลีกเลี่ยงได้อย่างชัดเจน
    นอกจากนี้ยังเสริมว่า เรื่องนี้เกิดขึ้นในช่วงไม่กี่สัปดาห์แรกของบริษัท ภายใต้แรงกดดันด้านเวลาสูง และขอให้มองเป็นเรื่องขำ ๆ เกี่ยวกับบั๊กที่ทำซ้ำได้อย่างประหลาดใน production

    • อาจลบไปเพราะมีปฏิกิริยาเชิงลบเยอะ
      แม้จะเป็นความผิดพลาดโง่ ๆ แต่มนุษย์ไม่ว่าจะเป็นรายบุคคลหรือเป็นกลุ่มก็ย่อมทำผิดพลาดโง่ ๆ ได้อยู่แล้ว
    • ดูเหมือนเขาจะเปลี่ยนอะไรบางอย่างอย่าง subdomain และบทความยังอยู่
      https://0912i390129ionkjan.bearblog.dev/how-a-single-chatgpt...
    • หน้า archive.org ตอนนี้โหลดไม่ขึ้น แต่ Google cache ยังมีสำเนาให้ดู
      https://webcache.googleusercontent.com/search?q=cache%3Ahttp...
  • เห็นข้อผิดพลาดได้ทันที แม้จะเคารพทีมนี้ แต่เรื่องนี้แทบไม่เกี่ยวกับ ChatGPT และเกี่ยวข้องมากกว่ากับการที่ทีมใช้ โมเดลการเขียนโปรแกรม ที่ยังไม่คุ้นเคยพอ
    ต่อให้ผ่าน code review มาแล้ว แค่มีเครื่องมือมอนิเตอร์ที่ตั้งค่าได้ภายใน 5 นาที ก็มีโอกาสสูงที่จะจับได้

    • พูดอย่างเป็นธรรม ถ้าไม่ได้ตั้งใจมองหาบั๊กนี้ก็คงไม่เห็นเหมือนกัน แต่ก็จริงที่ว่าแค่มี monitoring หรือ manual test ขั้นพื้นฐานที่สุดก็น่าจะจับได้ทันที
    • ดูเหมือนทีมจะไม่สามารถแก้ปัญหาพื้นฐานจาก log ของฐานข้อมูลหรือ log ของแอปพลิเคชันได้ด้วยซ้ำ นี่เป็นข้อผิดพลาดง่าย ๆ และน่ากังวลว่าถ้าเกิด ข้อผิดพลาดชั่วคราว อย่าง implicit lock ของตารางขึ้นมา จะรับมืออย่างไร
    • ไม่ใช่ความผิดพลาดแบบซื่อ ๆ แต่เป็นหัวข้อที่ตั้งใจทำ clickbait และปรับแต่งเพื่อ SEO โดยนัยว่า ChatGPT เป็นฝ่ายทำพลาด เพื่อกระตุ้นคลิกจากความกังวล
      หัวข้อแบบ “เราใช้ LLM แล้วเขียนโปรแกรมผิดพลาด และเพราะไม่ได้ทำ QA จึงเสียเงิน 10,000 ดอลลาร์” คงไม่ทำให้ผู้บริหารมีปฏิกิริยาแบบ “ถ้า ChatGPT ทำพัง เราจะเสีย exposure เท่าไร?” ได้ จะต้องมีผู้จัดการระดับกลางและระดับสูงจำนวนมากเอาบทความนี้ไปโพสต์บน LinkedIn แน่ ๆ
      LLM ไม่สามารถ “ทำพลาด” ได้ มันไม่ deterministic และไม่สามารถอนุมาน คิด หรือทำตรรกะได้ มันเป็นเครื่องสร้าง word salad ที่หรูหรามากโดยใช้ความน่าจะเป็นเชิงสถิติ และไม่มีหลักประกันว่าสิ่งที่สร้างออกมาจะถูกต้องหรือแม่นยำ ดังนั้นตามนิยามแล้ว การเรียกว่าเป็นความผิดพลาดก็ไม่เหมาะ
      แก้ไข: หลังจากบทความถูก downvote อย่างหนักด้วยเหตุผลที่ชัดเจน อันดับก็พุ่งขึ้นมาอย่างกะทันหัน ซึ่งดูเหมือนหมายความว่า moderator เป็นคน boost: https://hnrankings.info/40627558/
      น่าขำที่บทความ clickbait ซึ่งตามกฎควรถูกเปลี่ยนหัวข้อ กลับถูก moderator boost แถมการที่ผู้เขียนดูเหมือนจะสังกัดบริษัทของ Y Combinator ก็คงเป็นเรื่องบังเอิญล้วน ๆ: https://news.ycombinator.com/item?id=40629998
    • ที่น่าสนใจคืออีกสิ่งหนึ่งที่เจอข้อผิดพลาดนี้ก็คือ ChatGPT-4o แชตที่มีภาพแชร์ไม่ได้ แต่พอวางภาพโค้ดที่ผิดแล้วถามว่า “โค้ดนี้มีปัญหาอะไร” มันก็บอกดังนี้
      การสร้าง UUID สำหรับ primary key ไม่ควรใช้ str(uuid.uuid4()) แต่ควรใช้ callable uuid.uuid4 โดยตรง และบอกว่า SQLAlchemy จะเรียกฟังก์ชันตอนสร้างค่า ส่วนค่า default ของวันที่ server_default=text("(now())") อาจไม่ทำงานตามที่คาด จึงแนะนำให้ใช้ func.now() และให้ตรวจสอบการ import uuid กับ text ของ SQLAlchemy รวมถึงพิจารณาใช้ DateTime(timezone=True) เพื่อจัดการ timezone
      จากนั้นมันเสนอ code ที่แก้แล้วเป็น id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()), unique=True, nullable=False) ซึ่งการเพิ่ม lambda: ตรงนี้เป็นสิ่งที่แก้ปัญหา
    • ถ้าแทบไม่มีประสบการณ์ Python ก็คงคิดว่า uuid.uuid4() เป็นเหมือนการนิยาม schema ในเครื่องมืออย่าง Prisma ดังนั้นตัวบั๊กเองจึงไม่น่าแปลกใจ และผมก็อาจทำพลาดแบบเดียวกันได้
      แต่แค่รัน kubectl logs ครั้งเดียวก็น่าจะจับได้ทันทีแล้ว อีกอย่าง จาก Next.js กับ Prisma ไปใช้ Python เนี่ยนะ? ทำไม?
  • ตัวความผิดพลาดเองพอเข้าใจได้ ต่อให้เขียนโค้ดโดยไม่ใช้ ChatGPT ก็ดูเหมือนจะพลาดหลุดไปได้ค่อนข้างง่าย
    แต่ไม่เข้าใจว่าทำไมหลังจากล้มเหลวครั้งแรกแล้วยังจับไม่ได้ บริษัทนี้ไม่มี logging หรือ? ความจริงที่ว่า backend พยายาม reuse UUID ควรจะเห็นได้ทันทีจาก error

    • พวกเขาไม่รู้ด้วยซ้ำว่าเกิด error จนกว่าลูกค้าจะมาร้องเรียน ควรรู้ก่อนลูกค้าว่าเกิด error อะไรขึ้น และถ้ามี logging, alert หรือ monitoring รูปแบบใดรูปแบบหนึ่งก็น่าจะช่วยได้
    • นี่แหละคือปัญหาจริง ถ้าเป็นโปรเจกต์ที่เคลื่อนไหวเร็ว วันหนึ่งก็น่าจะเกิด production bug แบบนี้อีก หวังแค่ว่าครั้งหน้าจะไม่ใช้เวลา 5 วันในการระบุปัญหา
    • การใช้เวลา 5 วันเพื่อทำความเข้าใจบั๊กนี้นี่ค่อนข้างบ้ามาก
    • กรณีพิเศษอย่างการมี subscription ของ Stripe หลายรายการ หลุดจาก unit test ทั่วไปได้ก็พอเข้าใจได้ ตามที่บทความบอก ก็คงไม่ง่ายที่จะ reproduce ด้วย acceptance test และการไปโฟกัสที่ ChatGPT นั้นทั้งผู้เขียนและคนอื่น ๆ ต่างก็เกินไปหน่อย
      การเผลอส่ง string ให้ฟังก์ชันที่ต้องรับ callable object แทน String เป็นเรื่องที่เกิดได้บ่อย ถ้าไม่ได้ใช้ ORM ปัญหาเฉพาะนี้คงถูกหลีกเลี่ยงได้ แต่นั่นอาจเป็นอคติส่วนตัวของผมต่อ ORM ก็ได้ บั๊กคล้าย ๆ กันเกิดในบริบทที่ไม่ใช่ฐานข้อมูลได้เต็มที่
      คนที่คุยโวว่าตัวเองจับบั๊กนี้ได้แน่นอน คงเป็นวิศวกรที่เก่งกว่าผมมาก หรือที่เป็นไปได้มากกว่าคืออาจเข้าใจความสามารถตัวเองผิดไปเล็กน้อย
      อย่างไรก็ตาม การที่ ไม่มี log หรือไม่ได้ดู log นั้นเข้าใจยากจริง ๆ ถ้าเป็น ECS ผมคิดว่า exception แบบ Duplicate Key น่าจะถูกส่งต่อไป CloudWatch ได้โดยไม่ต้องตั้งค่าเพิ่มเติม เลยสงสัยว่าไม่ได้ถูกส่งไป หรือถูกส่งไปแล้วแต่ไม่มีใครตรวจดูว่าเกิด exception อะไรขึ้นตลอดทั้งคืนกันแน่
    • เพราะความล้มเหลวของกระบวนการประเภทนี้ Amazon จึงมีการแก้ไขข้อผิดพลาด หรือกระบวนการ postmortem: https://aws.amazon.com/blogs/mt/why-you-should-develop-a-cor...
      ในสถานการณ์แบบนี้ การถามว่าทำไมการตรวจพบจึงล่าช้า และทำไมการวินิจฉัยจึงใช้เวลานาน เป็นเรื่องที่มีประโยชน์
  • ผมเคยเห็นความผิดพลาดแบบเดียวกันหลายครั้งแม้ในโค้ดที่มนุษย์เขียน โดยเฉพาะใน React / TypeScript / JavaScript มักมีคนลืม lambda อยู่บ่อย ๆ
    บทความในบล็อกดูเหมือนไม่ได้อธิบายสาเหตุรากของปัญหาให้ชัด แล้วก็โยนไปว่าเป็นความผิดของ ChatGPT ทันที ถ้าทำงานรีบ ๆ แล้วเอาคอมมิตที่เป็นการเปลี่ยนแปลงใหญ่หรือไม่มีเพื่อนร่วมงานรีวิวเข้า main เรื่องแบบนี้ก็เกิดขึ้นได้
    ปัญหาจริง ๆ คือถ้าเร่งรีบ เลือกทางลัด และไม่มีการทดสอบกับการรีวิวโค้ดโดยเพื่อนร่วมงานอย่างเพียงพอ ก็จะเกิดข้อผิดพลาด แค่มีเทสต์ที่ลองตัวเลือกการสมัครหลาย ๆ แบบก็น่าจะเจอได้ทันทีแล้ว

    • โมเดลในหัวของผมต่อ ChatGPT คือ วิศวกรจูเนียร์ที่ไม่มีวันได้เลื่อนตำแหน่ง เป็นคนที่สักวันจะถูกไล่ออก เพียงแต่พิมพ์ได้เร็วแบบไม่จำกัด ดังนั้นถ้าใช้อย่างระมัดระวังมาก ๆ ก็อาจมีประโยชน์ได้
      ถ้าเอาคนแบบนี้ไปไว้ใกล้โค้ดที่สำคัญทางการเงิน ก็จะเกิดปัญหาคล้าย ๆ กัน และทำให้ผมสงสัยวิจารณญาณของคนที่ตัดสินใจ deploy โค้ดนั้นโดยแทบไม่มีการทดสอบ
    • ปัญหาแบบนี้พบได้ทั่วไปหลายที่ เช่น attribute ของคอมโพเนนต์ใน Vue อาจมีค่า default ได้ แต่ถ้าใช้ object หรือ array แบบ literal เป็นค่า default แทนฟังก์ชันที่คืน object หรือ array จะเกิดเรื่องใหญ่
      น่าแปลกที่ไม่มี lint rule สำหรับกรณีนี้
    • ChatGPT เป็นแค่ สิ่งที่เบี่ยงเบนความสนใจ เท่านั้น สิ่งสำคัญไม่ใช่ว่าโค้ดถูกสร้างโดยอะไร แต่คือคุณเอาโค้ดนั้นไปทำอะไร
    • โครงสร้างคือ ChatGPT เขียนโค้ด, push แล้วหลังจากนั้น ChatGPT ก็รีวิวด้วย /s
      หวังว่าจะไม่ใช่แบบนั้น
  • ส่วนที่ว่า “เดิมทีโปรเจกต์เป็น full-stack NextJS แต่ก่อนอื่นอยาก migrate ทุกอย่างไปเป็น Python/FastAPI” ทำให้ตาสว่างเลย
    ไม่รู้ว่าสตาร์ทอัพที่ยังไม่มีลูกค้าจะ justify การ เขียนใหม่ ได้อย่างไร

    • คิดเหมือนกันจนสงสัยว่าตัวเองบ้าหรือเปล่า ผมคิดว่าการเขียนใหม่ตั้งแต่ช่วงต้นแบบนี้ไม่สมเหตุสมผลเลย แต่เหมือนในคอมเมนต์ไม่มีใครชี้ประเด็นนี้
      ไม่ว่าจะมีลูกค้าหรือไม่ ผมก็ไม่เข้าใจว่าทำไมถึงย้ายในแนวราบจาก Node ไป Python ตั้งแต่ช่วงต้นขนาดนี้ ถ้ามีลูกค้าหลายร้อยคนแล้วกำลังจะเปลี่ยนไปใช้ Go อะไรแบบนั้นยังพอเข้าใจได้บ้าง แต่ก็ยังน่าสงสัยอยู่ดี
    • แถมยัง เขียนใหม่ ด้วยภาษาที่ประสบการณ์ไม่พอจนถึงขั้นมองบั๊กที่ดูชัดเจนไม่ออก
    • ในกรณีของผม ถ้าเป็นโปรเจกต์จริงที่กำลังทำอยู่ตอนนี้ อาจเป็นเพราะถึง C# จะเป็นภาษาที่ดี และ runtime กับ web framework ก็โอเค แต่ทำให้ความเร็วในการพัฒนาลดลงและจุดเจ็บปวดสะสมไปเรื่อย ๆ
      เช่น ต้องสร้าง DTO object เป็นกอง แต่ AutoMapper ไม่ทำงานกับชุดเวอร์ชันและการตั้งค่าโปรเจกต์ที่ผมใช้ ส่วน Entity Framework กับการ serialize/deserialize JSON ก็สร้างความเจ็บปวดมากกว่าประโยชน์ที่ได้
      แน่นอนว่าสามารถแก้แบบค่อยเป็นค่อยไปได้ ขุดเอกสารให้ลึก ผสม hack อัปเกรดแพ็กเกจ และเขียน config ใหม่ก็ทำได้ แต่ถ้าเป็นมนุษย์ก็จะอยากถือถังน้ำมันในเชิงเปรียบเทียบแล้วเผาทิ้งทั้งหมด จากนั้นทำระบบที่สองให้ดีกว่าเดิม แน่นอนว่าในความเป็นจริงมันไม่ได้ดีขึ้น แค่มีจุดเจ็บปวดแบบอื่นเกิดขึ้น และอาจทำสิ่งที่ระบบแรกเคยทำได้ไม่ครบหรือทำได้ไม่ดีด้วยซ้ำ
      ที่ทำงานก็เหมือนกัน ทุกครั้งที่เห็นระบบ legacy หรือระบบยุ่งยาก สมองจะมีแรงกระตุ้นแบบเดียวกัน ต้องใช้ความพยายามอย่างจริงจังและต่อเนื่องถึงจะเอาชนะสมองที่ตะโกนว่าให้เขียนใหม่ได้ บางครั้งการเปลี่ยนสถาปัตยกรรมอย่างการเขียนใหม่หรือการนำ container มาใช้ก็ออกมาดี แต่โดยมากแล้วมักเป็นการเดินเข้ากองไฟหรือกลายเป็นงานที่ไม่รู้จบ
      เว้นแต่จะมั่นใจสูงว่าจะช่วยปรับปรุงการปฏิบัติการของระบบหรือ developer experience ของนักพัฒนาคนอื่น ๆ ได้ การไม่ยอมแพ้ต่อแรงกระตุ้นนั้นถือว่าโชคดีแล้ว
    • ความล้มเหลวเดียวของ ChatGPT ในเรื่องนี้คือ ความสามารถของมันทำให้คนเข้าใจผิดว่าการเขียนใหม่ก่อนเปิดตัวเป็นเรื่องสมเหตุสมผลและทำได้ง่ายในการใช้ runway
  • ChatGPT น่าจะเป็นฝ่ายที่ทำให้แอปทำเงินได้ด้วยซ้ำ เพราะถ้าไม่มี ChatGPT ก็คงไม่มีความสามารถพอจะ implement
    การขาดความสามารถด้านการเขียนโค้ด ดีบัก logging และ monitoring ต่างหากที่ทำให้เสียเงิน 10,000 ดอลลาร์ และในเรื่องนี้ผลสุทธิของ ChatGPT เป็นบวก

    • ถ้าดูโปรเจกต์ของทีมนี้บน github.com/reworkd ก็จะเห็นระดับความสุกงอมของผลิตภัณฑ์และทีมได้ทันที เป็น การพัฒนาแบบขับเคลื่อนด้วยอีโมจิ
      ทุก commit message มีอีโมจิ ลิง กล้วย จรวด พลุ มีครบแทบทุกอย่าง
    • โดยเฉพาะถ้ามองว่าเป็นค่าใช้จ่ายเดือนละ 20 ดอลลาร์ ก็ยิ่งเป็นแบบนั้น
    • 10,000 ดอลลาร์เป็นเงินเล็กน้อย Elon อาจเสีย 500,000 ล้านดอลลาร์จากความผิดพลาดของ xAI ที่เพิ่งเกิดวันนี้ก็ได้
      https://grook.ai/share?id=e269e88a7b1a71eff4f176c864b30161&x...
    • ดูเหมือนว่ามีความสามารถอยู่แล้ว เพราะ implementation มีอยู่แล้ว
      เขาบอกว่าเดิมทีเป็น full-stack NextJS และระหว่าง migrate backend ไปเป็น Python/FastAPI ก็แปล database model ของ Prisma/Typescript ไปเป็น Python/SQLAlchemy งานนี้น่าเบื่อ จึงเห็นว่า ChatGPT ทำได้ค่อนข้างดีและใช้กับการ migrate เกือบทั้งหมด
      ถ้าไม่มี ChatGPT ตั้งแต่แรกก็คงไม่พยายามทำการ migrate ล่วงหน้านี้ ดังนั้นจะมองว่าผลสุทธิเป็นบวกก็ยาก สแตกเดิมอาจมี error logging ที่ดีกว่าก็ได้ หรืออาจไม่มีก็ได้ และเพราะเป็นโค้ดที่เขียนเองจึงอาจเข้าใจโครงสร้างดีกว่าและต้องการมันน้อยกว่าก็ได้
      การตัดสินใจ “เขียนโค้ดทั้งหมดใหม่เป็นครั้งที่สอง” ก่อนเปิด monetization ก็น่าสนใจอยู่เหมือนกัน
  • มีข้อความว่า “ก่อนอื่นผมอยากบอกว่าขั้นตอนปฏิบัติที่นี่แย่และสามารถหลีกเลี่ยงได้ เรื่องนี้เกิดขึ้นในช่วงเวลาอื่นที่มีแรงกดดันด้านเวลาสูง โปรดอ่านโดยคำนึงถึงจุดนี้”
    ข้อจำกัด แบบนี้ทำให้ซอฟต์แวร์แบบ subscription น่ากลัว

    • ผมนับถือมากที่ผู้เขียนเปิดเผยเรื่องนี้ โดยเฉพาะการใส่คำนำแบบนี้ การรู้ว่าคนอื่นทำพลาดอะไรมีประโยชน์มากจริง ๆ แต่การเปิดเผยความผิดพลาดของตัวเองอาจน่าอายพอสมควร
    • ถึงจะไม่ชอบ subscription แต่โลกสมัยก่อนที่ต้องซื้อไลเซนส์หลายร้อยดอลลาร์ต่อ user seat ก็ไม่ได้ยอดเยี่ยมเช่นกัน
    • เคยจัดการโค้ด subscription แบบ legacy มาก่อน และมันอาจเละเทะได้พอสมควร
      เคยมีกรณีที่เรียกเก็บเงินผู้ใช้สองครั้งเพราะ race condition ด้วย ดังนั้นพอเห็น timeout หรือ error ที่เกี่ยวกับเงิน ผมจะระแวงถึงขั้นสันนิษฐานไว้ก่อนว่ามีการชำระเงินแล้ว แล้วค่อยกลับไปตรวจสอบภายหลัง
    • ทางเลือกคือเขียนเอง ซึ่งก็จะทำให้ทุกอย่างอื่นมีข้อจำกัดตามมา
    • การ เขียนใหม่ ภายใต้แรงกดดันด้านเวลา “สูง” ก็ผิดปกติเช่นกัน
  • โค้ดที่เขียนด้วย TypeScript กับ Python, เฟรมเวิร์กอย่าง Next.js, รันงาน AWS 8 งานโดยแต่ละงานมี 5 อินสแตนซ์ แต่รายได้มีแค่ 40 ดอลลาร์ และเวลาในการพัฒนาแค่ไม่กี่สัปดาห์น่ะหรือ?
    นี่มันเกิดอะไรขึ้นกันแน่ พอแก้ตัวว่าโค้ดเละเพราะข้อจำกัดด้านเวลา แต่กลับเอาเวลาไปใช้กับการรีแฟกเตอร์ข้ามภาษาและสร้างระบบกระจายตัวโดยไม่มีเหตุผล อันนี้ยิ่งแย่กว่า
    เป็น ความซับซ้อนแบบทำร้ายตัวเอง ที่ต้องเล่นกลทั้งฟีเจอร์และความซับซ้อนทางเทคนิคที่ไร้สาระไปพร้อมกัน ไม่รู้ว่าคิดอะไรอยู่
    แก้ไข: เป็นบริษัท YC ฤดูร้อนปี 2023 แต่ถึงฤดูร้อนปี 2024 ผลิตภัณฑ์ก็ดูเหมือนยังอยู่หลังกำแพงรายชื่อรอใช้งานอยู่ อาจเป็นเพราะกำลังเขียนใหม่ด้วย Rust อยู่ก็ได้

    • เพราะมีเงินทุนตั้งต้น 500,000 ดอลลาร์ แล้วยังมีอีก 1.2 ล้านดอลลาร์อยู่ข้างบน แถมยังมี เครดิต AWS ฟรี ให้เผาทิ้งด้วย
  • ดูเหมือนเขาน่าจะยังไม่เคยเขียน Python รวมกันถึง 1,000 บรรทัดด้วยซ้ำ แต่กลับชี้ปัญหาได้ถูกต้อง
    Python มีข้อบกพร่องตรงที่ลอกกลยุทธ์การประเมินค่าของ Common Lisp มาได้ไม่ถูกต้อง ถ้ามีอาร์กิวเมนต์อย่าง foo=obj.whatever() ใน expression ค่าเริ่มต้นของอาร์กิวเมนต์ฟังก์ชันแบบเลือกได้ obj.whatever() จะถูกประเมินตอนที่นิยามฟังก์ชันถูกประมวลผล ไม่ใช่ตอนเรียกฟังก์ชัน
    ผมสงสัยว่าตั้งใจทำแบบนั้นเพื่อประสิทธิภาพ Python ยังมีข้อบกพร่องอีกอย่างคือไม่มีไวยากรณ์ literal จริง ๆ สำหรับอ็อบเจ็กต์ที่ใช้บ่อยอย่าง list [1, 2, 3] ไม่ใช่ literal แต่ใกล้เคียงกับ constructor มากกว่า และทุกครั้งที่ถูกประเมินก็ต้องสร้าง list ใหม่แล้วเติมค่าลงไป
    ผู้ออกแบบคงไม่อยากให้พารามิเตอร์อย่าง list=[] สร้าง list ว่างใหม่ทุกครั้งที่อาร์กิวเมนต์ถูกละไว้ ใน Lisp '(1 2 3) และ '() เป็น literal จริง ๆ และทุกครั้งที่ถูกอ้างถึงก็ชี้ไปยังอ็อบเจ็กต์เดียวกัน โปรแกรมเมอร์เลือกได้ว่าจะใช้ (list 1 2 3) หรือ '(1 2 3) เป็น expression ค่าเริ่มต้น
    แบบแรกจะสร้างอ็อบเจ็กต์ใหม่ที่แก้ไขได้ทุกครั้งเหมือน [1, 2, 3] ส่วนแบบหลังแทบจะแน่นอนว่าจะคืนอ็อบเจ็กต์เดิม และไม่สามารถแก้ไขได้อย่างน่าเชื่อถือและพกพาได้ ภาษาได้รับความนิยมยุคใหม่มีฟีเจอร์ส่วนใหญ่ของ Lisp กันหมดแล้ว จึงดูเหมือนมุกตลกที่ว่าไม่มีอะไรให้เสีย

    • สิ่งที่พูดนั้นถูกก็จริง แต่สิ่งที่เกิดขึ้นในบทความบล็อกไม่ใช่แบบนั้น ปัญหาอยู่ในนิยามคลาส ไม่ใช่ นิยามฟังก์ชัน
    • ประโยคที่ว่าใน foo=obj.whatever() นั้น obj.whatever() ถูกประเมินตอนประมวลผลนิยามฟังก์ชัน ไม่ใช่ตอนเรียกฟังก์ชัน ดูไม่น่าจะถูกได้เลย
      ถ้า .whatever() พึ่งพาสถานะภายในที่เปลี่ยนไปหลังจากการเริ่มต้นอ็อบเจ็กต์ แล้วมันจะเป็นอย่างไรล่ะ