- โปรเจกต์ .NET Unified Build คือระบบบิลด์รูปแบบใหม่ที่รวมผลิตภัณฑ์ทั้งหมดให้อยู่ในรูปแบบ “คลังเก็บแบบเสมือนที่เป็นหนึ่งเดียว (Virtual Monolithic Repository, VMR)” เพื่อลดความซับซ้อนและความไม่มีประสิทธิภาพของโครงสร้างการบิลด์เดิมที่อิงกับคลังเก็บแบบกระจาย
- วิธีการจัดองค์ประกอบผลิตภัณฑ์แบบ กระจายศูนย์ เดิม แม้จะมีความเป็นอิสระและความยืดหยุ่นสูง แต่ก็สร้างภาระอย่างมากในด้าน การจัดการ dependency, ความสอดคล้องของการบิลด์, และความเร็ว
- Unified Build ขยายแนวคิดจาก Source Build สำหรับ Linux distribution โดยนำโครงสร้างซอร์สแบบเดียวและสถาปัตยกรรม “Vertical Build” มาใช้ เพื่อลดเวลาบิลด์และทำให้คาดการณ์ได้มากขึ้น
- ผ่าน two-way code flow , scenario test, และ การปรับปรุงโครงสร้างพื้นฐานด้านการตรวจสอบอัตโนมัติและการเซ็นกำกับ ช่วยยกระดับทั้งประสิทธิภาพของนักพัฒนาและคุณภาพของผลิตภัณฑ์ไปพร้อมกัน
- ระบบนี้ถูกนำมาใช้อย่างเป็นทางการใน .NET 10 และสร้างผลลัพธ์ที่ชัดเจน เช่น ลดเวลาบิลด์ (24 ชั่วโมง→ต่ำกว่า 7 ชั่วโมง) , ลดต้นทุนการดูแลรักษา, และ เพิ่มความน่าเชื่อถือของการส่งมอบ
เบื้องหลังการเปลี่ยนแปลงโครงสร้างการบิลด์ของ .NET
- ระหว่างการโอเพนซอร์ส .NET ในช่วงปี 2015~2016 ได้มีการแยกพัฒนาออกเป็นหลายคลังเก็บ เช่น CoreCLR, CoreFX, ASP.NET Core, SDK
- แต่ละคลังเก็บบิลด์และส่งมอบแยกจากกัน และตัวผลิตภัณฑ์ทั้งหมดจะถูกประกอบเข้าด้วยกันผ่าน dependency graph
- วิธีนี้คล้ายกับระบบนิเวศ OSS แต่เมื่อมี security patch หรือ hotfix ด่วน จะต้องประสานหลายทีมพร้อมกัน ทำให้เกิดปัญหา ไม่สามารถคาดการณ์เวลาได้
- แม้วิธีพัฒนาแบบกระจายจะมีข้อดี (layering, การแยกชุมชน, การพัฒนาแบบ asynchronous ฯลฯ) แต่กลับไม่มีประสิทธิภาพเมื่อต้องรักษา ความสอดคล้องของผลิตภัณฑ์ (coherency)
ความซับซ้อนของการประกอบผลิตภัณฑ์และ overhead
- ความซับซ้อน (Complexity) : นิยามว่าเป็นจำนวนขั้นตอนที่ต้องผ่านก่อนที่การเปลี่ยนแปลงจะไปถึงมือลูกค้า
- ยิ่งมีคลังเก็บและ dependency node มาก ก็ยิ่งต้องใช้เวลาและการประสานงานจากคนมากขึ้นเพื่อรักษาความสอดคล้อง
- โอเวอร์เฮด (Overhead) : เวลาที่ไม่ได้สร้างผลลัพธ์ที่ส่งถึงลูกค้าได้โดยตรง
- เช่น การสร้าง PR, การรออนุมัติ, การเข้าคิว, การตั้งค่าสภาพแวดล้อม
- จากการวิเคราะห์การบิลด์ runtime ของ .NET 8 พบว่า ประมาณ 38.5% ของเวลาบิลด์ทั้งหมดเป็น overhead
- เมื่อความซับซ้อนและ overhead รวมกัน ประสิทธิภาพการบิลด์จะลดลงอย่างรวดเร็ว และทำให้วงจรรีลีสโดยรวมยาวขึ้น
ที่มาของ Source Build และ Unified Build
- Source Build เป็นระบบที่ออกแบบมาเพื่อให้ Linux distribution สามารถ บิลด์ .NET แบบออฟไลน์จากซอร์สเดียว ได้
- ยึดหลักการ implementation เดียว, platform เดียว, และสภาพแวดล้อมการบิลด์เดียว
- build orchestrator จะจัดการ dependency ของแต่ละองค์ประกอบและลำดับการบิลด์
- Source Build สามารถบิลด์ได้ภายใน 50 นาที ด้วย ความซับซ้อนต่ำและ overhead ต่ำ
- Microsoft พยายามนำแนวคิดนี้มาใช้กับการบิลด์ภายในด้วย แต่ติดปัญหาเรื่อง ซอร์สปิด, dependency แบบ legacy, และ join ข้ามแพลตฟอร์ม
- เพื่อแก้ปัญหานี้ จึงมีการนำแนวทางอย่าง reference-only packages (source-build-reference-packages) , หลักการ implementation เดียว, และ การตัด join ออก มาใช้
เป้าหมายและการออกแบบของ Unified Build
- บิลด์ผลิตภัณฑ์ .NET ทั้งหมดได้จาก commit เดียว
- สร้าง distribution สำหรับทุกแพลตฟอร์มได้จากสภาพแวดล้อมเดียว
- ให้บุคคลภายนอก Microsoft สามารถบิลด์และตรวจสอบได้อย่างอิสระ
- ซิงก์โค้ดแบบสองทาง ระหว่าง VMR และคลังเก็บรายองค์ประกอบโดยอัตโนมัติผ่าน two-way code flow
- ดำเนินการตรวจสอบฟังก์ชันในระดับผลิตภัณฑ์ทั้งหมดผ่าน scenario test
- ใช้โครงสร้าง Vertical Build เพื่อทำการบิลด์แยกตามแพลตฟอร์มแบบขนาน
- ประกอบด้วย build vertical ประมาณ 35~40 เส้น (short/tall stack)
ขั้นตอนการนำ Unified Build ไปใช้
- .NET 7: ออกแบบแนวคิดและได้รับการอนุมัติ
- .NET 8: ปรับปรุงโครงสร้างพื้นฐานของ Source Build และวางรากฐาน
- .NET 9: ทดลอง vertical build และ code flow
- .NET 10: นำมาใช้เป็นผลิตภัณฑ์จริงและรวมเข้าใน RTM
- เริ่มใช้กระบวนการบิลด์ใหม่ใน Preview 4 และเปลี่ยนผ่านเต็มรูปแบบตั้งแต่ Preview 5
องค์ประกอบหลัก
Virtual Monolithic Repository (VMR)
- คลังเก็บ dotnet/dotnet ทำหน้าที่เป็น เลย์เอาต์ซอร์สเดียวสำหรับทุกองค์ประกอบ
- นักพัฒนาสามารถทำงานได้ทั้งในคลังเก็บรายตัวหรือใน VMR ทำให้ได้ทั้ง ความยืดหยุ่นของแบบกระจาย และ ความสอดคล้องของแบบรวมศูนย์
Vertical Build
- ดำเนินการบิลด์แยกอย่างอิสระตามแต่ละแพลตฟอร์มและ runtime
- หลังจากบิลด์แบบขนานแล้วจึงรวมผลลัพธ์เข้าด้วยกัน โดย join บางส่วนจะจัดการผ่าน build pass เพิ่มเติม
Code Flow
- การซิงก์โค้ดสองทาง: คลังเก็บองค์ประกอบ → VMR, VMR → คลังเก็บองค์ประกอบ
- บันทึกสถานะ code flow ล่าสุดไว้ใน
eng/Version.Details.xml
- สร้างการเปลี่ยนแปลงเป็นไฟล์แพตช์และเปิด PR อัตโนมัติ
- มีตรรกะสำหรับจัดการความขัดแย้งและกู้คืนจากข้อผิดพลาดในตัว
Scenario Test Validation
- นอกเหนือจาก unit test เดิม ยังเพิ่ม scenario test ที่ตรวจสอบความสามารถของผลิตภัณฑ์ทั้งระบบ
- การทดสอบทำงานบนพื้นฐานของผลลัพธ์จากการบิลด์ ช่วยเสริมการป้องกัน regression และการรับประกันคุณภาพ
ผลลัพธ์และผลกระทบ
- ลดเวลาบิลด์: มากกว่า 24 ชั่วโมง → ต่ำกว่า 7 ชั่วโมง (รวมการเซ็นกำกับ)
- เพิ่มความยืดหยุ่น: รอบการบิลด์และส่งมอบสั้นลง รองรับการแก้ไขด่วนได้ง่ายขึ้น
- เพิ่มความสามารถในการคาดการณ์: รู้ได้ชัดเจนว่าหลังเปลี่ยนแปลงแล้วจะบิลด์เสร็จเมื่อไร
- ปรับปรุงโครงสร้างพื้นฐาน: เครื่องมือเซ็นกำกับ, log, การบิลด์แบบขนาน, การทำ dependency flow อัตโนมัติ ฯลฯ
- Source Build สำหรับ Linux distribution ก็สามารถรักษาสถานะ pre-build clean ได้อยู่เสมอ
ทิศทางต่อไป
- ใน .NET 11 มีแผนนำ ระบบอัตโนมัติของ code flow และ monitoring agent ที่ใช้ AI มาใช้
- เพื่อทำให้การติดตามข้อผิดพลาดในกระบวนการตั้งแต่ PR → การรวมเข้าสู่ผลิตภัณฑ์เป็นอัตโนมัติ
- ระยะยาวจะผลักดัน การตัด join point ออก เพื่อทำให้การบิลด์ง่ายขึ้นและเร็วขึ้น
- เป้าหมาย: บิลด์ครบทั้งหมดให้เสร็จภายใน 4 ชั่วโมง
บทสรุป
- Unified Build ที่เข้ามาแก้ข้อจำกัดของโมเดลการบิลด์แบบกระจาย ได้ยกระดับประสิทธิภาพการบิลด์และการส่งมอบของ .NET อย่างเป็นรากฐาน
- มีความก้าวหน้าชัดเจนในสามด้าน คือ ลดความซับซ้อนและ overhead, เพิ่มความสอดคล้อง ความเร็ว และคุณภาพ
- ตั้งแต่ .NET 10 RTM เป็นต้นไป ระบบนี้ถูกนำมาใช้เต็มรูปแบบ และจะได้รับการปรับปรุงต่อเนื่องในเวอร์ชันถัดไป
1 ความคิดเห็น
ความเห็นบน Hacker News
ฉันนับถือ ทีม .NET มากจริงๆ
พวกเขาออกบทความเชิงเทคนิคที่ลึกอยู่บ่อยๆ และหมกมุ่นกับการ ปรับแต่งประสิทธิภาพ อย่างมาก (เช่น พัฒนาการของ Kestrel และ Entity Framework)
ASP.NET เป็นหนึ่งในไม่กี่โปรเจ็กต์ขนาดใหญ่ที่รอดผ่านการเปลี่ยนแปลงระดับใหญ่แบบ Python 2→3 มาได้
เมื่อก่อนมันพึ่งพาฟีเจอร์เหมือนเวทมนตร์เพื่อซิงก์เซสชัน แต่ตอนนี้ทำงานด้วยวิธีที่ต่างออกไปโดยสิ้นเชิง
รู้สึกดีที่มีบริษัทมูลค่า 3 ล้านล้านดอลลาร์พยายามปรับปรุงสแตกที่ฉันใช้อย่างจริงจัง
เลยเปลี่ยนไปใช้ Dapper กับระบบ migration ที่ทำเอง แล้ว เวลา verify DB และ seeding ตอนเริ่มต้นลดจาก 10 วินาที → ต่ำกว่า 2 วินาที (บนฮาร์ดแวร์สเปกต่ำ + SQLite)
query ที่ Entity สร้างมี multi-join ที่ไม่จำเป็นเยอะมาก
ช่วงนี้กำลังย้ายไปใช้ Go backend แบบเรียบง่ายมากขึ้น และใช้ .NET กับโซลูชันอื่นแทน
เฟรมเวิร์กอย่าง WPF ก็มีปัญหาเยอะถึงขั้นต้องแฮ็ก Win32
ตัวอย่างเช่น เพิ่งมาถึง .NET 9 ที่คืนค่าทุก network interface ได้ถูกต้อง ก่อนหน้านี้ runtime จะแสดงเฉพาะ NIC ที่ active
ยังมีโปรเจ็กต์ที่ต้องคงการรองรับ Windows 7 อยู่
แม้แต่โปรเจ็กต์ใหม่ก็ยังมีกรณีที่ต้องใช้ .NET 4.8 เช่น ตอน build สูตร Excel จะเกิดปัญหา async/await deadlock
อีกทั้งฟีเจอร์ integration กับ Windows ก็พัง ทำให้ network call เดียวกัน authenticate ได้บน 4.8 แต่ล้มเหลวบน Core
การ พังของ backward compatibility ในไลบรารีจำนวนมากทำให้ migration ไม่ง่าย
ประสิทธิภาพดีขึ้นก็จริง แต่ในแง่ฟีเจอร์กลับรู้สึกว่า .NET Framework กลายเป็นฟอสซิลไปแล้ว
กว่าจะมี JSON serializer ใน standard library ก็ใช้เวลา 15 ปี และก็ยังไม่มีการรองรับฟอร์แมตภาพสมัยใหม่อย่าง webp หรือ heic
Visual Studio เด้งข้อความแครชแทบทุกวัน
เมื่อก่อนฉันเป็นแฟน .NET ตัวยง แต่คิดถึง ภาวะผู้นำของ Anders
ช่วงหลังฉันพัฒนา C# REST API backend บน VSCode ของ macOS สำหรับ side project และ deploy บน Linux มา 3 ปีแล้ว
ใช้ SQLite, EFCore, Minimal API แล้วรู้สึกสบายกว่าฝั่ง frontend (NextJS/React/MaterialUI, npm package มากกว่า 50 ตัว) มาก
ส่วนตัวเป็น API framework ที่ชอบที่สุด
ตัวเลือกรองคือชุด TS + Hono + Zod-OpenApi + SwaggerUI แต่การตั้งค่า type context ค่อนข้างจุกจิก
ฉันประทับใจที่รากฐานของการทำให้ .NET เป็นโอเพนซอร์สและข้ามแพลตฟอร์ม คือระบบ build ของ Linux distribution
เพราะงั้นจึงต้องมีระบบ build ที่ตอบโจทย์ความต้องการของพวกเขา และสุดท้ายการรวมเข้ากับ โมเดลของ Linux distribution ก็เป็นทางเดียว
โมเดลนี้เรียบง่ายแต่ประสิทธิภาพต่ำกว่า ระบบ distributed build ที่มี caching จะเร็วกว่า แต่ไม่เข้ากับ workflow ของผู้ดูแลแพ็กเกจ
เราตัดสินใจว่าการ optimize เพื่อความเรียบง่ายดีกว่าสำหรับการดึงการมีส่วนร่วมจากชุมชน
เป้าหมายคือทำให้ชุมชน build และแจกจ่ายเองได้บนแพลตฟอร์มหลากหลายอย่าง BSD, S390x เป็นต้น
ปริมาณของ pull request และผลลัพธ์เชิงบวก ตลอด 10 ปีที่ผ่านมาน่าทึ่งมาก
ไม่คิดเลยว่าบทความด้าน วิศวกรรมซอฟต์แวร์ ที่น่าประทับใจที่สุดที่อ่านในปีนี้จะมาจาก Microsoft
ฉันชอบ .NET เวอร์ชันหลังๆ แต่เคยคิดว่าความแข็งแกร่งของมันเป็นผลลัพธ์จากความบังเอิญ
แต่บทความนี้แสดงให้เห็นถึง ความพยายามอย่างเป็นระบบเพื่อยกระดับคุณภาพ (รวมถึงไดอะแกรมและตัวอย่างการใช้ LLM)
ต่อให้ในอนาคตการลงทุนแบบนี้จะลดลง นี่ก็ยังเป็นตัวอย่างที่ดีของคำว่า “ควรทำอย่างไร”
คนที่มีส่วนร่วมในโปรเจ็กต์นี้คงได้ประสบการณ์ที่สุดยอดมาก
บทความดี แต่ฉันคิดว่าทีม .NET ควรเลิกใช้ Azure DevOps
เวลารอคิวคือคอขวดที่ใหญ่ที่สุด ควรใช้ เซิร์ฟเวอร์ build แบบ bare metal
ฮาร์ดแวร์ Mac เชื่อมต่อและเริ่มทำงานได้เร็ว
เราสร้าง VM ใหม่ที่สะอาด สำหรับแต่ละงานเพื่อให้ได้ทั้ง compliance และความเสถียร
ถ้าคงเครื่อง hot ที่เตรียมพร้อมไว้ล่วงหน้าก็จะตัดเวลารอได้ แต่ ต้นทุน สูงเกินไป
การทำให้ SKU หลากหลายพร้อมรอตลอดเวลานั้นไม่ค่อยเป็นไปได้ในทางปฏิบัติ เลยต้องประนีประนอม
ฉันสงสัยว่าทำไม Developer Division ของ Microsoft ถึงเก่งระดับแนวหน้าของอุตสาหกรรม แต่แผนกอื่นกลับกลายเป็นสัญลักษณ์ของความไร้ประสิทธิภาพและระบบราชการ
หลังจาก Bill ออกไป วัฒนธรรมการหันกลับมาทบทวนตัวเองก็หายไป
ฉันคิดว่าต้นตอของปัญหาคือ แนวคิดที่ยึด abstraction เป็นศูนย์กลาง ซึ่งพยายามทำให้ระบบซับซ้อนดูเรียบง่าย
แทนที่จะพยายามแก้ทุกอย่างจากเลเยอร์บน วิธีที่ค่อยๆ สร้างจากเลเยอร์ล่างขึ้นมาสามารถกำจัดปัญหาได้ถึงรากมากกว่า
พอเห็นประโยคที่ว่า “ในหนึ่งเดือน build major version 3~4 ตัวและ SDK band อีกหลายสิบตัว” ก็สงสัยว่าทำไมถึงมี ความหลากหลายของสายพันธุ์ มากขนาดนี้
และแต่ละเวอร์ชันก็ต้อง build SDK/aspnet/runtime สำหรับแพลตฟอร์มหลากหลาย เช่น x64/arm32/arm64, Linux/macOS/Windows
ก่อนที่ Node จะบูม .NET เป็น ตัวเลือกฝั่งแบ็กเอนด์ที่แข็งแรง (และประสิทธิภาพก็ดีกว่า Node)
หลังเหตุการณ์ supply chain attack ใน ecosystem ของ Node ช่วงหลัง หวังว่านักพัฒนาจำนวนมากจะกลับมาหาแพลตฟอร์มที่เสถียร
การเปลี่ยนแปลงใหญ่คือการย้ายไป .NET Core แต่เรื่องนั้นก็เกือบ 10 ปีแล้ว
การอัปเดต framework และ dependency อาจจะจุกจิกนิดหน่อย แต่ก็ง่ายกว่าการอัปเดตโปรเจ็กต์ React มาก
ด้วยรอบ LTS ที่สั้น การตามเวอร์ชันล่าสุดจึงไม่ยาก ในรอบการพัฒนาที่เร็ว การดูแลแบบนี้เป็นเรื่องปกติ
แต่ความจริงตอนนี้ยังวนอยู่กับ Node.js, Java และ low-code (iPaaS)
ถึงอย่างนั้น ปัญหาด้านประสิทธิภาพก็ยังเปิดโอกาสให้ฉันเสนอ C++ addon ได้เป็นครั้งคราว