2 คะแนน โดย GN⁺ 5 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • uv มีจุดเด่นด้านความเร็ว การจัดการเวอร์ชัน Python และการรวมหลายเครื่องมือไว้ในไบนารีเดียว แต่ UX ของการจัดการแพ็กเกจในช่วงบำรุงรักษายังขรุขระ
  • สามารถตรวจสอบแพ็กเกจที่ล้าสมัยได้ด้วย uv pip list --outdated แต่เพราะไม่ได้เป็นคำสั่งระดับบนสุดและอยู่ใต้เนมสเปซที่เข้ากันได้กับ pip จึงค้นพบได้ยาก
  • uv add pydantic จะเพิ่มข้อกำหนดแบบ ไม่มีขอบเขตบน โดยปริยาย เช่น pydantic>=2.13.4 ซึ่งเปิดให้ขยับข้ามเมเจอร์เวอร์ชันได้
  • การอัปเกรดทั้งหมดทำผ่าน uv lock --upgrade และหากต้องการอัปเกรดหลายแพ็กเกจเฉพาะเจาะจง ก็จำเป็นต้อง ใส่ --upgrade-package ซ้ำ ทำให้คำสั่งยืดยาว
  • สามารถตั้งค่า add-bounds = "major" เพื่อให้ได้ข้อกำหนดเริ่มต้นที่ปลอดภัยขึ้น แต่ยังเป็นฟีเจอร์พรีวิว และสำหรับแอปพลิเคชันก็ควรมี UX การอัปเดตที่ตรงไปตรงมามากกว่านี้

จุดแข็งของ uv และความไม่สะดวกในช่วงบำรุงรักษา

  • uv ของ Astral มีจุดแข็งด้านความเร็ว, การจัดการเวอร์ชัน Python และการแทนที่หลายเครื่องมือด้วยไบนารีเดียว
  • ขั้นตอนเริ่มต้นโปรเจกต์ Python ใหม่และเพิ่ม dependency ตัวแรกนั้นทำได้ง่าย แต่เมื่อโปรเจกต์เข้าสู่ช่วงบำรุงรักษา UX สำหรับการตรวจสอบแพ็กเกจที่ล้าสมัยและอัปเกรดเป็นประจำจะดูขรุขระกว่า pnpm หรือ Poetry
  • ความไม่สะดวกหลักอยู่ที่การค้นหาคำสั่งสำหรับตรวจสอบแพ็กเกจที่ล้าสมัย การไม่มีขอบเขตบนของข้อกำหนดเวอร์ชันโดยปริยาย และความยืดยาวของคำสั่งอัปเกรด

การตรวจสอบแพ็กเกจที่ล้าสมัย

  • ในโปรเจกต์ JavaScript สามารถใช้ pnpm outdated เพื่อตรวจดูแพ็กเกจที่ล้าสมัย เวอร์ชันปัจจุบัน เวอร์ชันล่าสุด และเวอร์ชันที่ข้อกำหนดอนุญาตได้อย่างกระชับ
  • ใน uv ไม่มีคำสั่งระดับบนสุด uv outdated และในตอนแรกจึงมีการใช้คำสั่งต่อไปนี้แทน
$ uv tree --outdated --depth 1
  • uv tree --outdated --depth 1 ไม่ได้กรองเฉพาะรายการที่ล้าสมัย แต่จะแสดงต้นไม้ dependency ระดับบนสุดทั้งหมดก่อน แล้วค่อยแนบหมายเหตุเล็ก ๆ ไว้ข้างรายการที่อัปเดตได้
  • แม้จะมี dependency 50 ตัวและมีแพ็กเกจล้าสมัยเพียง 2 ตัว ก็ยังต้องไล่ดูรายการ 50 บรรทัด
  • poetry show --outdated ของ Poetry แม้ชื่อคำสั่งจะเข้าใจได้ยากกว่า แต่ผลลัพธ์จริงจะแสดงเฉพาะแพ็กเกจที่ล้าสมัย

ความเสี่ยงของข้อกำหนดเวอร์ชันโดยปริยาย

  • วิธีปริยายของ pnpm และ Poetry

    • pnpm add จะบันทึกข้อกำหนดแบบแคเร็ต เช่น ^1.23.4 ลงใน package.json
    • ^1.23.4 อนุญาตเวอร์ชัน 1.x.x แต่จะไม่อัปเดตไปเป็น 2.0.0
    • Poetry ก็ใช้รูปแบบอย่าง >=1.23.4,<2.0.0 โดยปริยายเช่นกัน แม้จะอ่านยากกว่าแต่ให้ผลเหมือนกัน
    • ในสองเครื่องมือนี้ ภายใต้สมมติฐานว่าแพ็กเกจทำตาม SemVer การรัน pnpm update หรือ poetry update จะช่วยลดโอกาสที่บิลด์พังจากการเปลี่ยนแปลง API หลัก
  • วิธีปริยายของ uv

    • uv add pydantic จะเพิ่มข้อกำหนดแบบไม่มีขอบเขตบนใน pyproject.toml ดังนี้
dependencies = [
    "pydantic>=2.13.4",
]
  • ภายใต้ข้อกำหนดนี้ pydantic เวอร์ชัน 2, 3 หรือ 100 ก็ถือว่าอนุญาตทั้งหมด
  • เมื่อรันการอัปเดตครั้งใหญ่ จึงไม่ได้รับแค่การแก้บั๊ก แต่ยังอาจรับเอาการเปลี่ยนแปลงที่ทำให้ความเข้ากันได้พังซึ่งผู้ดูแลทุกตัวในกราฟ dependency ปล่อยออกมาด้วย
  • โดยเฉพาะในการบำรุงรักษาแอปพลิเคชัน สิ่งนี้อาจนำไปสู่ความเสี่ยงด้านเสถียรภาพ

UX ของคำสั่งอัปเกรด

  • ใน pnpm และ Poetry การอัปเดตทั้งหมดทำได้ง่ายดังนี้
$ pnpm update
$ poetry update
  • ใน uv จะใช้คำสั่งต่อไปนี้สำหรับการอัปเกรดทั้งหมด
$ uv lock --upgrade
  • uv lock --upgrade ไม่ได้เป็น uv update หรือ uv upgrade แต่ทำงานเป็นออปชันของคำสั่ง lock จึงไม่ค่อยตรงสัญชาตญาณเท่าไรสำหรับคำสั่งจัดการแพ็กเกจที่มนุษย์ใช้งาน
  • เมื่อรวมกับข้อกำหนดแบบไม่มีขอบเขตบน uv lock --upgrade จึงเท่ากับการยกทุกแพ็กเกจใน lockfile ไปเป็นเวอร์ชันล่าสุดที่สุดเท่าที่จะเป็นไปได้
  • การอัปเดตนี้อาจรวมถึง dependency ที่ซ้อนลึกซึ่งเราไม่ได้รู้จักโดยตรงด้วย
  • หากต้องการอัปเดตเฉพาะบางแพ็กเกจ pnpm สามารถระบุชื่อแพ็กเกจต่อกันได้ดังนี้
$ pnpm update pydantic httpx uvicorn
  • แต่ใน uv ต้องใส่แฟล็ก --upgrade-package ซ้ำสำหรับแต่ละแพ็กเกจ
$ uv lock --upgrade-package pydantic --upgrade-package httpx --upgrade-package uvicorn
  • เมื่อต้องอัปหลายแพ็กเกจพร้อมกัน การใช้แฟล็กซ้ำ กลายเป็นความยุ่งยากที่ชัดเจน

แฟล็ก --bounds และการตั้งค่า

  • เมื่อไม่นานมานี้ uv ได้เพิ่มออปชัน --bounds สำหรับ uv add
$ uv add pydantic --bounds major
  • คำสั่งนี้จะสร้างข้อกำหนดที่ปลอดภัยกว่า คือ pydantic>=2.13.4,<3.0.0
  • --bounds major ตอนนี้ยังเป็นฟีเจอร์พรีวิว และเป็นออปชันแบบ opt-in ที่ต้องพิมพ์เองทุกครั้ง
  • ต่อมาพบว่าสามารถตั้งค่าเริ่มต้นครั้งเดียวใน pyproject.toml ได้
[tool.uv]
add-bounds = "major"
  • หากใช้การตั้งค่านี้ ก็จะได้ค่าเริ่มต้นที่สมเหตุสมผลกว่าสำหรับ uv add ในครั้งต่อ ๆ ไป โดยไม่ต้องพิมพ์ --bounds major ทุกครั้ง
  • สำหรับแอปพลิเคชัน พฤติกรรมนี้ควรเป็นค่าเริ่มต้นจะดีกว่า แต่ในด้านความสะดวกใช้งานจริงก็ไม่ได้แย่อย่างที่อธิบายไว้ตอนแรกเสียทีเดียว

ความแตกต่างระหว่างแอปพลิเคชันกับไลบรารี

  • คำแนะนำมาตรฐานของ Python packaging คือ ไลบรารี ที่เผยแพร่บน PyPI ไม่ควรตรึงขอบเขตบนไว้ และคำแนะนำนี้ก็สมเหตุสมผล
  • หากทุกไลบรารีตรึงขอบเขตบนไว้ ต้นไม้ dependency ของผู้ใช้ปลายทางด้านล่างอาจไม่สามารถ resolve ได้
  • ในทางกลับกัน แอปพลิเคชัน คือโหนดปลายสุดของกราฟ dependency และไม่มีผู้ใช้รายอื่นมา resolve ตามข้อกำหนดเหล่านั้น
  • สำหรับแอปพลิเคชัน การมีขอบเขตบนไม่มีต้นทุน และช่วยป้องกันการกระโดดขึ้นเมเจอร์เวอร์ชันโดยไม่คาดคิดได้
  • ขอบเขตของบทความนี้คือการบำรุงรักษาแอปพลิเคชันอย่างเว็บไซต์ บริการ หรือเครื่องมือภายในองค์กร ส่วนในการเผยแพร่ไลบรารี ค่าเริ่มต้นแบบไม่มีขอบเขตบนอาจยังสมเหตุสมผล

ส่วนที่แก้ไขแล้วและปัญหาที่ยังเหลืออยู่

  • หากใช้ uv pip list --outdated ก็สามารถกรองดูเฉพาะแพ็กเกจที่ล้าสมัยได้
$ uv pip list --outdated
  • ด้วยเหตุนี้ คำวิจารณ์เรื่องผลลัพธ์ที่รกเกินไปของ uv tree --outdated --depth 1 จึงเบาลง
  • ปัญหาที่ยังเหลือคือฟังก์ชันนี้ไม่ได้เป็นคำสั่งระดับบนสุด แต่ไปอยู่ใต้เนมสเปซที่เข้ากันได้กับ pip จึงมีการค้นพบได้ยาก
  • สามารถกำหนด bounds เริ่มต้นได้ด้วย add-bounds = "major" ทำให้ภาพแบบเลือกเอาระหว่างต้องแก้ขอบเขตบนเองทุกครั้งหรือยอมรับความเสี่ยงนั้นไม่ตรงนัก
  • ถึงอย่างนั้น ฟีเจอร์ดังกล่าวยังเป็นพรีวิว และในการจัดการแพ็กเกจสำหรับแอปพลิเคชันก็ยังต้องการข้อกำหนดเริ่มต้นที่ปลอดภัยกว่าและคำสั่งอัปเดตที่ตรงสัญชาตญาณกว่าเดิม

แนวทางปรับปรุงที่อยากเห็น

  • ควรมีคำสั่งเฉพาะอย่าง uv outdated ที่แสดงเฉพาะแพ็กเกจที่ล้าสมัยอย่างชัดเจน
  • ควรมีคำสั่ง update ที่ใช้งานได้คล่องตัวกว่าเดิม โดยไม่ต้องใส่แฟล็กซ้ำเมื่อต้องอัปเดตหลายแพ็กเกจ
  • ข้อกำหนดเวอร์ชันโดยปริยายควรสะท้อนความคาดหวังด้านเสถียรภาพของ semantic versioning (SemVer) ได้ดีกว่านี้
  • ในสภาพปัจจุบัน ยังมีภาระที่ต้องตรวจทุกบรรทัดของการเปลี่ยนแปลงใน lockfile อย่างระแวดระวัง

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

 
GN⁺ 5 시간 전
ความเห็นจาก Hacker News
  • ช่วงเวอร์ชันเริ่มต้นของ uv add สามารถตั้งเป็น ค่าถาวร ได้ จึงไม่จำเป็นต้องส่งทุกครั้ง
    อ้างอิง: https://docs.astral.sh/uv/reference/settings/#add-bounds
    เหตุผลที่ค่าเริ่มต้นไม่ใส่ขอบเขตบนก็เพราะมันสร้างความขัดแย้งที่ไม่จำเป็นใน ecosystem มากเกินไป และตอนใช้ Poetry ก็เคยรวบรวมข้อมูลที่เกี่ยวข้องไว้เหมือนกัน: https://github.com/zanieb/poetry-relax#references

    • เข้าใจว่าการ ลบเวอร์ชันขอบเขตบน มีความสำคัญเมื่อจะเผยแพร่ไลบรารี แต่บทความนี้เขียนจากมุมของคนทำเว็บไซต์ ไม่ใช่ไลบรารี
      เวลาจัดการ dependency ในโปรเจกต์เว็บ ก็อยากมีขอบเขตบนเพื่อป้องกันการเปลี่ยนแปลงที่ทำให้พัง โดยตั้งอยู่บนสมมติฐานว่า dependency นั้นทำตาม SemVer
    • ฝั่งชุมชน Haskell ก็สู้กับปัญหานี้มาหลายปี และแนวทางที่เคยประสบความสำเร็จที่สุดช่วงหนึ่งคือ Stackage
      มันสนับสนุนไม่ให้ใส่ขอบเขตบนแบบป้องกันเกินเหตุ และคอย build ส่วนใหญ่ของ ecosystem ที่เคลื่อนไหวอยู่ทุกครั้งที่ออกรีลีส เพื่อหาปัญหาความเข้ากันได้จริง พร้อมส่งการแจ้งเตือนอัตโนมัติให้เจ้าของ และให้กำหนดเวลาที่ชัดเจนสำหรับการคงอยู่ในรีลีส "LTS" ถัดไป
      ตอนนี้ดูเหมือนว่าตัวแก้ dependency ของ Cabal เพียงอย่างเดียวก็ค่อนข้างเสถียรแล้ว แต่การมี nightly build ครอบคลุมกว้าง ๆ พร้อมความล้มเหลวและการบล็อกที่มองเห็นได้ น่าจะช่วยให้ ecosystem ยังคงแก้ dependency ได้ต่อไป
    • เพิ่งรู้จักการตั้งค่า add-bounds และคิดว่ามันมีประโยชน์กับโปรเจกต์ที่การ pin dependency แบบเป๊ะสำคัญ แต่ผู้พัฒนาที่ประสบการณ์น้อยอาจพลาดได้ โดยเฉพาะ ผลิตภัณฑ์ปลายทาง ที่ไม่ใช่ไลบรารี
    • สงสัยว่ามีวิธีตั้งค่าแฟล็ก --native-tls แบบถาวรไหม
      เพราะการตั้งค่า Zscaler ที่บริษัททำให้ UV ล้มเหลวตลอดถ้าไม่มีแฟล็กนี้
      อีกอย่างก็อยากรู้ว่ามีแผนรองรับการระบุเวอร์ชัน Python ที่เข้ากันได้กับสถาปัตยกรรมเฉพาะหรือไม่ แพ็กเกจหนึ่งที่บริษัทดูแลต้องใช้ Python 32 บิต เลยต้องส่ง --python /path/to/32bit ตลอด
    • อาจเป็นคำถามที่ยังไม่ค่อยตกผลึกนัก แต่สงสัยว่ามีวิธีทำให้ uv เคารพ exclude-newer ใน pyproject.toml ไหม
      พอรัน uv run แล้ว exclude-newer ใน pyproject.toml จะถูกลบออก
      จะให้รัน uv run —-frozen หรือ uv run --exclude-newer ทุกครั้งก็พอทำได้ แต่ไม่ค่อยรู้สึกว่าเป็น workflow ที่ถูกต้อง เลยสงสัยว่ามีวิธีที่เป็นธรรมเนียมกว่านี้ไหม
  • uv ถูกออกแบบมาให้ ไม่มีขอบเขตบน เพราะต้องการผลการแก้ dependency เพียงชุดเดียว
    npm สามารถติดตั้งผลการแก้ dependency ที่ต่างกันในคนละส่วนของ tree ได้ แต่ใน Python ไม่มีทางเลือกนั้น Rye ก็ต้องตัดสินใจแบบเดียวกัน และตรงนี้ไม่มีทางออกที่ดีกว่า
    ถ้าใส่ขอบเขตบนจริง ๆ ก็อาจทำให้เกิด tree ที่แก้ dependency ไม่ได้อีกต่อไป ในอดีต ecosystem ของแพ็กเกจ Python บางส่วนถึงขั้นต้องแจก override สำหรับแพ็กเกจเก่าที่ปล่อยออกมาโดยตั้งสมมติฐานเรื่องขอบเขตบนผิด
    ณ วันนี้ เราไม่มีทางรู้ได้ว่าแพ็กเกจที่ยังไม่ถูกปล่อยออกมาในอนาคตจะเข้ากันได้กับแพ็กเกจของเราหรือไม่

    • โดยส่วนตัวมองว่าดีกว่าที่ uv จะบอกตอนอัปเดตว่าแพ็กเกจเข้ากันไม่ได้ และถ้าจำเป็นค่อย override เอง
      ยังดีกว่าไปเจอ error จากเวอร์ชันไม่เข้ากันตอน runtime ที่ตามแกะยาก
    • การไม่มีขอบเขตบนใน pyproject.toml ไม่ใช่ปัญหาที่แท้จริง
      ปัญหาจริงคือ uv lock —-upgrade จะ อัปเกรดทุกอย่างรวดเดียว ที่ไม่มีขอบเขตบน
      ถ้ามีวิธีอัปเกรดแพ็กเกจโดยไม่ขยับเมเจอร์เวอร์ชัน คำสั่งนี้จะปลอดภัยขึ้นมาก
    • uv ทำให้หลายอย่างดีขึ้นมากแล้ว แต่โดยพื้นฐานก็ดูเหมือนยังมีหลายจุดที่เครื่องมืออย่างเดียวแก้ไม่ได้
      เทียบกับก่อนมี uv ถือว่าดีขึ้นมหาศาล แต่ก็คงยากที่จะดีได้อย่างสมบูรณ์ถ้าไม่ยอมให้ ecosystem ทั้งหมดมีการเปลี่ยนแปลงแบบทำให้ของเดิมพังบ้าง พอนึกถึงตอนย้ายจาก Python 2 ไป 3 ก็เดาว่าคงยังไม่มีใครอยากให้เกิดอะไรแบบนั้นในเร็ว ๆ นี้
    • สำหรับคนเขียนไลบรารี คำพูดนั้นถูกต้อง แต่ถ้ากำลังทำเว็บไซต์และพึ่งหลายแพ็กเกจ ก็อยากได้ ขอบเขตบน เพื่อความปลอดภัยเวลาอัปเกรด
      แฟล็ก —-bound ช่วยได้ แต่ก็เป็นอีกอย่างที่ต้องพิมพ์และต้องจำ
      ถ้า uv รู้ได้ว่าโปรเจกต์นั้นไม่ใช่ไลบรารี บางทีอาจตั้งให้ใส่ขอบเขตบนเป็นค่าเริ่มต้นได้ไหม
    • เอาจริงไม่ว่าจะ uv หรือ npm วิธีใช้ที่ถูกต้องก็คือเปลี่ยนทุกอย่างเป็น = แล้วอย่าไว้ใจว่าอัปเดตที่ไม่ใช่เมเจอร์จะไม่พัง และ อัปเดตด้วยมือเท่านั้น
  • แอปที่ใช้งานจริงตัวหนึ่งมี Python dependency 257 ตัว และกว่าครึ่งเป็น direct dependency
    ใน pyproject.toml ไม่ได้ใส่ขอบเขตบน แล้วรัน uv lock --upgrade ทุกสองสัปดาห์ผ่าน GitHub Actions
    เรามี test coverage ที่ดี ถ้ามีพัง test ก็จะ fail และยังมี workflow ช่วยรีวิวด้วย AI ด้วย พอมี PR สำหรับอัปเกรด ระบบ AI จะใช้สคริปต์ Python มาสรุปรายการอัปเดตเมเจอร์/ไมเนอร์ หาลิงก์ changelog พร้อมสรุป และวิเคราะห์ความเสี่ยงของแต่ละแพ็กเกจจากวิธีที่โค้ดเบสใช้งานแพ็กเกจนั้น
    โดยรวมแทบไม่เจ็บปวดเลย ไม่ต้องค่อย ๆ อัปทีละแพ็กเกจ ไม่ต้องไล่ดูแพ็กเกจเก่า หรือจัดการ dependency ที่ถูกปล่อยทิ้งไว้ กรณีที่ต้องรอให้ผู้เขียน dependency แก้ไขจนเราแก้ในโค้ดเองไม่ได้เกิดขึ้นน้อยมาก ประมาณปีละครั้ง ในช่วง 3 เดือนที่ผ่านมา มีการขยับเมเจอร์เวอร์ชัน 18 ครั้ง และมีแค่ครั้งเดียวที่ต้องแก้โค้ด
    อยากทำแบบนี้กับฝั่งฟรอนต์เอนด์เหมือนกัน แต่ test ยังไม่พอจะรันแบบปลอดภัยได้ การเขียน test ฝั่งแบ็กเอนด์ทำง่ายกว่าและสำคัญกว่า จึงคิดว่าทุกโค้ดเบสควรมี ถ้ามี test แล้ว ก็แค่อัปเกรดอัตโนมัติทั้งหมดได้เลย

    • การเขียน test ก็เป็นงานที่ AI agent ทำได้ดี
      อย่างน้อยก็เก่งมากในการแปลงคำสั่งภาษาธรรมชาติให้เป็น test ที่ถูกต้อง
      ช่วงหลังแทบไม่ได้เขียน test ด้วยมือตัวเองเลย และเมื่อก่อนเป็นเรื่องที่บ่นตลอด แต่ตอนนี้ไม่ใช่แล้ว
  • UV ทำอะไรให้ Python เยอะมากก็จริง แต่วันนี้ก็ต้องสู้กับมันพอสมควร
    กำลังพยายามรวมการดูแลสคริปต์ที่กระจายอยู่หลาย repository และเวลาผ่านไปก็มีรูปแบบการเขียนที่แยกกันมากขึ้น
    วิธีที่คิดไว้คือ uv run --with $package main --help และอยากให้มัน 1) ถ้าไม่มีให้ติดตั้งแล้วรันอัตโนมัติ 2) ถ้าเป็นเวอร์ชันล่าสุดอยู่แล้วก็ไม่ต้องติดตั้ง 3) ถ้าไม่ล่าสุดก็อัปเดตให้
    แต่ทั้งสามข้อกลับยุ่งยากกว่าที่คิด โดยพื้นฐานแล้ว uv run จะติดตั้งใหม่ทุกครั้ง ทำให้เสียเวลา 6 วินาทีไปกับ virtual environment และการติดตั้ง
    uvx หรือ uv tool ก็ไม่ได้ดีขึ้นมากนัก เพราะกลายเป็นเกิดปัญหาใหม่คือผู้ใช้จะไม่ได้รับการอัปเกรด
    สุดท้ายเลยต้องทำให้สคริปต์ยิง GET แบบ pagination ไปที่ CodeArtifact ถ้ามีเวอร์ชัน non-dev ที่ใหม่กว่าก็ค่อยอัปเดตแล้วรันใหม่ มันใช้งานได้ และหน่วง 200ms ก็ดีกว่า 6 วินาที แต่ก็ยังไม่ใช่ประสบการณ์ที่ต้องการ

    • uv run --with $package main --help ควรทำงานแบบที่ว่าไว้โดยมี overhead น้อยมาก เลยค่อนข้างสับสน
      มันไม่ควรติดตั้งใหม่ทุกครั้ง และ environment ของ --with ก็ควรถูกเก็บไว้ในแคช แม้ environment จะไม่ถูกแคช แต่ dependency ก็ควรถูกแคช และการติดตั้งจากแคชก็ควรเร็วมาก น่าจะต่ำกว่า 200ms ได้แน่
      ถ้าเปิดตัวอย่างที่ทำซ้ำปัญหาได้แบบละเอียด จะช่วยดูให้ได้
    • ถ้าเป็น use case นั้น uv tool install กับ uv tool upgrade ดูจะเหมาะกว่า
      แต่ความติดขัดเล็ก ๆ แบบนี้น่าจะแก้ได้ไม่ยากนัก จึงอยากให้เปิด issue ไว้
    • อีกวิธีคือกำหนด dependency ที่ต้องใช้ไว้ใน document block ด้านบนของไฟล์ แล้วรันแค่ uv run main
      มันจะติดตั้ง dependency ที่ต้องใช้แบบอัตโนมัติ แคชไว้ และรันให้: https://docs.astral.sh/uv/guides/scripts/
    • หรือให้ผู้ใช้รัน uv tool upgrade เองก็ได้ไม่ใช่หรือ
    • https://copier.readthedocs.io/en/stable/ ก็น่าลองดู
      ไม่แน่ใจว่าเป็น use case เดียวกันเป๊ะไหม แต่เคยใช้ซิงก์ ecosystem ของ polyrepo microservices แล้วดีมาก
  • ค่อนข้างแปลกใจที่มีการแนะนำ "uv tree --outdated --depth 1" เพื่อดูรายการ dependency เก่า
    ส่วนตัวใช้ "uv pip list --outdated" มาตลอดตั้งแต่มีให้ใช้
    แต่ก็เห็นด้วยว่าคำสั่งที่สำคัญระดับนี้น่าจะมี subcommand ระดับบนสุด แยกออกมาต่างหาก

    • ในมุมของคนเขียน นั่นไม่ใช่คำแนะนำเท่าไร แต่เป็นวิธีเดียวที่ตัวเองรู้
      ยอมรับว่า "uv pip list --outdated" ให้ output ที่ดีกว่ามาก ขอบคุณ
      แต่การที่มีวิธีดูแพ็กเกจเก่าถึง 2 วิธีและ output ต่างกันมาก ก็ทำให้รู้สึกว่า UX มันแย่อยู่ดี
    • "uv tree -od1" ก็น่าจะใช้ได้เหมือนกัน
      แต่คำวิจารณ์ที่มักมีต่อ package manager อย่าง pacman ก็คล้ายกัน คือควรมีคำสั่งที่คนเข้าใจง่ายสำหรับสิ่งที่ใช้บ่อยแบบ apt
  • เมื่อเทียบกับคำว่า “แย่” ในชื่อเรื่อง ตัวอย่างที่ยกมาดูเหมือนเป็นแค่การต้องใส่อาร์กิวเมนต์เพิ่มอีกไม่กี่ตัว
    ชื่อที่เหมาะกว่าน่าจะประมาณ สิ่งปรับปรุงเล็ก ๆ น้อย ๆ ที่อยากให้มีใน UV

    • คำแบบนั้นรวมถึงวลีอย่าง “ใครเป็นคนออกแบบ command-line interface นี้” ดูเหมือนตั้งมาเพื่อดึงความสนใจและคลิก
      ตัว feedback เองมีประโยชน์และส่วนใหญ่ก็เห็นด้วย แต่ถ้อยคำแบบนั้นลดคุณค่าของ feedback และชวนให้คนตั้งการ์ดป้องกัน
      โดยส่วนตัวก็คิดว่า command-line interface ของ uv ใช้งานจุกจิกอยู่บ้าง แต่ก็เข้าใจว่าทำไมมันถึงถูกเขียนมาแบบนี้
  • uv ยอดเยี่ยมก็จริง แต่ปัญหาใหญ่ที่สุดของ Python packaging ตอนนี้ก็ยังเป็นเรื่องการจัดการ แพ็กเกจวิทยาศาสตร์/แมชชีนเลิร์นนิง ให้ดี
    ถ้าอยากติดตั้ง PyTorch ก็ต้องคิดก่อนว่าเป็นเวอร์ชันอะไร ใช้ CUDA หรือไม่ ถ้าใช้ CUDA ก็ยังมีแยกตามเวอร์ชัน CUDA อีก 6 แบบ และไฟล์ wheel ก็ใหญ่เกินกว่าจะอัปขึ้น PyPI ได้ จึงต้องโหลดโดยตรง
    Conda ช่วยแก้ปัญหานี้ได้เพียงบางส่วน Spack ปรับแต่งได้อย่างมากและสามารถเตรียม C/C++/Fortran dependency ที่ต้องใช้ รวมถึง compiler toolchain เพื่อดึงประสิทธิภาพสูงสุดได้ แต่ก็ผสานกับ uv และเครื่องมืออื่นได้ไม่ดีนัก เลยทำให้การนำโปรเจกต์แมชชีนเลิร์นนิงเชิงทดลองที่นักวิจัยสร้างไว้ไปสู่ production เป็นเรื่องยาก

    • เมื่อก่อนก็เลี่ยงไปใช้ Anaconda แต่ของแถมที่ติดมามีมากเกินไป และสภาพแวดล้อมพัฒนากับสภาพแวดล้อม production ก็ต่างกันมาก เลยไม่ค่อยเวิร์กเหมือนกัน
      สุดท้ายก็ย้อนกลับมาสู่สถานการณ์แบบที่พูดไว้ตอนแรก
  • มี feedback ที่เป็นประโยชน์เยอะ แต่ก็ปนถ้อยคำแบบคลิกเบตอยู่บ้าง
    เรื่อง pnpm outdated ดูเป็นคำขอที่สมเหตุสมผล แม้ที่ผ่านมาอาจไม่ได้ถูกพูดถึงบ่อยนัก น่าจะมาจากความต่างของวัฒนธรรมระหว่าง Python กับ JavaScript ส่วนตัวแทบไม่เคยสนใจเลยว่า dependency เก่าหรือไม่ ตราบใดที่มันไม่เปราะบางหรือไม่พัง แต่ใน ecosystem ของ JavaScript ดูเหมือนการอัปเกรดเมื่อมีโอกาสเป็นเรื่องค่อนข้างปกติ ไม่ได้บอกว่าสิ่งนี้ไม่ดี แค่ชี้ให้เห็นว่าสัญชาตญาณเรื่องควรเปิดอะไรไว้ใน command-line interface นั้นต่างกันได้มากระหว่างชุมชนโปรแกรมมิงขนาดใหญ่
    อย่างที่ Armin พูด พฤติกรรมเรื่องขอบเขตบนของ uv เป็นสิ่งที่ตั้งใจไว้ และมีความจำเป็นในเชิงฟังก์ชันตามวิธีที่ Python แก้ dependency นี่เป็น trade-off ที่ Python เลือกเมื่อเทียบกับภาษาอื่น แต่ก็เป็น trade-off ที่ดีตรงที่ dependency tree จะมีแต่ละ dependency เพียงชุดเดียว และทุก requirement ที่เกี่ยวข้องกันจะถูกแก้ให้ลงตัวอยู่ในนั้น
    เหตุผลที่ uv lock --upgrade เป็นแบบนั้นก็เพราะมันอัปเกรด lock file ไม่ใช่ requirement ของผู้ใช้เอง ในขณะที่ pnpm update ดูเหมือนจะอัปเดต requirement ของผู้ใช้ใน package.json อาจฟังดูสับสนได้ แต่การวางไว้ใต้ uv lock ถือว่าถูกต้องกว่า ไม่เช่นนั้นก็จะมีผู้ใช้ที่งงว่า uv upgrade ไม่ได้อัปเกรดในแบบที่ตัวเองคิดว่าควรเป็น ถึงอย่างนั้นก็ยังมีช่องให้ทำให้นำเสนอได้สะอาดกว่านี้ และเห็นได้ชัดว่ามีความต้องการจากผู้ใช้สำหรับ subcommand ของ uv ที่เอาไว้อัปเกรด requirement เองโดยตรง
    https://news.ycombinator.com/item?id=48230048

    • เห็นด้วยเรื่องแพ็กเกจเก่าและขอบเขตบน แต่ถ้าผู้ใช้บ่นว่า interface ใช้ยาก ก็ต้องมีอะไรบางอย่างจริง ๆ
      คำสั่ง uv lock ที่แตะเฉพาะ lock file นั้นฟังดูสมเหตุสมผล แต่ผู้ใช้มีความต้องการจริงในการอัปเกรด direct dependency และ transitive dependency สำหรับ transitive dependency ทำได้ด้วย uv lock --upgrade-package แต่ก็ค่อนข้างยืดเยื้อ มันใช้กับ direct dependency ได้เหมือนกัน แต่จะไม่แก้ pyproject.toml และไฟล์นี้เป็นสิ่งที่นักพัฒนามองเห็นได้ชัดกว่ามาก
      ถ้าเวอร์ชันแพ็กเกจใน uv.lock นำหน้า pyproject.toml ไปแล้ว pyproject.toml ก็จะเชื่อถือได้น้อยลงในฐานะคู่มือบอกภาพรวมของ dependency จึงอยากได้คำสั่ง uv upgrade ที่เป็นมิตร
      กับดัก UX ที่ใหญ่ที่สุดของ uv เท่าที่เห็นจนถึงตอนนี้คือ uv pip หลายโปรเจกต์ใช้ uv ได้ถูกต้องในงานพัฒนา ผ่าน pyproject.toml และ uv.lock แต่กลับใช้ uv pip install -r pyproject.toml ใน Dockerfile สำหรับ deploy หรือในเครื่องมือ CI ซึ่งเป็นการข้าม uv.lock ไป
      ปัญหาหนึ่งคือ coding agent เห็น pip มากเกินไปในข้อมูลฝึกจนแนะนำแพตเทิร์น uv pip ที่ไม่ดี แต่ uv เองก็ควรมีมาตรการช่วยปกป้องผู้ใช้ด้วย
      uv เป็นเครื่องมือที่ยอดเยี่ยมและควรถูกใช้อย่างแพร่หลายมากขึ้น: https://aleyan.com/blog/2026-why-arent-we-uv-yet
    • ถ้าในมุมของผู้เขียนดู “เหมือนคลิกเบต” ก็ขออภัย แต่จริง ๆ แล้วมันเป็นแค่ ความตรงไปตรงมาและซื่อสัตย์แบบดัตช์
      poetry update ก็อัปเดต lock file เช่นกัน ผมคิดว่าวิธีจัดองค์ประกอบของ CLI ใน uv ทำงานด้วยค่อนข้างน่ารำคาญ มันให้ความรู้สึกเหมือนออกแบบมาเพื่อความแม่นยำและเพื่อเครื่อง มากกว่าจะออกแบบมาให้เป็นมิตรกับผู้ใช้
    • ในฐานะคนที่ย้ายจาก pip มา uv เวลาต้องใช้ข้อมูลนั้นก็ยังกลับไปใช้ uv pip list --outdated
    • uv upgrade อยู่ใน roadmap
      ที่ยังไม่ได้ทำเพราะการทำให้เป็นประสบการณ์ที่ดีนั้นยากมาก มีรายละเอียดปลีกย่อยมากกว่าที่คนคิด ทีมก็เล็ก และมีลำดับความสำคัญอื่นอีกมาก
    • หนึ่งในประโยชน์ของฟีเจอร์แบบ pnpm outdated คือใช้ดูว่าเมื่อรัน "uv sync --update" หรือ "uv lock --update" แล้วอะไรบ้างที่จะถูกอัปเดต
      แต่อาจจะดีกว่าถ้าคำสั่งเหล่านั้นมี prompt ยืนยันมาก่อน
  • Pixi ใช้ uv เป็น backend และผมชอบ UI ของมันเพราะเพิ่ม task alias เพื่อแสดงรายการแพ็กเกจเก่าให้อ่านง่ายได้สะดวก
    โดยเฉพาะ Pixi-diff-to-markdown ที่ช่วยให้ไล่ดูการอัปเดตแพ็กเกจอัตโนมัติใน CI ได้ง่าย
    ถ้าอยากดูว่าในบรรดาแพ็กเกจเก่า มีตัวไหนบ้างที่จะถูกอัปเดต ก็สร้าง task alias แบบนี้ในโปรเจกต์ได้
    pixi task add outdated "pixi update --dry-run --json | pixi exec pixi-diff-to-markdown"
    แล้วในโปรเจกต์ก็รัน pixi run outdated
    output จะเป็นตาราง Markdown ที่อ่านง่าย ซึ่งมีแพ็กเกจที่จะอัปเดต เวอร์ชันเดิม และเวอร์ชันใหม่ที่จะถูกติดตั้งด้วยคำสั่ง pixi update แน่นอนว่าก็แล้วแต่รสนิยมและบริบท

  • ไม่นานมานี้มีสคริปต์ env โผล่มาใน path จนไปรบกวนการใช้คำสั่ง UNIX ปกติอย่าง env
    พอไปดูถึงรู้ว่าโปรแกรมติดตั้งของ uv เป็นคนสร้างมันขึ้นมาตอนรันคำสั่งนี้
    curl -LsSf [https://astral.sh/uv/install.sh](<https://astral.sh/uv/install.sh>;) | sh
    มันติดตั้งลงใน $HOME/.cargo/bin/ และสร้างเชลล์สคริปต์ไว้ที่ $HOME/.cargo/env เพื่อเติม $HOME/.cargo/bin/ ไว้ข้างหน้าถ้า PATH ยังไม่มี
    เข้าใจได้ยากจริง ๆ ว่าทำไมนักพัฒนาบางคนถึงเขียนโค้ดที่ไปทับคำสั่ง UNIX พื้นฐานแบบนี้