คำถามของบทความนี้คืออะไร?

สวัสดีผู้อ่านทุกคนด้วยนะครับ ช่วงนี้วงการเขียน Code ของเราน่าจะเข้าสู่ยุคสมัยใหม่แล้ว เอ้ย… ไม่สิ… ต้องบอกว่าเข้าไปแล้วต่างหาก เพราะจากเดิมที่ Developer ต้องนั่งพิมพ์ Code เองทีละบรรทัด ตอนนี้เราสามารถโยน Requirements ให้ Coding Agent แล้วปล่อยให้มันอ่าน Project, แก้ Code, Run Command, เขียน Test และวนแก้งานของตัวเองต่อได้แล้ว โดยที่ระหว่างนี้เราไปนั่งจิบกาแฟ ทำกับข้าวรอได้เลยแบบชิลๆ (ไหมนะ)

บางครั้งมันก็ทำได้ดีจนน่าตกใจ ส่วนบางครั้งมันก็ทำอะไรอ๋องๆ ออกมานอก Scope ที่เรากำหนดไว้ แล้วปล่อยให้เรานั่งงงว่าตกลง Documentation เก่าหรือ Agent เมากาวกันแน่นะ 555

ก่อนอื่นผมขอเคลียร์ Scope ของบทความนี้ให้ตรงกันก่อนนะครับ

บทความนี้ ไม่ได้กำลังจะถามว่า เราควรใช้ Rust หรือ Go เพื่อสร้าง AI Agent หรอกนะ แต่สิ่งที่ผมกำลังพูดถึงคือ

ในยุคที่เราปล่อยให้เหล่าน้องๆ AI หรือ Coding Agents ทั้งหลายเข้ามาเขียน Production Code ให้เรา ไม่ว่า Code นั้นจะเป็น Backend, Web Service, Game, CLI, Desktop Application หรืออะไรก็ตามแต่จะคิดได้ เราควรเลือกภาษาไหนเป็น Target ให้มันเขียนระหว่าง Rust กับ Go?

และคำตอบของผมก็คือ Rust ครับ

เพราะในยุค Agentic AI ต้นทุนของการ Generate Code กำลังลดลงเรื่อยๆ (ก็แค่หวังว่านะ) ขณะที่ต้นทุนของการ Verify Code กำลังกลายเป็นปัญหาใหญ่ขึ้นทุกวัน

เมื่อ AI สามารถผลิต Code ให้เราได้เร็วในระดับความเร็วแสง (เอ่อ… ก็แค่เปรียบเทียบน่ะ) สิ่งที่เราต้องการไม่ใช่แค่ภาษาที่เขียนได้ไว แต่คือภาษาที่มี Verification ตั้งแต่ระดับ Compiler คอยตบหน้าไอ้พวก Code ที่มีปัญหาด้าน Type, Concurrency และ Memory Safety ก่อนที่จะมีสิทธิ์ขึ้นไปอยู่บน Production ซะมากกว่า

ว่าแต่นายน่ะ นายนั่นแหละผู้อ่าน นายก็คิดเหมือนกันไหมล่ะ…

ตรงนี้แหละที่ Rust ได้เปรียบ Go แบบโคตรชัด (มองจากดาวอังคารก็ยังรู้เลยว่าผมอวย R…)

TL;DR สำหรับคนขี้เกียจอ่าน

ถ้าเราจะปล่อยให้ AI เขียน Production Code ให้เป็นสัดส่วนมากขึ้น ผมเลือก Rust เพราะ Rust มี Verification Layer ตั้งแต่ Compile Time ที่หนากว่า Go อย่างเห็นได้ชัด เช่น

  • Ownership และ Borrow Checker
  • Memory Safety โดยไม่ต้องพึ่ง Garbage Collector ซึ่งในบาง Workload อาจเพิ่มต้นทุนด้าน CPU และ Latency
  • Send และ Sync สำหรับคุม Type ที่ถูกส่งหรือ Share ข้าม Thread
  • Pattern Matching สำหรับ State ที่เป็น Enum
  • Result<T, E> สำหรับบังคับให้ Error อยู่ใน Type Flow
  • Trait Bound สำหรับบอก Constraint ของ Generic Code
  • cargo check สำหรับตรวจ Code อย่างรวดเร็ว (มั้ง) ก่อน Build Binary จริง
  • Clippy สำหรับจับ Pattern ที่น่าสงสัยและ Code Smell เพิ่มเติม

แน่นอนว่า Rust ไม่ได้ทำให้เราไม่ต้อง Review Code เลยนะ ถ้าเห็นใครที่บอกว่าใช้ Rust แล้ว Merge Code จาก AI ได้โดยไม่ต้องดูอะไรเลย มันโม้ครับ ทรงนั้นไม่ได้เรียกว่าคนจริงที่ต้องละลาย แต่เรียกว่า “ประมาท” ซะมากกว่านะ

แต่ถึงอย่างนั้น ก็ต้องบอกกันตามตรงว่า Rust ช่วยลด พื้นที่ที่มนุษย์ต้อง Review เอง ลงได้จริง อันนี้ไม่ได้โม้ — อย่างน้อยก็ในข้อผิดพลาดหลายกลุ่มที่ Type System และ Compiler ตรวจได้

เราสามารถปล่อยให้ Compiler ช่วยตรวจเรื่อง Type, Ownership, Lifetime, Concurrency และอื่นๆ ได้จริง ส่วนที่เหลือ เราก็เอาเวลาของมนุษย์ไปตรวจสิ่งที่ Compiler ไม่รู้หรือทำไม่ได้แทน เช่น

  • ตรวจเช็ก Business Logic ว่ามันถูกต้องไหม AI หลอนหรือเปล่า?
  • คุณภาพของ Code เป็นยังไง แบ่ง Domain ชัดเจนหรือเปล่า SOLID ไหม?
  • Authorization ปลอดภัยพอในระดับที่ควรหรือยัง? ถ้า Hacker Bypass ได้ขึ้นมาก็คงจะวุ่นไม่น้อย
  • Requirements ครบตามที่ทีม Business ต้องการไหม?

นี่คือใจความหลักของบทความนี้ครับ

Rust ไม่ได้ทำให้ Human Review ถูกลดค่าจนกลายเป็นศูนย์ แต่มันช่วยกำจัดงาน Review เชิง Technical บางกลุ่มออกไป (ก็อย่างเช่น Type, Ownership และ Memory Safety อะไรทรงนี้) เพื่อให้มนุษย์โฟกัสกับสิ่งที่ต้องใช้เวลากับมันจริงๆ

ส่วน Go ยังมีข้อดีเรื่อง Syntax เรียบง่าย Compile เร็ว Onboarding ง่าย และมี Ecosystem สำหรับ Backend ที่แข็งแรงมาก แต่ความง่ายของ Go ถูกออกแบบมาเพื่อช่วยให้ มนุษย์เขียน Code ได้เร็ว มากกว่าจะช่วยลดโอกาสเจอเหตุการณ์หัวแตกในขา Runtime

พอคนที่เขียน Code หลักของเรากลายเป็น Agent ซึ่งสามารถนั่งแก้ Compiler Error ได้ทั้งวันโดยไม่บ่น หรือเรียกร้องขอคาเฟอีนเพิ่ม ความเข้มงวดที่เคยถูกมองว่าเป็นข้อเสียของ Rust กลับกลายเป็น Feature ที่มีค่ามากขึ้นมาทันทีครับ

ก็อย่างที่บอกอะนะ ในเมื่อยุคนี้ AI ช่วยแก้ปัญหาเรื่องความยากในการเขียน Code ให้เราได้มากขึ้นแล้ว แล้วทำไมเรายังถึงเอาการที่บอกว่า “Go มันเรียนรู้ได้ง่าย เขียนได้ไวกว่า Rust” มาเป็นข้ออ้างอยู่อีก เพราะในเมื่อคนที่ต้องมาปวดหัวกับความยากเหล่านั้นคือ AI ไม่ใช่มนุษย์แต่อย่างใด

ยุค Agentic AI เปลี่ยนสมการของการพัฒนา Software ยังไง?

เมื่อก่อน Flow การพัฒนา Software ทั่วไปจะเป็นประมาณนี้ เวลาส่วนใหญ่อยู่ที่ Human คิดและพิมพ์ Code ดังนั้นภาษาที่เขียนง่ายและ Compile เร็วจึงได้เปรียบมาก (มั้ง)

แต่เมื่อ Coding Agent เข้ามา Flow จะกลายเป็นแบบนี้

เมื่อ Agent เขียนโค้ด ต้นทุนย้ายจากการพิมพ์ไปสู่การตรวจ

งานของมนุษย์ขยับขึ้นไปอยู่ที่การกำหนดโจทย์และตรวจสิ่งที่ compiler ไม่รู้ ส่วน Agent รับ feedback จาก toolchain แล้ววนแก้ได้ต่อเนื่อง

Flow เดิมมนุษย์อยู่ในทุกลูป
  1. 01คิดและออกแบบ
  2. 02เขียนโค้ด
  3. 03รับ compiler error
  4. 04แก้โค้ด
  5. 05review และทดสอบ
  6. 06ส่งขึ้นระบบ

เวลาเขียนและแก้ syntax เป็นสัดส่วนสำคัญ ภาษาที่เรียนง่ายและคอมไพล์เร็วจึงช่วยมนุษย์โดยตรง

Flow แบบ AgenticToolchain กลายเป็น feedback loop
  1. 01มนุษย์วางแผนและกำหนดขอบเขต
  2. 02Agent เขียนโค้ด
  3. 03toolchain ส่ง diagnostics
  4. 04Agent แก้แล้วตรวจซ้ำ
  5. 05มนุษย์ตรวจ logic และความเสี่ยง
  6. 06ส่งขึ้นระบบ

วนซ้ำจน checks ผ่าน

คุณค่าของภาษาจึงไม่ได้อยู่แค่เขียนง่าย แต่อยู่ที่ feedback ซึ่งกันข้อผิดพลาดได้ก่อน runtime

เปรียบเทียบวงจรพัฒนาซอฟต์แวร์แบบเดิมกับวงจรที่ Coding Agent รับ feedback จาก toolchain โดยตรง

ก็นะ… ตัว Agent มันไม่เคยรำคาญ Compiler มันไม่เคยเบื่อ Borrow Checker ไม่เคยหัวร้อนใส่ Lifetime และไม่ต้องกลับบ้านไปนอนเพราะ Trait Bound ยาวเกินหนึ่งบรรทัดเลยน่ะสิ

Compiler บอก Error อะไรมา Agent ก็เอากลับไปแก้และ Ship ใหม่ได้เสมอๆ ดังนั้นคำถามสำคัญจึงไม่ใช่แค่ว่า

ภาษาไหนทำให้ Agent เขียน Code ได้ง่ายกว่า?

แต่ควรถามว่า

ภาษาไหนให้ Feedback ที่มีคุณภาพมากพอจะบังคับ Agent ให้แก้ Code เข้าใกล้สิ่งที่ปลอดภัย ก่อนส่งงานกลับมาให้มนุษย์?

ในมุมนี้ Rust มีความได้เปรียบอย่างมหาศาลครับ

cargo check จะตรวจ Package และ Dependencies โดย Compiler เกือบทั้งหมด แต่ข้าม Final Code Generation ทำให้มันเร็วกว่า cargo build และเหมาะมากกับ Feedback Loop ที่ต้อง Run ซ้ำเรื่อยๆ

มันไม่ใช่ Formal Verification และ Diagnostics บางอย่างจะเจอเฉพาะตอน Code Generation หรือ Runtime แต่สิ่งที่มันตรวจได้ก็เยอะพอจะทำหน้าที่เป็น Reviewer ด่านแรกแบบไม่มีวันเหนื่อย ไม่มีวันขี้เกียจ และไม่กด Approve แบบซี้ซั้วอย่างแน่นอน

Go Compile ผ่าน ไม่ได้แปลว่า Code พร้อม Run

ตรงนี้ผมไม่ได้กำลังบอกว่า Go ไม่มี Type System หรือ Compiler ไม่ตรวจอะไรเลยนะครับ Go Compiler จับ Syntax Error, Type Mismatch, Missing Import และปัญหาอีกหลายอย่างได้ตามปกติ

แต่ข้อผิดพลาดสำคัญหลายๆ อย่าง โดยเฉพาะใน Concurrent Program สามารถ Compile ผ่านแล้วค่อยไปหัวแตก Runtime ได้โดยที่เราไม่รู้ตัว

ลองดูตัวอย่างง่ายๆ แบบนี้ครับ

package main

import "sync"

func main() {
	counts := map[string]int{}
	tools := []string{"search", "database", "email"}

	var wg sync.WaitGroup

	for _, tool := range tools {
		wg.Add(1)

		go func() {
			defer wg.Done()
			counts[tool]++
		}()
	}

	wg.Wait()
}

Code นี้ Compile ผ่านครับ

แต่ Goroutine หลายตัวกำลังเขียนลง map เดียวกันโดยไม่มี Synchronization จึงมี Data Race และอาจเกิด Runtime Failure ได้

Go มี Race Detector ที่ดี เราสามารถใช้ go test -race หรือ go run -race ช่วยตรวจได้ แต่ เอกสารของ Go เอง ระบุชัดว่า Race Detector เจอเฉพาะ Race ที่เกิดขึ้นจริงใน Runtime เท่านั้น

แปลว่าถ้า Test ไม่ได้วิ่งผ่าน Path นั้น หรือ Timing ไม่ทำให้ Race เกิดขึ้นตอนตรวจ เราก็มีโอกาสหาไม่เจอครับ

นอกจากนี้ Go Code Review Comments ยังเตือนว่า Goroutine สามารถ Leak จากการ Block บน Channel ได้ และ Garbage Collector จะไม่ Terminate Goroutine นั้นให้เราเอง

ทีนี้ลองมาดูแนวคิดเดียวกันใน Safe Rust

use std::thread;

fn main() {
    let mut completed = 0;

    thread::scope(|scope| {
        scope.spawn(|| completed += 1);
        scope.spawn(|| completed += 1);
    });
}

Rust จะไม่ยอมให้ Code นี้ Compile เพราะเรากำลังพยายามถือ Mutable Borrow ของ completed มากกว่าหนึ่งชุดพร้อมกัน

Compiler ไม่ได้แก้ Architecture ให้เรานะครับ แต่จะบังคับให้เราเลือกวิธี Share State ออกมาอย่างชัดเจน เช่น

  • ย้าย Ownership ของข้อมูลเข้าไปใน Worker
  • ส่ง Result กลับมารวมทีหลัง
  • ใช้ Message Passing
  • ใช้ Atomic Type
  • ใช้ Arc<Mutex<T>> เมื่อ Shared Mutable State จำเป็นจริงๆ

นี่คือความต่างที่สำคัญมาก

Go มี Tool ไว้ช่วยตามหา Data Race เมื่อ Path นั้นถูก Run ส่วน Safe Rust ไม่ยอมให้ Shared Mutable State รูปแบบนี้มีสิทธิ์ Compile ผ่านตั้งแต่แรก ผ่าน Ownership และ Send/Sync ตามแนวคิด Fearless Concurrency

สำหรับ Code ที่มนุษย์เขียนเอง เราอาจบอกว่า “ไม่เป็นไร เดี๋ยวเขียน Test ให้ครบและ Review ดีๆ” แต่เมื่อ Agent สามารถ Generate Concurrent Code ออกมาครั้งละหลายร้อยบรรทัด Guarantee จาก Compiler จะช่วยลดภาระตรวจของมนุษย์ได้มากกว่าคำว่า “Code ดูง่าย” อย่างชัดเจนนั่นเอง (เห็นด้วยไหมล่ะ ผู้อ่านทั้งหลาย)

การ์ตูนสองช่อง เปรียบเทียบ Go Compiler ที่บอกว่าไม่มีอะไรผิดพลาดแล้วส่งกล่องปิดให้ผ่าน กับ Rust Compiler ที่เปิดกล่องจนเจอไฟแล้วเตือนให้แก้ก่อน
ภาพประกอบโดย เรืองยศ หนานเจียง: ความต่างระหว่างการปล่อยให้ปัญหารออยู่ในกล่องจนถึง Runtime กับการเปิดตรวจให้เจอก่อน

Invalid State: Bug ที่ Type System ช่วยฆ่าทิ้งก่อนเกิดได้

นอกจาก Memory กับ Concurrency แล้ว อีกเรื่องที่ Rust เหมาะกับ AI-generated Code มากก็คือการ Model State ครับ

สมมุติเรากำลังเขียนระบบ Order สำหรับ Web Application ด้วย Go

type Order struct {
	Status        string
	PaymentID     *string
	FailureReason *string
	ShippedAt     *time.Time
}

Struct นี้ยืดหยุ่นมาก แต่ก็สามารถสร้าง Combination ประหลาดๆ ได้เช่นกัน

  • Status == "paid" แต่ PaymentID == nil
  • Status == "failed" แต่ไม่มี FailureReason
  • Order ถูก shipped แล้วแต่ยังมีสถานะ pending_payment
  • มีทั้ง FailureReason และ ShippedAt พร้อมกัน

เราสามารถแก้ด้วย Constructor, Validation และการไม่ Export Field มั่วๆ ได้อยู่แล้วนะ เพราะ Go เองก็ไม่ได้บังคับให้เราออกแบบ Model ห่วยๆ แบบนี้ แต่คำถามคือ ถ้า Agent เป็นคนเพิ่ม Field และ Flow ใหม่ทรงนี้เข้าไปเรื่อยๆ ล่ะ? มนุษย์แบบเราๆ ต้องคอยตรวจ Convention เหล่านี้ตลอดๆ ไม่เบื่อแย่เลยเหรอ? (เอาจริงๆ ก็เพิ่ม Instruction ให้ Agent ได้แหละ แต่ก็เปลือง Tokens อยู่ดี)

ใน Rust เราสามารถบังคับ Shape ของ State ผ่าน Enum ได้ตรงกว่า

enum OrderState {
    PendingPayment,
    Paid {
        payment_id: PaymentId,
    },
    Failed {
        reason: PaymentFailure,
    },
    Shipped {
        payment_id: PaymentId,
        shipped_at: DateTime<Utc>,
    },
}

กรณีนี้ Paid ไม่มีทางเกิดโดยไม่มี PaymentId และ Failed ต้องมี PaymentFailure เพราะ Type บังคับเอาไว้

ถ้า Agent เพิ่ม State ใหม่เข้ามา แล้ว Function อื่นมี match ซึ่งยัง Handle ไม่ครบ Compiler ก็จะฟ้อง Non-exhaustive Pattern ให้เราไปแก้ทุกจุดที่เกี่ยวข้อง

แนวคิดเดียวกันเอาไปใช้กับ Game ได้เหมือนกันครับ เช่น

enum PlayerState {
    Alive { hp: NonZeroU16 },
    Downed { revive_deadline: Instant },
    Dead,
}

เราไม่ต้องมี is_dead, is_downed, hp, revive_time กระจายเป็น Flags หลายตัว แล้วคอยภาวนาว่ามันจะไม่ขัดกันเอง ยิ่ง Agent เขียน Feature เพิ่มเร็วเท่าไร การทำให้ Invalid State เป็นสิ่งที่สร้างไม่ได้ตั้งแต่แรกยิ่งมีค่ามากขึ้นเท่านั้นครับ

การ Refactor Code ที่ AI เขียน: Compiler ต้องเป็นผู้ช่วยชี้ทางสว่าง ไม่ใช่แค่ยืนดูเฉยๆ แล้วหัวเราะ

อีกงานหนึ่งที่ Coding Agent ทำบ่อยมากคือ Refactor ครับ สมมุติเราเปลี่ยน UserId จาก String ธรรมดาให้กลายเป็น Domain Type หรือเปลี่ยน Function จาก Return ค่าเปล่าให้ Return Result

ใน Rust เราสามารถปล่อยให้ Agent แก้ Type ต้นทางก่อน จากนั้น Run cargo check แล้วใช้ Compiler Errors เป็นตัวไล่แก้ Call Site ที่เหลือได้

struct UserId(uuid::Uuid);

fn delete_user(id: UserId) -> Result<DeletedUser, DeleteUserError> {
    // ...
}

จุดไหนยังส่ง String เข้ามา จุดไหนยังไม่ Handle Result หรือจุดไหนพยายามใช้ Value หลัง Ownership ถูก Move ไปแล้ว Compiler จะชี้ให้เห็นได้ทันที

ส่วน Go Compiler ก็ช่วยจับ Signature Mismatch ได้เหมือนกันครับ ตรงนี้ไม่ใช่ความสามารถเฉพาะ Rust แต่ Rust สามารถใส่ Constraint ลงใน Type ได้ลึกกว่า และ Pattern Matching แบบ Exhaustive ทำให้การแก้ State Model กระจายไปยัง Consumer ได้ชัดมากขึ้น

สำหรับ Agent นี่คือ Feedback ที่มีโครงสร้าง มันไม่ได้ต้องเดาเองจาก Log ตอน Runtime ว่า Flow ไหนลืม Handle Case ใหม่… ยิ่ง Project ใหญ่ Compiler Error ยิ่งทำหน้าที่เหมือน Dependency Graph คอยบอก Agent ว่าการเปลี่ยน Contract หนึ่งจุดกระทบตรงไหนบ้าง

แล้วถ้าเป็น Web Backend ล่ะ? Rust ยังได้เปรียบอยู่ไหม?

ได้เปรียบดิวะ!!! ก็ในเมื่องาน Web Backend ไม่ได้มีแต่ CRUD โง่ๆ แต่มี Business State, Concurrent Work, Queue, Cache, Permission และสากกระเบือยันเรือรบ…

เราอาจใช้ Axum สำหรับ HTTP, Tokio สำหรับ Async Runtime, SQLx สำหรับ Database และ Serde สำหรับ Serialization แล้วบังคับให้ Request, Domain Model, Error และ Response มี Type Boundary ชัดเจน

ข้อดีไม่ใช่แค่ Performance แต่คือ Agent สามารถใช้ Compiler ตรวจ Integration ระหว่าง Layer ได้ตลอดทาง อย่างเช่น

  • Handler ส่ง Input ผิด Type เข้า Usecase ไม่ได้
  • Usecase ลืม Handle Repository Error ไม่ได้ ถ้า API บังคับ Return Result
  • Shared State ที่ไม่ Send + Sync จะถูกปฏิเสธเมื่อเอาเข้า Async Server ที่กำหนด Bound นี้
  • Enum Error ที่เพิ่ม Variant ใหม่จะทำให้ Match ที่ไม่ครบถูก Compiler ฟ้อง
  • Resource จะถูก Cleanup ตาม Ownership และ RAII เมื่อออกจาก Scope

เผื่อใครไม่รู้จัก RAII นะครับ มันคือแนวทางที่ผูกอายุของ Resource ไว้กับ Object หรือ Variable เมื่อตัวแปรหลุด Scope ตัวทำลายจะทำงานและปล่อย Resource ตามกติกาของ Type นั้น

แต่ด้วยความที่ไม่อวย Rust จนมากเกินไป ผมก็ต้องบอกอย่างตรงไปตรงมาว่า Go นั้นยังถือว่าทำ Backend ได้ดีในระดับหนึ่งนะ และสำหรับทีมที่มี Go Service จำนวนมากอยู่แล้ว ต้องการจะย้ายทั้งหมดมาเป็น Rust เพียงเพราะอ่านบทความนี้จบเฉยๆ อย่าหาทำครับ ไม่น่าจะคุ้มค่ากับต้นทุนและความเสี่ยงที่จะตามมาแน่ๆ รอเอาไปใช้กับ Project ใหม่ๆ น่าจะดีกว่า 🙏🙏🙏 ผมมองว่าถ้า Case นี้ Rust จะช่วยเป็น Safety Harness ในระยะยาวได้ครับ 🙏🙏🙏

Go ยังเหมาะกับอะไรในยุคนี้?

ถึงตรงนี้หลายคนน่าจะคิดว่า ผมกำลังจะบอกให้ผู้อ่านว่า Go มันกาก ย้ายมา Rust มันให้หมด ก็ยังอย่าเพิ่งนะ ใจเย็นก่อน Go ยังเหมาะมากเมื่อ

  • บริษัทมี Go Codebase และ Go Team ที่แข็งแรงอยู่แล้ว
  • Service เป็น Glue Code เชื่อม API แบบตรงไปตรงมา
  • ต้องการ Build Time ที่เร็วมากใน Monorepo ขนาดใหญ่
  • SDK หรือ Infrastructure Library ที่ต้องใช้รองรับ Go ดีกว่าอย่างชัดเจน
  • Failure Domain ของ Component แคบและไม่มี Side Effect รุนแรง
  • Team Convention, Test, Static Analysis และ Observability ที่แข็งแรงพอ

ถ้าเรามี Go Service ที่ผ่าน Production มาหลายปี มี Test ครบ มีทีมดูแล และไม่ได้มีปัญหาอะไร การ Rewrite เป็น Rust เพื่อให้ Architecture ดูหล่อขึ้นคือการเผาเงินเล่นเปล่าๆ แต่ถ้าถามว่า Greenfield Production Code ซึ่ง AI จะ Generate และ Refactor เป็นสัดส่วนมากควรเริ่มจากอะไร สำหรับโจทย์แบบนี้ผมเทให้ Rust เป็นตัวเลือกแรกอย่างชัดเจนครับ

เพราะข้อได้เปรียบด้าน “เขียนง่าย” ของ Go มันมีน้ำหนักลดลงแล้วครับ เพราะเราให้ Agent เขียนให้ ไม่ต้องมาปวดหัวเขียนเอง 100% แบบเมื่อก่อน แถมยังได้เปรียบด้าน “การตรวจเข้มจาก Compiler” ของ Rust อีกต่างหาก เทียบกับ Trade-off เรื่องความยากในการเขียน ผมมองว่าคุ้มกว่าเยอะครับ

แต่ๆๆๆ อย่าลืมว่า การใช้ Rust ก็ไม่ได้หมายความว่าเราจะไม่ต้องตรวจ Code เลยนะ

อันนี้ต้องย้ำ เพราะเดี๋ยวจะมีคนนำบทความนี้ไปใช้เป็นข้ออ้างในการ Merge Code แบบซี้ซั้วไม่ตรวจอะไรเลย เนื่องจาก Rust Compiler ไม่รู้ว่า

  • Agent เข้าใจ Requirement ของเราผิดตั้งแต่แรกหรือเปล่า
  • UI Flow นี้ทำให้ User กดผิดง่ายแค่ไหน
  • Discount 20% ต้องคำนวณก่อนหรือหลัง Tax
  • User คนนี้มีสิทธิ์ลบ Project หรือไม่
  • …

Rust ยังมี Deadlock ได้ มี Logic Race ได้ มี Memory Leak ผ่าน Reference Cycle บางชนิดได้ Panic ได้ และถ้าใช้ unsafe ผิดก็สร้าง Undefined Behavior ได้เช่นกัน

ดังนั้นคำว่า “ไม่ต้องตรวจเยอะ” ในที่นี้หมายถึง

ไม่ต้องเสียเวลามนุษย์ไปตรวจสิ่งที่ Compiler สามารถพิสูจน์ให้เราได้อยู่แล้วมากเท่าเดิม

Human Review ควรย้ายขึ้นไปอยู่ระดับ Domain, Security, Data Migration, Performance และ User Impact แทนที่จะนั่งไล่ทุกบรรทัดว่า Value นี้ถูก Move ไปหรือยัง หรือ Match นี้ Handle State ครบหรือเปล่า แบบนั้นเสียเวลาแย่ เอาเวลาไปนั่งจิบกาแฟเถอะครับถ้าจะต้องตรวจขนาดนั้น

แบบนี้ต่างหากคือการใช้ AI เขียน Code อย่างมีสติ…

Pipeline ที่ผมอยากให้ Coding Agent มันทำให้เรียบร้อย ก่อนส่งงานถึงมือเรา

ถ้าจะใช้ Rust ให้ได้ประโยชน์ในยุค Agentic AI เราต้องไม่ปล่อยให้ Agent อวยเราไปเรื่อยๆ แบบ “You’re absolutely right!” ครับ ให้มันผ่าน Toolchain อย่างน้อยประมาณนี้ก่อน

Pipeline ที่ Coding Agent ควรผ่านก่อนส่งงานให้มนุษย์

Compiler เป็นด่านสำคัญ แต่ production readiness ต้องประกอบด้วยหลายด่านและจบด้วย human review

  1. 01
    รูปแบบโค้ดcargo fmt --all -- --check

    ทำให้ diff อ่านง่ายและลด noise ก่อน review

  2. 02
    Compilercargo check --workspace --all-targets

    ตรวจ type, ownership และ target ที่ประกาศไว้โดยไม่สร้าง binary ขั้นสุดท้าย

  3. 03
    Lintcargo clippy --workspace --all-targets -- -D warnings

    ยกระดับ lint ที่เลือกใช้ให้เป็นข้อผิดพลาดใน CI

  4. 04
    Testcargo test --workspace

    รัน unit และ integration tests ที่อยู่ใน workspace

  5. 05
    Project checks

    ทดสอบ contract, migration, feature combinations และ external services ตามระบบจริง

  6. 06
    Human review

    ตรวจ requirement, business logic, security, data migration, performance และผลกระทบต่อผู้ใช้

ลำดับขั้นต่ำเป็นเพียงจุดเริ่มต้น แต่ละโครงการต้องเพิ่ม checks ตาม failure modes และสภาพแวดล้อมของตัวเอง

ถ้า Project มี Feature Flags ให้สร้าง CI Matrix ตาม Combination ที่รองรับจริง อย่าใส่ --all-features แบบไม่ดูอะไร เพราะบาง Project อาจมี Features ที่ตั้งใจให้ Mutually Exclusive กัน

ถ้ามี unsafe ให้แยก Review เข้มขึ้น และใช้ Tool เพิ่มตามประเภทงาน ถ้ามี Database Migration ให้ Test ทั้ง Up และ Down ถ้ามี Web ให้ Test Contract กับ Database และ External Service …บลาๆ

Compiler เป็นด่านสำคัญ แต่ Production Readiness ต้องเป็น Pipeline ไม่ใช่ Command เดียว… ข้อได้เปรียบของ Rust คือทุกครั้งที่ Agent แก้ Code เราสามารถส่ง Feedback ที่ละเอียดและเป็น Machine-readable กลับไปให้มันวนแก้ต่อได้ โดยไม่ต้องเรียกมนุษย์มาช่วยทุก Error

บทสรุป

เมื่อ AI กลายเป็นคนลงมือเขียน Production Code ให้เราเป็นจำนวนมาก เราควรเลือกภาษาแบบไหนเพื่อคุมคุณภาพของสิ่งที่มันผลิตออกมา และคำตอบของผมคือ Rust

ในอดีต Rust ถูกวิจารณ์ว่าเขียนยาก Compiler จุกจิก และใช้เวลาต่อสู้กับ Ownership มากกว่า Go แต่เมื่อ Agent เป็นคนรับหน้าที่เขียนและแก้ Compiler Error ความจุกจิกเหล่านั้นไม่ได้ตกอยู่บนบ่ามนุษย์ทั้งหมดอีกต่อไป สิ่งที่เหลืออยู่กับเราคือ Guarantee จาก Type System, Ownership, Borrow Checker, Send, Sync, Exhaustive Matching และ Result

พูดง่ายๆ ก็คือ AI ช่วยลดต้นทุนการเขียน Rust แต่ไม่ได้ลดประโยชน์ที่ Rust Compiler มอบให้เรา

กลับกัน ข้อได้เปรียบสำคัญของ Go ที่ว่าเขียนง่ายและใช้ Developer น้อยลง กลับมีน้ำหนักลดลงเมื่อ Agent สามารถ Generate Boilerplate และแก้ Code ได้แทบตลอดเวลา

ดังนั้นถ้าผมต้องเริ่ม Greenfield Project วันนี้ ไม่ว่าจะเป็น Web Backend, Game, CLI, Desktop Tool หรือ Systems Service และตั้งใจให้ Coding Agent ช่วยเขียน Code เป็นสัดส่วนใหญ่ ผมจะเลือก Rust เป็น Default ก่อน

ไม่ใช่เพื่อหยุด Review ทั้งหมด แต่เพื่อลด Review เชิง Technical ในส่วนที่ Compiler ตรวจได้ แล้วเอาเวลาไปจิบกาแฟแทน แทนที่จะต้องไล่เช็กเองทีละบรรทัดว่าตรงไหนไม่ Memory Safe และยังต้องลุ้นว่าจะหัวแตกตอน Runtime หรือไม่ ผ่ามมม!!!

หวังว่าบทความนี้จะมีประโยชน์กับทุกคนครับ ขอบคุณที่อ่านกันจนจบครับ 💀

References