Back to blogWeb Development

สิ่งที่ได้เรียนรู้จากการเริ่มใช้ Ruby on Rails ในยุค AI Agent Era ของ Junior

Chanthawat
Chanthawat
6 min read
สิ่งที่ได้เรียนรู้จากการเริ่มใช้ Ruby on Rails ในยุค AI Agent Era ของ Junior

สิ่งที่ได้เรียนรู้จากการเริ่มใช้ Ruby on Rails ในยุค AI Agent Era ของ Junior#

บทนำ: ทำไมเด็กจบใหม่ปี 2026 ถึงไปจับภาษาที่เกิดปี 1995#

ตอนที่ตัดสินใจเขียน UTCC AI Academy ด้วย Ruby on Rails คำถามแรกที่โดนถามคือ "ทำไมไม่ใช้ Next.js ที่ถนัดอยู่แล้ว" ซึ่งเป็นคำถามที่ตอบยากพอสมควรในตอนนั้น เพราะเหตุผลจริง ๆ คือ "อยากลอง" มากกว่าจะมีเหตุผลทางวิศวกรรมที่หนักแน่น

ผ่านมาหลายเดือน ตอนนี้ตอบได้ชัดกว่าเดิมมาก และคำตอบมันเกี่ยวกับ AI agent มากกว่าที่คิดไว้ตอนแรก

บทความนี้ไม่ใช่ tutorial สอน Rails และไม่ใช่บทความชวนทุกคนย้ายมาใช้ Rails แต่เป็นบันทึกของสิ่งที่ได้เรียนรู้จริง ๆ จากการเขียนโปรเจกต์หนึ่งตัวให้เสร็จ ในปีที่การเขียนโค้ดส่วนใหญ่เกิดขึ้นผ่านการคุยกับ AI agent มากกว่าการพิมพ์ทีละบรรทัด

โปรเจกต์ที่พูดถึงคือแพลตฟอร์มเรียน AI สำหรับนักศึกษามหาวิทยาลัยหอการค้าไทย เปิดให้ทุกคณะเข้าใช้ มี course catalog บทเรียนที่ตรวจแบบฝึกหัดและโจทย์เขียนโค้ดให้ระหว่างเรียน และ progress ที่ตามผู้เรียนไปทุกหน้าจอ อินเทอร์เฟซเป็นภาษาไทยเป็นหลักและสลับเป็นอังกฤษได้ สแตกคือ Rails 8.1, Ruby 3.4.10, SQLite และ Hotwire โดยไม่มี Node เลย


1. Convention over Configuration คือ context ที่ agent ไม่ต้องเดา#

นี่คือบทเรียนที่สำคัญที่สุด และเป็นเรื่องที่ไม่เคยคิดมาก่อนเลยตอนเริ่ม

เวลาสั่ง AI agent ว่า "เพิ่มหน้า leaderboard" ในโปรเจกต์ Next.js ที่เขียนเอง agent ต้องหาคำตอบให้ได้ก่อนหลายข้อ ไฟล์ควรอยู่ที่ไหน เรียก data ยังไง โปรเจกต์นี้ใช้ server component หรือ client component ใช้ fetch ตรง ๆ หรือมี service layer ตั้งชื่อไฟล์แบบ kebab-case หรือ PascalCase คำตอบเหล่านี้ไม่มีอยู่ในตัวเฟรมเวิร์ก มันอยู่ในหัวเรา หรืออยู่กระจัดกระจายในโค้ดที่เขียนไปแล้ว

พอเป็น Rails คำถามพวกนี้หายไปเกือบหมด เพราะเฟรมเวิร์กตอบไว้ให้แล้ว controller อยู่ที่ app/controllers model อยู่ที่ app/models view ของ LeaderboardsController#show อยู่ที่ app/views/leaderboards/show.html.erb ไม่มีทางเลือกอื่นให้ต้องตัดสินใจ

ผลที่เกิดขึ้นจริงคือ agent เดาถูกตั้งแต่ครั้งแรกบ่อยขึ้นมาก เพราะโครงสร้างที่มันต้องเดานั้น เป็นโครงสร้างเดียวกับที่มันเคยเห็นในโค้ด Rails หลายล้านบรรทัดตอนเทรน convention ที่เคยถูกมองว่าเป็นข้อจำกัด กลายเป็น context ฟรีที่ไม่ต้องเขียนอธิบายใน prompt

สิ่งที่ตามมาคือ controller ในโปรเจกต์นี้เล็กมากโดยธรรมชาติ

class ProgressController < ApplicationController
  def show
    @progress = progress
  end
end

ProgressController ทั้งไฟล์มีขนาด 238 ไบต์ ส่วน LeaderboardsController มี 154 ไบต์ ไม่ใช่เพราะพยายามเขียนให้สั้น แต่เพราะงานที่เหลือมันไปอยู่ในที่ที่ควรอยู่ตามธรรมชาติของเฟรมเวิร์ก

บทเรียน: ในยุคที่ AI เขียนโค้ดให้ ความสามารถของเฟรมเวิร์กในการ "ลดจำนวนการตัดสินใจที่ต้องอธิบาย" มีค่ามากกว่าความยืดหยุ่น


2. ยิ่ง dependency น้อย agent ยิ่งพลาดน้อย#

โปรเจกต์นี้ไม่มี package.json ไม่มี node_modules ไม่มี bundler ฝั่ง JavaScript

  • JS ใช้ importmap-rails + Turbo + Stimulus โหลดไฟล์ตรง ๆ ผ่าน import map
  • CSS ใช้ Tailwind v4 ผ่าน tailwindcss-rails ซึ่งเป็น standalone binary ไม่มี npm ไม่มี PostCSS
  • Assets ใช้ Propshaft ไม่มี Sprockets manifest

และฝั่ง infrastructure ก็เป็นเรื่องเดียวกัน ไม่มี Redis ไม่มี Memcached ไม่มี job runner แยกต่างหาก ทุกอย่างเป็นไฟล์ SQLite

งาน เครื่องมือ เบื้องหลัง
Background jobs solid_queue รันใน process เดียวกับ Puma
Cache solid_cache SQLite
WebSockets solid_cable SQLite
Database sqlite3 ไฟล์เดียวใน dev/test

เรื่องนี้สำคัญกับการทำงานร่วมกับ agent มากกว่าที่คิด เพราะ ทุก dependency คือพื้นที่ที่ agent จะเดาเวอร์ชันผิดได้ อาการที่เจอบ่อยคือ agent เขียนโค้ดถูกตามเอกสารของไลบรารีเวอร์ชันหนึ่ง แต่โปรเจกต์ติดตั้งอีกเวอร์ชันหนึ่ง แล้วเสียเวลาไล่แก้กันไปมา

ยิ่ง surface area เล็ก โอกาสเจอปัญหาแบบนี้ยิ่งน้อยลงตามไปด้วย และเวลาที่ประหยัดได้ตรงนี้ มากกว่าเวลาที่เสียไปจากการที่ importmap ไม่มี tree-shaking พอสมควร อย่างน้อยก็ในสเกลของโปรเจกต์ขนาดนี้

ข้อควรระวัง: ถ้าโปรเจกต์ต้องใช้ไลบรารี JS หนัก ๆ ที่มาเป็น npm package เท่านั้น สมการนี้จะเปลี่ยนทันที การไม่มี Node ไม่ได้ดีเสมอไป มันดีเพราะโปรเจกต์นี้ไม่ต้องการมัน


3. bin/ci คือ definition of done ที่ agent ตรวจตัวเองได้#

ก่อนหน้านี้เวลาให้ agent เขียนโค้ด วิธีตรวจคือเปิดดูเอง แล้วรัน dev server กดดูทีละหน้า ซึ่งช้าและพลาดง่าย

สิ่งที่เปลี่ยนวิธีทำงานไปเลยคือการมีคำสั่งเดียวที่บอกได้ว่างานเสร็จหรือยัง

bin/ci   # lint + security scan + tests ทั้งหมด

ข้างในรวม rubocop-rails-omakase, Brakeman, bundler-audit และ importmap audit พร้อมกับ Minitest ทั้งชุด (โปรเจกต์นี้ใช้ Minitest ไม่ใช่ RSpec และรันแบบ parallel ส่วน system test ขับผ่าน Capybara + Selenium)

พอมีคำสั่งเดียวแบบนี้ วิธีสั่งงานเปลี่ยนจาก "เขียนฟีเจอร์นี้ให้หน่อย" เป็น "เขียนฟีเจอร์นี้ แล้วรัน bin/ci ให้เขียว" ซึ่งต่างกันมาก เพราะ agent วน loop แก้ตัวเองได้โดยไม่ต้องรอเราตรวจ

จุดที่ต้องพูดตรง ๆ คือ สิ่งนี้ไม่ได้มาจาก Rails โดยตรง Rails ให้ bin/rails test และ generator ของ Rails 8 ก็แถม Brakeman กับ RuboCop มาให้ แต่การรวมทุกอย่างเป็นคำสั่งเดียวและถือว่านั่นคือเส้นแบ่ง "เสร็จ/ไม่เสร็จ" เป็นสิ่งที่ต้องตั้งใจทำเอง (ในโปรเจกต์นี้อยู่ที่ config/ci.rb) ทำแบบเดียวกันใน Node ก็ได้ แต่ประเด็นคือถ้าจะทำงานกับ agent อย่างจริงจัง ควรทำตั้งแต่วันแรก ไม่ใช่ทำตอนโปรเจกต์โตแล้ว


4. Scrum สองสัปดาห์ ยังใช้ได้อยู่ไหมเมื่อ agent เขียนโค้ดเร็วกว่าเดิมสิบเท่า#

คำถามที่เจอบ่อยคือ ในเมื่อ AI เขียนโค้ดได้เร็วขนาดนี้ กระบวนการแบบ Scrum ที่มี sprint สองสัปดาห์ยังจำเป็นอยู่หรือเปล่า

คำตอบจากการทำจริงคือ จำเป็นมากขึ้น ไม่ใช่น้อยลง แต่หน้าที่ของมันเปลี่ยนไป จากเดิมที่ Scrum ถูกใช้เพื่อจัดการ "ความช้า" ของการพัฒนา ตอนนี้มันถูกใช้เพื่อจัดการ "ความเร็ว" แทน

โปรเจกต์นี้ใช้ Scrum แบบ sprint สองสัปดาห์ โดยมีหลักที่ไม่ยืดหยุ่นอยู่ข้อเดียวคือ ขอบเขตงานต่อรองได้ แต่เส้นตายต่อรองไม่ได้ ถ้างานใหญ่กว่าที่คิด ให้ลดขอบเขต ไม่ใช่ขยายเวลา และ sprint ใหม่เริ่มทันทีที่ sprint เก่าจบ ไม่มีช่วง "ตามงานให้ทัน" คั่นกลาง

4.1 Sprint goal คือ prompt ที่ดีที่สุดเท่าที่จะเขียนได้#

Scrum บอกว่า sprint goal ต้องเป็นประโยคเดียวที่บอกว่า sprint นี้มีไว้ทำอะไร ตัวอย่างจากเอกสารของโปรเจกต์คือ

"Course และ Topic กลายเป็นตารางจริง" คือเป้าหมาย "แก้นู่นแก้นี่" ไม่ใช่เป้าหมาย

ตอนแรกอ่านแล้วรู้สึกว่านี่เป็นเรื่องการจัดการโปรเจกต์ทั่วไป แต่พอทำงานกับ agent จริง ๆ ถึงเห็นว่ามันคือเรื่องเดียวกันกับการเขียน prompt

งานที่เขียนเป็นประโยคเดียวไม่ได้ แปลว่าเรายังไม่เข้าใจงานนั้นดีพอ และถ้าเราไม่เข้าใจ agent ก็ไม่มีทางเข้าใจ ผลลัพธ์ของ prompt คลุมเครือไม่ใช่โค้ดที่พัง แต่เป็นโค้ดที่ ดูสมเหตุสมผลแต่แก้ปัญหาผิดข้อ ซึ่งเสียเวลากว่ามาก เพราะกว่าจะรู้ตัวก็ merge ไปแล้ว

หลักที่ใช้คือ sprint goal ที่ดีต้องเป็น vertical slice — หยิบหน้าจอที่ยังเป็น placeholder มาหนึ่งอัน แล้วทำให้มันเป็นของจริงทั้งเส้น ตั้งแต่ตารางในฐานข้อมูลจนถึงสิ่งที่ผู้ใช้เห็น ไม่ใช่ทำ layer ใดชั้นเดียวให้ครบทุกหน้าจอ เพราะ vertical slice เท่านั้นที่เอาไปเดโมได้จริงตอนจบ sprint

4.2 Definition of Done ที่เครื่องตรวจได้ สำคัญกว่าที่คนตรวจ#

นี่คือจุดที่กระบวนการกับเครื่องมือมาบรรจบกัน Definition of Done ของโปรเจกต์นี้เขียนไว้ชัดเจนว่างานจะถือว่าเสร็จเมื่อ ส่งขึ้นได้จริง ไม่ใช่เมื่อเขียนโค้ดจบ ซึ่งประกอบด้วย

เกณฑ์ ใครตรวจได้
bin/ci ผ่านบนเครื่องตัวเอง (RuboCop, bundler-audit, importmap audit, Brakeman, เทสต์, ปลูก seed ใหม่) agent ตรวจเองได้
พฤติกรรมใหม่ต้องมีเทสต์ และการแก้ข้อความต้องไม่ทำ locale assertion พัง agent ตรวจเองได้
th.yml และ en.yml แก้พร้อมกัน array ที่อ้างด้วย index ต้องยาวเท่ากันและเรียงเหมือนกัน ต้องเขียนกฎไว้ให้
ไม่ทำลาย invariant ที่ระบุไว้ใน CLAUDE.md ต้องเขียนกฎไว้ให้
เดโมได้ด้วยการล็อกอินแล้วเดินดูจริง คนเท่านั้น

สังเกตว่าสองข้อแรกเป็นสิ่งที่ agent วนแก้เองได้จนกว่าจะเขียว สองข้อกลางเป็นสิ่งที่ต้องมีคนเขียนเป็นลายลักษณ์อักษรไว้ก่อน (ซึ่งคือหัวข้อถัดไปของบทความนี้) และข้อสุดท้ายเป็นสิ่งที่ยังไม่มีอะไรแทนคนได้

จุดที่ต้องพูดตรง ๆ คือโปรเจกต์นี้ไม่มี CI service คำว่าเขียวหมายถึงเขียวบนเครื่องเราก่อนจะเรียกว่าเสร็จ ซึ่งเป็นข้อจำกัดของโปรเจกต์นักศึกษา ไม่ใช่สิ่งที่แนะนำให้ทำตามในงานจริง

4.3 Retrospective คือที่ที่กฎสำหรับ agent ถูกเขียนขึ้น#

ข้อนี้เป็นสิ่งที่ไม่ได้คาดไว้เลยตอนเริ่ม

Scrum กำหนดให้ retrospective จบด้วย การปรับปรุงที่เป็นรูปธรรมหนึ่งถึงสองข้อ ที่มีเจ้าของ ไม่ใช่จบด้วยรายการบ่น ปกติแล้วการปรับปรุงพวกนี้จะเป็นเรื่องของทีม เช่นเปลี่ยนเวลาประชุม หรือแบ่งงานใหม่

แต่พอทำงานกับ agent ผลลัพธ์ของ retrospective ส่วนใหญ่กลายเป็น บรรทัดใหม่ในเอกสารที่ agent อ่าน แทน

รูปแบบที่เกิดซ้ำ ๆ คือ agent ทำผิดแบบเดิมสองครั้ง เราเสียเวลาแก้สองครั้ง พอถึง retro คำถามไม่ใช่ "ทำไม agent โง่" แต่เป็น "กฎข้อนี้อยู่ที่ไหน แล้วทำไมมันหาไม่เจอ" คำตอบเกือบทุกครั้งคือกฎนั้นอยู่ในหัวเรา ไม่ได้อยู่ในไฟล์ไหนเลย

พอมองแบบนี้ retrospective ก็เปลี่ยนสภาพจากพิธีกรรมของทีม เป็นกลไกบำรุงรักษาคุณภาพของ context ที่เราป้อนให้ agent ซึ่งเป็นสินทรัพย์ที่มีค่าที่สุดอย่างหนึ่งของโปรเจกต์

4.4 ลำดับของ backlog สำคัญขึ้นเมื่อมี agent#

Backlog ของโปรเจกต์นี้เรียงตามลำดับการพึ่งพากันจริง ๆ โดยแต่ละข้อปลดล็อกข้อถัดไป

  1. ตาราง Course และ Topic — เป็นที่ยึดของ string ที่ส่วนอื่นอ้างถึงอยู่แล้ว
  2. Submissions — เงื่อนไขก่อนจะย้ายการตรวจบทเรียนออกจากฝั่ง client ซึ่งเป็นช่องโหว่ด้านความเชื่อถือข้อเดียวที่รู้ตัว
  3. Section / cohort — leaderboard และรายงานของอาจารย์ติดอยู่ที่แนวคิดที่ตาราง users ยังไม่มี
  4. Projects, awards, notifications — placeholder ชุดสุดท้าย

เหตุผลที่ลำดับสำคัญขึ้นเมื่อทำงานกับ agent คือ agent ยินดีสร้างของบนฐานที่ยังไม่มีอยู่จริงเสมอ ถ้าสั่งให้ทำ leaderboard ทั้งที่ยังไม่มีแนวคิดเรื่อง cohort มันจะไม่ทักท้วง แต่จะประดิษฐ์อะไรบางอย่างขึ้นมาแทน ซึ่งดูใช้ได้จนกว่าจะถึงวันที่เราทำ cohort จริง แล้วต้องรื้อทิ้งทั้งหมด

คนที่ต้องตอบว่า "ยังไม่ถึงเวลาทำอันนี้" คือเรา ไม่ใช่มัน

4.5 ข้อควรระวัง: ความเร็วของ agent ทำให้อยากขยายขอบเขต#

ผลข้างเคียงที่ชัดที่สุดของการมี agent คือ เมื่องานที่วางแผนไว้สองสัปดาห์เสร็จในสามวัน สัญชาตญาณแรกคือดึงงานเพิ่มเข้ามาใน sprint

Scrum ห้ามเรื่องนี้ไว้ด้วยเหตุผลที่ยังใช้ได้อยู่ — งานที่ดึงเข้ามากลางทางคืองานที่ไม่ผ่านการถามว่า "ทำเสร็จตาม Definition of Done ทันไหม" และประสบการณ์ตรงคือ งานที่เพิ่มเข้ามาแบบนั้นมักเป็นงานที่โค้ดเสร็จเร็วจริง แต่เทสต์ เอกสาร และการแปลสองภาษาไม่เสร็จ กลายเป็นหนี้ที่ไปโผล่ sprint ถัดไป

เวลาที่เหลือจาก agent ควรถูกใช้กับสิ่งที่ agent ทำแทนไม่ได้มากกว่า เช่น เดินทดสอบแอปจริง คุยกับคนที่จะใช้งาน หรือเขียนกฎที่ค้นพบใหม่ลงเอกสาร


5. Invariant ที่ agent มองไม่เห็น ต้องเขียนไว้เป็นลายลักษณ์อักษร#

นี่คือบทเรียนที่ได้มาจากการโดนเจ็บจริง

โปรเจกต์นี้เป็นสองภาษา ข้อความทุกคำที่มนุษย์อ่านอยู่ใน config/locales/th.yml และ config/locales/en.yml โดยสองไฟล์นี้มีโครงสร้างตรงกัน 1:1 และบางค่าเป็น array ที่โค้ดเรียกใช้ด้วย index

แปลว่ากฎที่แท้จริงของโปรเจกต์คือ เพิ่ม key ในไฟล์หนึ่ง ต้องเพิ่มในอีกไฟล์ที่ตำแหน่งเดียวกัน

กฎข้อนี้ไม่ปรากฏอยู่ในไฟล์ใดไฟล์หนึ่ง มันเป็นความสัมพันธ์ระหว่างสองไฟล์ agent ที่กำลังแก้ th.yml อยู่จะไม่มีทางรู้ ถ้าไม่มีใครบอก และผลลัพธ์คือหน้าจอภาษาอังกฤษพังเงียบ ๆ โดยไม่มี error

ตัวอย่างอื่นในโปรเจกต์เดียวกัน:

  • db/seeds.rb ต้อง idempotent เสมอ เพราะมันถูกรันซ้ำได้
  • TopicCompletion.record ต้อง idempotent เช่นกัน — เป็น find_or_initialize_by ที่ไม่ย้าย timestamp ที่ตั้งไว้แล้ว เพราะแบบฝึกหัดและโจทย์เขียนโค้ดรายงานผลผ่านทุกครั้งที่รัน ถ้าไม่ระวังตรงนี้ จำนวนหัวข้อที่เรียนจบจะพองขึ้นเรื่อย ๆ
  • param จากผู้ใช้ ห้ามเชื่อและห้าม raise ทุก controller ต้อง fallback ไปค่า default ผ่าน whitelist

ข้อสุดท้ายมีเหตุผลด้านความปลอดภัยที่ชัดเจน ชื่อ tab ใน AdminConsole.tab_for ถูกเอาไปต่อเป็น path ของ render โดยตรง ถ้าปล่อยให้ค่าอะไรก็ได้ผ่านเข้าไป นั่นคือช่องโหว่ template injection ทันที

บทเรียน: AI agent เก่งเรื่องรูปแบบที่เห็นได้จากโค้ดตรงหน้า แต่มองไม่เห็นข้อตกลงที่กระจายอยู่ระหว่างไฟล์ งานของเราคือเปลี่ยนข้อตกลงพวกนั้นให้เป็นอย่างใดอย่างหนึ่ง — เขียนลงเอกสารที่ agent อ่านได้ หรือดีกว่านั้นคือเปลี่ยนให้เป็นเทสต์ที่ bin/ci จับได้


6. งานเปลี่ยนจาก "เขียนโค้ด" เป็น "อ่านโค้ดและตัดสินใจ"#

เรื่องนี้อาจเป็นการเปลี่ยนแปลงที่ใหญ่ที่สุดสำหรับเด็กจบใหม่

สิ่งที่มหาวิทยาลัยฝึกมาคือการเขียนโค้ดจากศูนย์ให้ได้ แต่งานจริงในตอนนี้คือการอ่าน diff ที่ agent เขียนมา แล้วตอบให้ได้ว่ามันถูกไหม เข้ากับสถาปัตยกรรมที่วางไว้หรือเปล่า และมันพังตรงไหนได้บ้าง

ตัวอย่างที่ชัดที่สุดในโปรเจกต์นี้คือ LearnerProgress ซึ่งเป็นคลาสธรรมดาที่โหลด row ของผู้เรียนมา ครั้งเดียว แล้วพับข้อมูลใน Ruby แทนที่จะ aggregate ใน SQL

ถ้าดูเผิน ๆ นี่ขัดกับสิ่งที่เรียนมาว่า "ให้ฐานข้อมูลทำงานหนักแทน" และถ้า agent เสนอมาแบบนี้ คนที่อ่านไม่เป็นอาจสั่งให้แก้เป็น SQL ทันที แต่เหตุผลที่เลือกแบบนี้มีสามข้อและถูกต้องในบริบทนี้

  1. ปริมาณข้อมูลคือหนึ่ง row ต่อหนึ่งหัวข้อต่อหนึ่งนักศึกษา ซึ่งเล็กมาก
  2. การคำนวณวันที่ต้องใช้ Time.zone ของ Rails ไม่ใช่ของ SQLite
  3. หน้าจอต่าง ๆ ขอข้อมูลชุดเดียวกันในหกมุมมองที่ต่างกัน โหลดครั้งเดียวแล้วพับหลายแบบจึงคุ้มกว่า

ประกอบกับการโหลดแบบ includes(:course, :topic) ทำให้ใช้สามคิวรีคงที่ ไม่ว่าจะมีกี่ row

การจะเถียงกับ agent เรื่องแบบนี้ได้ ต้องเข้าใจทั้ง N+1 query, time zone และ access pattern ของหน้าจอ ซึ่งเป็นความรู้ที่ไม่ได้หายไปไหนเลย มันแค่ถูกใช้ในจังหวะที่ต่างจากเดิม


7. สิ่งที่ Rails ยังยาก และข้อที่ต้องคิดให้ดีก่อนตาม#

เพื่อความเป็นธรรม ไม่ใช่ทุกอย่างจะดีไปหมด

Magic ยังเป็น magic อยู่ เวลา agent เขียนโค้ดที่ใช้ Active Record callback หรือ concern ซ้อนกันหลายชั้น การไล่ว่าอะไรรันตอนไหนยากกว่าโค้ดที่เขียนตรง ๆ มาก โดยเฉพาะกับคนที่เพิ่งเริ่มใช้ Rails การอ่าน ApplicationController แล้วเข้าใจว่ามี before_action อะไรทำงานอยู่บ้างต้องใช้เวลาพอสมควร

ลำดับของ concern มีความหมาย ในโปรเจกต์นี้ต้อง include Authentication ก่อน include Authorization เพราะต้องการให้การเด้งไป /login ชนะการเด้งเพราะสิทธิ์ไม่พอ นี่คือ bug ที่รอเกิดถ้าใครสลับสองบรรทัดนี้โดยไม่รู้ และเป็นอีกตัวอย่างของ invariant ที่มองไม่เห็นจากโค้ด

ตลาดงานไทยเป็นอีกเรื่อง ถ้าเป้าหมายคือหางานในไทย จำนวนตำแหน่ง Rails น้อยกว่า JavaScript/TypeScript อย่างเทียบกันไม่ได้ ข้อนี้ต้องยอมรับตรง ๆ สิ่งที่ได้จากการเรียน Rails จึงไม่ใช่ทักษะที่เอาไปสมัครงานได้โดยตรง แต่เป็นการได้เห็นว่าเฟรมเวิร์กที่ออกแบบมาคนละปรัชญากับที่เคยใช้ มันแก้ปัญหาเดียวกันด้วยวิธีไหน ซึ่งอันนั้นย้ายข้ามภาษาได้

เอกสารเก่าปนใหม่ Rails มีอายุสามสิบปี ตัวอย่างโค้ดบนอินเทอร์เน็ตส่วนใหญ่เป็นของเวอร์ชันเก่า และ agent ก็ดูดของเก่าพวกนั้นมาด้วย อาการที่เจอบ่อยคือมันเสนอวิธีของ Rails 6 หรือแนะนำให้ลง Devise ทั้งที่ Rails 8 มี generate authentication มาให้ในตัวแล้ว


8. สรุป: สิ่งที่อยากบอกตัวเองเมื่อตอนเริ่ม#

ถ้าย้อนกลับไปได้ จะบอกตัวเองห้าข้อ

หนึ่ง เลือกเฟรมเวิร์กโดยดูว่ามันบังคับให้ตัดสินใจน้อยแค่ไหน ไม่ใช่ดูว่ามันให้อิสระมากแค่ไหน ในยุคที่ AI ช่วยเขียน อิสระที่มากเกินไปแปลว่าต้องอธิบายมากขึ้น

สอง ทำ bin/ci หรือคำสั่งเทียบเท่าตั้งแต่วันแรก ก่อนจะมีโค้ดให้ตรวจด้วยซ้ำ เพราะมันเปลี่ยนวิธีสั่งงาน agent ไปเลย

สาม ทุกครั้งที่พบว่าตัวเองกำลังจะพิมพ์อธิบายกฎเดิมซ้ำใน prompt นั่นคือสัญญาณว่ากฎนั้นควรถูกเขียนลงไฟล์ หรือกลายเป็นเทสต์

สี่ อย่าทิ้งกระบวนการเพราะคิดว่า AI ทำให้มันไม่จำเป็นแล้ว sprint goal ที่คมคือ prompt ที่ดี Definition of Done ที่เครื่องตรวจได้คือสิ่งที่ทำให้ agent วนแก้เองได้ และ retrospective คือที่ที่กฎสำหรับ agent ถูกเขียนขึ้น กระบวนการไม่ได้ช้าลงเพราะ AI มันแค่เปลี่ยนไปคุมเรื่องอื่นแทน

ห้า อย่าหยุดเรียนเรื่องพื้นฐาน เพราะมูลค่าของมันย้ายจาก "เขียนออกมาได้" ไปเป็น "ตัดสินได้ว่าสิ่งที่ agent เขียนมานั้นถูกหรือผิด" ซึ่งข้อหลังต้องการความเข้าใจที่ลึกกว่าเดิมด้วยซ้ำ

การเริ่ม Rails ในปี 2026 ไม่ใช่การย้อนยุค แต่เป็นการค้นพบว่าปรัชญาที่ออกแบบไว้ตั้งแต่ยังไม่มี AI มันเข้ากับการทำงานร่วมกับ AI ได้ดีอย่างน่าประหลาดใจ

โค้ดทั้งหมดของโปรเจกต์อยู่ที่ github.com/NakaSato/UTCC-AI-ACADEMY และตัวเว็บอยู่ที่ academy.boring9.dev

#ruby#rails#ai-agent#hotwire#web-development#career
Share