13 ธ.ค. 2568·อ่าน 3 นาที

ขยาย backend Go ที่ส่งออกแล้วด้วย middleware แบบกำหนดเองอย่างปลอดภัย

ขยาย backend Go ที่ส่งออกโดยไม่สูญเสียการเปลี่ยนแปลง: จะวางโค้ดกำหนดเองไว้ที่ไหน วิธีเพิ่ม middleware และ endpoints และวิธีวางแผนอัปเกรด

ขยาย backend Go ที่ส่งออกแล้วด้วย middleware แบบกำหนดเองอย่างปลอดภัย

ปัญหาที่เกิดขึ้นเมื่อคุณปรับแต่งโค้ดที่ส่งออกแล้ว

โค้ดที่ส่งออกไม่เหมือนกับ repo Go ที่เขียนด้วยมือเสมอไป กับแพลตฟอร์มอย่าง AppMaster backend ถูกสร้างจากโมเดลเชิงภาพ (สคีมาข้อมูล, กระบวนการธุรกิจ, การตั้งค่า API) เมื่อคุณ re-export ตัวสร้างอาจเขียนทับส่วนใหญ่ของโค้ดให้ตรงกับโมเดลที่อัปเดต นั่นดีสำหรับการรักษาความสะอาด แต่เปลี่ยนวิธีที่คุณควรปรับแต่ง

ความผิดพลาดที่พบบ่อยที่สุดคือการแก้ไขไฟล์ที่ถูกสร้างขึ้นโดยตรง มันอาจทำงานครั้งหนึ่ง แล้วการส่งออกครั้งถัดไปจะเขียนทับการเปลี่ยนแปลงของคุณหรือสร้างความขัดแย้งในการรวมโค้ดที่น่าเกลียด ยิ่งไปกว่านั้น การแก้ไขด้วยมือเล็กน้อยอาจทำลายสมมติฐานที่ตัวสร้างใช้ (ลำดับ routing, โซ่ middleware, การตรวจสอบคำขอ) แอปยังคอมไพล์ได้ แต่พฤติกรรมเปลี่ยนไป

การปรับแต่งอย่างปลอดภัยหมายถึงการที่การเปลี่ยนแปลงของคุณทำซ้ำได้และง่ายต่อการตรวจสอบ ถ้าคุณสามารถ re-export backend, นำเลเยอร์กำหนดเองของคุณไปใช้ และเห็นอย่างชัดเจนว่าสิ่งใดเปลี่ยน คุณอยู่ในจุดที่ดี ถ้าการอัปเกรดทุกครั้งรู้สึกเหมือนการทำโบราณคดี นั่นไม่ดี

นี่คือปัญหาที่คุณมักเจอเมื่อการปรับแต่งเกิดในที่ผิด:

  • การแก้ไขของคุณหายไปหลังการ re-export หรือคุณต้องใช้เวลาหลายชั่วโมงแก้ความขัดแย้ง
  • เส้นทางเปลี่ยนและ middleware ของคุณไม่ทำงานตรงที่คาดหวัง
  • โลจิกถูกทำซ้ำระหว่างโมเดล no-code และโค้ด Go แล้วเริ่มเบี่ยงเบน
  • "การเปลี่ยนบรรทัดเดียว" กลายเป็น fork ที่ไม่มีใครอยากแตะ

กฎง่ายๆ ช่วยตัดสินใจว่าการเปลี่ยนแปลงควรวางไว้ที่ไหน ถ้าการเปลี่ยนแปลงเป็นส่วนหนึ่งของพฤติกรรมทางธุรกิจที่คนที่ไม่ใช่นักพัฒนาควรปรับได้ (ฟิลด์, การตรวจสอบความถูกต้อง, เวิร์กโฟลว์, สิทธิ์) ให้ใส่ไว้ในโมเดล no-code ถ้าเป็นพฤติกรรมโครงสร้างพื้นฐาน (การผสาน auth แบบกำหนดเอง, การบันทึกคำขอ, header พิเศษ, rate limit) ให้ใส่มันไว้ในเลเยอร์ Go แบบกำหนดเองที่อยู่รอดหลังการ re-export

ตัวอย่าง: การบันทึก audit สำหรับทุกคำขอมักเป็น middleware (โค้ดกำหนดเอง) ขณะที่ฟิลด์ใหม่ที่จำเป็นในคำสั่งซื้อมักเป็นโมเดลข้อมูล (no-code) แยกส่วนให้ชัดและการอัปเกรดจะคาดเดาได้

ทำแผนที่โค้ดเบส: ส่วนที่สร้างได้ vs ส่วนของคุณ

ก่อนขยาย backend ที่ส่งออก ให้ใช้เวลา 20 นาทีทำแผนที่ว่าส่วนใดจะถูกสร้างใหม่เมื่อ re-export และส่วนใดเป็นของคุณจริงๆ แผนที่นี้คือสิ่งที่จะทำให้อัปเกรดน่าเบื่อ (ดี)

โค้ดที่ถูกสร้างมักบอกตัวเองได้: คอมเมนต์หัวไฟล์เช่น "Code generated" หรือ "DO NOT EDIT", ชื่อลักษณะที่สม่ำเสมอ และโครงสร้างที่เป็นรูปแบบเดียวกันพร้อมคอมเมนต์มนุษย์น้อย

วิธีปฏิบัติในการจัดหมวด repo คือแยกทุกอย่างเป็นสามกลุ่ม:

  • Generated (อ่านอย่างเดียว): ไฟล์ที่มีเครื่องหมายของ generator, รูปแบบซ้ำๆ, หรือโฟลเดอร์ที่ดูเหมือน skeleton ของ framework
  • Owned by you: แพ็กเกจที่คุณสร้าง, ตัวห่อ (wrappers), และการกำหนดค่าที่คุณควบคุม
  • Shared seams: จุดเชื่อมที่ตั้งใจให้ลงทะเบียนได้ (routes, middleware, hooks) ที่การแก้ไขเล็กน้อยอาจจำเป็นแต่ควรน้อยที่สุด

ปฏิบัติกับกลุ่มแรกเป็น read-only แม้ว่าคุณจะแก้ไขได้ทางเทคนิค ถ้าคุณเปลี่ยนมัน ให้ถือไว้ว่า generator จะเขียนทับอีกครั้งหรือคุณจะแบกภาระการ merge ตลอดไป

ทำให้ขอบเขตเป็นเรื่องจริงสำหรับทีมโดยเขียนโน้ตสั้น ๆ เก็บไว้ใน repo (เช่น README ที่รูท) เขียนให้ชัดเจน:

"Generator-owned files: anything with a DO NOT EDIT header and folders X/Y. Our code lives under internal/custom (or similar). Only touch wiring points A/B, and keep changes there small. Any wiring edit needs a comment explaining why it can't live in our own package."

โน้ตเดียวนี้ป้องกันการแก้ไขด่วนกลายเป็นปัญหาอัปเกรดยาว

จะวางโค้ดกำหนดเองที่ไหนเพื่อให้การอัปเกรดง่าย

กฎที่ปลอดภัยที่สุดคือ: ถือว่าโค้ดที่ส่งออกเป็น read-only แล้วใส่การเปลี่ยนแปลงของคุณในพื้นที่กำหนดเองที่เป็นเจ้าของชัดเจน เมื่อคุณ re-export ในภายหลัง (เช่น จาก AppMaster) คุณต้องการให้การ merge เป็น "แทนที่โค้ดที่สร้างไว้, เก็บโค้ดกำหนดเองไว้"

สร้างแพ็กเกจแยกสำหรับการเพิ่มเติมของคุณ มันสามารถอยู่ใน repo ได้ แต่ไม่ควรถูกรวมเข้ากับแพ็กเกจที่สร้างขึ้น โค้ดที่สร้างรันแกนหลักของแอป; แพ็กเกจของคุณเพิ่ม middleware, routes และ helper

เลย์เอาต์ปฏิบัติได้:

  • internal/custom/ สำหรับ middleware, handlers, และ helper เล็ก ๆ
  • internal/custom/routes.go สำหรับลงทะเบียน routes กำหนดเองในที่เดียว
  • internal/custom/middleware/ สำหรับโลจิกคำขอ/การตอบกลับ
  • internal/custom/README.md ที่มีกฎเล็ก ๆ สำหรับการแก้ไขในอนาคต

หลีกเลี่ยงการแก้ wiring ของเซิร์ฟเวอร์ในห้าจุด Aim for one thin "hook point" ที่คุณแนบ middleware และลงทะเบียน routes เพิ่มเติม หากโครงการที่สร้างแล้วเผย router หรือ handler chain ให้เสียบเข้าไปที่นั่น ถ้าไม่มี ให้เพิ่มไฟล์ integration เดียวใกล้ entrypoint ที่เรียกบางอย่างอย่าง custom.Register(router)

เขียนโค้ดกำหนดเองเสมือนว่าคุณอาจวางมันลงใน export ใหม่พรุ่งนี้ เก็บ dependencies ให้น้อยที่สุด หลีกเลี่ยงการคัดลอก types ที่สร้างขึ้นเมื่อสามารถทำได้ และใช้ตัวดัดแปลง (adapters) เล็ก ๆ แทน

ขั้นตอนทีละขั้น: เพิ่ม middleware แบบปลอดภัย

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

เริ่มจากทำให้ middleware แคบ: การบันทึกคำขอ, การตรวจสอบ auth แบบง่าย, rate limit หรือ request ID หากมันพยายามทำหลายงาน คุณจะต้องเปลี่ยนไฟล์มากขึ้นในภายหลัง

สร้างแพ็กเกจเล็ก ๆ (ตัวอย่าง internal/custom/middleware) ที่ไม่จำเป็นต้องรู้จักทั้งแอป เก็บ public surface ให้เล็ก: ฟังก์ชัน constructor เดียวที่คืน wrapper ของ handler มาตรฐาน Go

package middleware

import "net/http"

func RequestID(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Add header, log, or attach to context here.
		next.ServeHTTP(w, r)
	})
}

ตอนนี้เลือกจุด integration หนึ่งจุด: ที่ที่ router หรือ HTTP server ถูกสร้าง ลงทะเบียน middleware ของคุณตรงนั้นเพียงครั้งเดียว และหลีกเลี่ยงการกระจายการเปลี่ยนแปลงไปยังแต่ละ route

เก็บวงจรการตรวจสอบให้สั้น:

  • เพิ่มเทสต์หนึ่งชุดที่มุ่งเป้าโดยใช้ httptest เพื่อตรวจผลลัพธ์หนึ่งอย่าง (รหัสสถานะหรือ header)
  • ทดสอบด้วยการเรียกด้วยมือหนึ่งครั้งและยืนยันพฤติกรรม
  • ยืนยันว่า middleware ทำงานอย่างสมเหตุสมผลเมื่อเกิดข้อผิดพลาด
  • เพิ่มคอมเมนต์สั้น ๆ ใกล้บรรทัดการลงทะเบียนเพื่ออธิบายว่าทำไมมันถึงมีอยู่

diff เล็ก จุดเชื่อมเดียว ง่ายต่อการ re-export

ขั้นตอนทีละขั้น: เพิ่ม endpoint ใหม่โดยไม่ต้อง fork ทุกอย่าง

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

ถือว่าโค้ดที่ถูกสร้างไว้เป็น read-only และเพิ่ม endpoint ของคุณในแพ็กเกจกำหนดเองขนาดเล็กที่แอป import นั่นคือสิ่งที่ทำให้อัปเกรดสมเหตุสมผล

เริ่มโดยเขียนสัญญาไว้ก่อนแตะโค้ด endpoint ยอมรับอะไร (query params, JSON body, headers)? คืนค่าอะไร (รูปร่าง JSON)? เลือกรหัสสถานะล่วงหน้าเพื่อไม่ให้จบลงที่ "อะไรก็ตามที่ทำงานได้"

สร้าง handler ในแพ็กเกจกำหนดเองของคุณ ทำให้มันเรียบง่าย: อ่านอินพุต, ตรวจสอบ, เรียกใช้บริการหรือ helper ของฐานข้อมูลที่มีอยู่, เขียนการตอบกลับ

ลงทะเบียน route ที่จุด integration เดียวกันที่คุณใช้สำหรับ middleware ไม่ใช่ภายในไฟล์ handler ที่สร้างไว้ มองหาจุดที่ router ถูกประกอบในช่วง startup แล้ว mount routes กำหนดเองของคุณที่นั่น ถ้าโปรเจกต์ที่สร้างแล้วรองรับ hooks หรือการลงทะเบียนแบบกำหนดเอง ใช้สิ่งนั้น

เช็คลิสต์สั้น ๆ เพื่อให้พฤติกรรมสอดคล้อง:

  • ตรวจสอบอินพุตตั้งแต่ต้น (ฟิลด์ที่จำเป็น, รูปแบบ, min/max)
  • ส่งคืนรูปแบบข้อผิดพลาดเดียวกันทุกที่ (message, code, details)
  • ใช้ context timeouts เมื่อการทำงานอาจค้าง (DB, เรียกเครือข่าย)
  • บันทึกข้อผิดพลาดที่ไม่คาดคิดครั้งเดียว แล้วคืนค่า 500 ที่สะอาด
  • เพิ่มเทสต์เล็ก ๆ ที่เรียก route ใหม่และตรวจสถานะกับ JSON

ยืนยันด้วยว่า router ลงทะเบียน endpoint ของคุณเพียงครั้งเดียว การลงทะเบียนซ้ำเป็นกับดักหลังการ merge ที่พบบ่อย

รูปแบบการรวมที่ช่วยให้การเปลี่ยนแปลงถูกจำกัด

สร้างเครื่องมือภายในได้เร็วขึ้น
สร้างเครื่องมือภายในหรือพอร์ทัลแอดมินอย่างรวดเร็ว แล้วขยายด้วย middleware Go แบบกำหนดเอง
เริ่มฟรี

ปฏิบัติต่อ backend ที่สร้างเป็น dependency ชอบ composition: เดินสายฟีเจอร์รอบ ๆ แอปที่สร้างแทนการแก้ไขลอจิกแกนของมัน

เลือก configuration และ composition

ก่อนเขียนโค้ด ตรวจสอบว่าพฤติกรรมนั้นเพิ่มได้ผ่านการตั้งค่า, hooks, หรือ composition มาตรฐานหรือไม่ Middleware เป็นตัวอย่างที่ดี: เพิ่มที่ขอบ (router/HTTP stack) เพื่อให้สามารถลบหรือเปลี่ยนลำดับได้โดยไม่แตะ business logic

ถ้าคุณต้องการพฤติกรรมใหม่ (rate limiting, audit logging, request IDs) เก็บมันไว้ในแพ็กเกจของคุณเองและลงทะเบียนจากไฟล์ integration เดียว ในการรีวิวควรอธิบายได้ง่าย: "แพ็กเกจใหม่หนึ่งตัว จุดลงทะเบียนหนึ่งจุด"

ใช้ adapters เพื่อหลีกเลี่ยงการรั่วไหลของ types ที่สร้าง

โมเดลและ DTO ที่สร้างมักเปลี่ยนระหว่างการส่งออก เพื่อลดความเจ็บปวดเมื่ออัปเกรด ให้แปลที่ขอบเขต:

  • แปลงประเภทคำขอที่สร้างเป็น struct ภายในของคุณเอง
  • รันโลจิกโดเมนโดยใช้เฉพาะ struct ของคุณ
  • แปลงผลลัพธ์กลับเป็นประเภทการตอบกลับที่สร้าง

ด้วยวิธีนี้ ถ้าประเภทที่สร้างเปลี่ยน ตัวคอมไพเลอร์จะชี้คุณไปยังจุดเดียวที่จะอัปเดต

เมื่อคุณจำเป็นต้องแตะโค้ดที่สร้างจริง ๆ ให้แยกมันไว้ในไฟล์ wiring เดียว หลีกเลี่ยงการแก้ไขหลาย handler ที่สร้างขึ้น

// internal/integrations/http.go
func RegisterCustom(r *mux.Router) {
    r.Use(RequestIDMiddleware)
    r.Use(AuditLogMiddleware)
}

กฎปฏิบัติ: หากคุณบรรยายการเปลี่ยนแปลงไม่ออกใน 2-3 ประโยค แปลว่ามันน่าจะยุ่งเกินไป

วิธีรักษาขนาด diff ให้อยู่ในระดับจัดการได้เมื่อเวลาผ่านไป

เป้าหมายคือการที่การ re-export ไม่กลายเป็นสัปดาห์ของความขัดแย้ง เก็บการแก้ไขให้เล็ก หาได้ง่าย และอธิบายได้ง่าย

ใช้ Git ตั้งแต่วันแรกและแยกการอัปเดตที่สร้างออกจากงานกำหนดเอง ถ้าคุณผสมมัน คุณจะไม่รู้ว่าอะไรเป็นสาเหตุของบั๊กในภายหลัง

รูทีนการ commit ที่อ่านง่าย:

  • หนึ่งจุดประสงค์ต่อหนึ่ง commit ("Add request ID middleware", ไม่ใช่ "misc fixes")
  • อย่าผสมการเปลี่ยนแปลงเฉพาะรูปแบบกับการเปลี่ยนแปลงเชิงตรรกะ
  • หลังการ re-export ให้ commit การอัปเดตที่สร้างก่อน แล้วค่อย commit การปรับแต่งของคุณ
  • ใช้ข้อความ commit ที่กล่าวถึงแพ็กเกจหรือไฟล์ที่คุณแตะ

เก็บ CHANGELOG_CUSTOM.md ง่าย ๆ ที่รายการการปรับแต่งแต่ละรายการ ทำไมมันถึงมี และมันอยู่ที่ไหน นี่มีประโยชน์เป็นพิเศษกับการส่งออกจาก AppMaster เพราะแพลตฟอร์มสามารถสร้างโค้ดใหม่ทั้งหมดได้และคุณต้องการแผนที่อย่างรวดเร็วของสิ่งที่ต้องนำกลับหรือยืนยัน

ลดเสียงรบกวนของ diff ด้วยการจัดรูปแบบและกฎ lint ที่สอดคล้องกัน รัน gofmt ในทุก commit และรันเช็กเดียวกันใน CI หากโค้ดที่สร้างใช้สไตล์เฉพาะ อย่า "ทำความสะอาด" ด้วยมือเว้นแต่คุณพร้อมจะทำความสะอาดนั้นซ้ำหลังการ re-export

ถ้าทีมของคุณทำการแก้ไขด้วยมือแบบเดียวกันหลังการส่งออกทุกครั้ง ให้พิจารณา workflow แบบแพตช์: export, apply patches (หรือสคริปต์), รันเทสต์, ปล่อย

วางแผนการอัปเกรด: re-export, merge และยืนยัน

หยุดการแยก Fork โค้ดที่สร้างขึ้น
ประหยัดเวลาด้วยการสร้างแอปแกนกลาง แล้วเขียนโค้ดเฉพาะส่วนที่ต้องการเท่านั้น
สร้างด้วย No Code

การอัปเกรดจะง่ายสุดเมื่อคุณปฏิบัติต่อ backend ว่าเป็นสิ่งที่คุณสามารถสร้างใหม่ได้ ไม่ใช่สิ่งที่บำรุงรักษาด้วยมือไปตลอด เป้าหมายคงที่: re-export โค้ดสะอาด แล้วนำพฤติกรรมกำหนดเองของคุณกลับมาผ่านจุดรวมเดิมทุกครั้ง

เลือกจังหวะการอัปเกรดที่เข้ากับความเสี่ยงและความถี่การเปลี่ยนแปลงของแอป:

  • ตามการปล่อยของแพลตฟอร์มถ้าคุณต้องการแพตช์ความปลอดภัยหรือฟีเจอร์ใหม่อย่างรวดเร็ว
  • แบบรายไตรมาสถ้าแอปนิ่งและการเปลี่ยนแปลงน้อย
  • เฉพาะเมื่อจำเป็นถ้า backend เปลี่ยนไม่บ่อยและทีมเล็ก

เมื่อถึงเวลาจะอัปเกรด ให้ทำการ re-export ทดลองในสาขาแยก สร้างและรันเวอร์ชันที่ส่งออกใหม่ก่อน เพื่อคุณจะรู้ว่าอะไรเปลี่ยนก่อนที่ชั้นกำหนดเองจะเข้ามา

จากนั้นนำการปรับแต่งกลับผ่าน seams ที่วางแผนไว้ (การลงทะเบียน middleware, กลุ่ม router กำหนดเอง, แพ็กเกจกำหนดเองของคุณ) หลีกเลี่ยงการแก้ไขจิ๋วในไฟล์ที่สร้าง หากการเปลี่ยนแปลงไม่สามารถแสดงผ่านจุดรวมได้ นั่นคือสัญญาณให้เพิ่ม seam ใหม่ครั้งเดียว แล้วใช้มันตลอดไป

ยืนยันด้วยเช็คลิสต์การถดถอยสั้น ๆ มุ่งที่พฤติกรรม:

  • ฟลูว์การยืนยันตัวตนทำงาน (ล็อกอิน, รีเฟรชโทเค็น, ออกจากระบบ)
  • 3 ถึง 5 endpoint สำคัญคืนค่าโค้ดสถานะและรูปแบบเดิม
  • หนึ่ง unhappy path ต่อ endpoint (อินพุตไม่ถูกต้อง, ขาด auth)
  • งานแบ็กกราวด์หรืองานตามกำหนดเวลาเรียกใช้งานได้
  • endpoint สุขภาพ/ความพร้อมคืนค่า OK ในสภาพแวดล้อมการปรับใช้ของคุณ

ถ้าคุณเพิ่ม middleware บันทึก audit ให้ยืนยันว่า logs ยังคงรวม user ID และชื่อเส้นทางสำหรับการเขียนหนึ่งรายการหลังการ re-export และ merge

ความผิดพลาดทั่วไปที่ทำให้อัปเกรดเจ็บปวด

วิธีที่เร็วที่สุดที่จะทำลายการ re-export ครั้งต่อไปคือการแก้ไขไฟล์ที่สร้างขึ้น "แค่ครั้งเดียว" มันรู้สึกไม่เป็นอันตรายเมื่อคุณแก้บั๊กเล็ก ๆ หรือเพิ่มการตรวจ header แต่เดือนต่อมาคุณอาจจำไม่ได้ว่าเปลี่ยนอะไร ทำไม และ generator ตอนนี้ผลิตผลลัพธ์เหมือนเดิมหรือไม่

กับดักอีกอย่างคือการกระจายโค้ดกำหนดเองไปทั่ว: helper ในแพ็กเกจหนึ่ง, การตรวจ auth ในอีกที่, ปรับแต่ง middleware ใกล้ routing, handler เฉพาะในโฟลเดอร์สุ่ม ไม่มีใครเป็นเจ้าของ และทุกการ merge กลายเป็นการค้นหา ไว้การเปลี่ยนแปลงไว้ในไม่กี่ที่ที่ชัดเจน

การผูกแน่นกับ internals ที่สร้างขึ้น

การอัปเกรดเจ็บปวดเมื่อโค้ดกำหนดเองของคุณพึ่งพา struct ภายในที่สร้าง, ฟิลด์ส่วนตัว, หรือรายละเอียดโครงสร้างแพ็กเกจแม้การ refactor เล็กน้อยในโค้ดที่สร้างก็สามารถทำให้การ build ของคุณล้มเหลว

ขอบเขตที่ปลอดภัย:

  • ใช้ DTO คำขอ/การตอบกลับที่คุณควบคุมสำหรับ endpoint กำหนดเอง
  • โต้ตอบกับเลเยอร์ที่สร้างผ่าน interfaces หรือฟังก์ชันที่ export มา ไม่ใช่ types ภายใน
  • ตัดสินใจ middleware โดยพึ่งพา HTTP primitives (headers, method, path) เมื่อเป็นไปได้

ข้ามการทดสอบในจุดที่ควรมี

บั๊กของ middleware และ routing เสียเวลาเพราะความล้มเหลวอาจดูเหมือน 401 แบบสุ่มหรือ "endpoint not found" เทสต์โฟกัสเล็ก ๆ ช่วยประหยัดชั่วโมงได้

ตัวอย่างสมจริง: คุณเพิ่ม audit middleware ที่อ่าน request body เพื่อบันทึก และอยู่ดี ๆ บาง endpoint เริ่มได้รับ body ว่าง เทสต์เล็ก ๆ ที่ส่ง POST ผ่าน router และตรวจทั้งผลด้าน audit และพฤติกรรม handler จะจับการถดถอยนั้นและให้ความมั่นใจก่อน/หลังการ re-export

เช็คลิสต์ด่วนก่อนปล่อย

ทำให้ repo ของคุณเป็นมิตรกับการอัปเกรด
รับซอร์สโค้ดจริงและทำให้อัปเกรดคาดเดาได้ด้วยขอบเขตการปรับแต่งที่ชัดเจน
ส่งออกซอร์ส

ก่อนปล่อยการเปลี่ยนแปลงกำหนดเอง ให้ทำการตรวจสอบด่วนที่จะปกป้องคุณใน re-export ต่อไป คุณควรรู้ว่าต้องนำกลับมาที่ไหน อยู่ตรงไหน และยืนยันอย่างไร

  • เก็บโค้ดกำหนดเองทั้งหมดไว้ในแพ็กเกจหรือโฟลเดอร์ชื่อชัดเจน (เช่น internal/custom/)
  • จำกัดจุดสัมผัสกับ wiring ที่สร้างไว้ไว้ที่หนึ่งหรือสองไฟล์ ทำให้เป็นสะพาน: ลงทะเบียน routes ครั้งเดียว ลงทะเบียน middleware ครั้งเดียว
  • จดลำดับของ middleware และเหตุผล ("Auth ก่อน rate limiting" และทำไม)
  • ให้แต่ละ endpoint ที่กำหนดเองมีเทสต์อย่างน้อยหนึ่งรายการยืนยันการทำงาน
  • เขียนขั้นตอนการอัปเกรดที่ทำซ้ำได้: re-export, นำเลเยอร์กำหนดเองกลับมา, รันเทสต์, ปล่อย

ถ้าทำได้เพียงอย่างเดียว ให้ทำโน้ตการอัปเกรด มันเปลี่ยน "คิดว่าน่าจะโอเค" เป็น "เราพิสูจน์ได้ว่ายังทำงาน"

ตัวอย่าง: เพิ่ม audit logging และ endpoint สุขภาพ

ก้าวไปไกลกว่าการมี backend อย่างเดียว
สร้างโซลูชันครบชุดด้วย backend เว็บ และแอปมือถือเนทีฟจากโมเดลเดียว
สร้างแอป

สมมติว่าคุณส่งออก backend Go (ตัวอย่างเช่นจาก AppMaster) แล้วคุณต้องการเพิ่มสองอย่าง: request ID พร้อม audit logging สำหรับการกระทำของแอดมิน และ /health endpoint สำหรับมอนิเตอร์ เป้าหมายคือเก็บการเปลี่ยนแปลงให้ง่ายต่อการนำกลับมาหลังการ re-export

สำหรับ audit logging ให้วางโค้ดไว้ที่ที่เป็นเจ้าของชัดเจน เช่น internal/custom/middleware/ สร้าง middleware ที่ (1) อ่าน X-Request-Id หรือสร้างใหม่ (2) เก็บไว้ใน context ของคำขอ (3) บันทึกบรรทัด audit สั้น ๆ สำหรับเส้นทางแอดมิน (method, path, user ID ถ้ามี, และผล) เก็บเป็นบรรทัดต่อคำขอและหลีกเลี่ยงการทิ้ง payload ขนาดใหญ่

เชื่อมต่อมันที่ขอบ ใกล้จุดที่ลงทะเบียน routes หาก router ที่สร้างมีไฟล์ setup เดียว ให้เพิ่ม hook เล็ก ๆ ที่นำเข้า middleware ของคุณแล้วนำไปใช้กับกลุ่ม admin เท่านั้น

สำหรับ /health ให้เพิ่ม handler เล็ก ๆ ใน internal/custom/handlers/health.go คืนค่า 200 OK พร้อม body สั้น ๆ เช่น ok อย่าใส่ auth เว้นแต่เครื่องมอนิเตอร์ของคุณต้องการ หากใส่ ให้จดไว้

เพื่อให้เปลี่ยนแปลงง่ายต่อการนำกลับมา โครงสร้างการ commit ควรเป็นแบบนี้:

  • Commit 1: เพิ่ม internal/custom/middleware/audit.go และเทสต์
  • Commit 2: เชื่อม middleware เข้ากับ admin routes (diff เล็กที่สุด)
  • Commit 3: เพิ่ม internal/custom/handlers/health.go และลงทะเบียน /health

หลังอัปเกรดหรือ re-export ให้ยืนยันพื้นฐาน: เส้นทางแอดมินยังต้องการ auth, request ID ปรากฏใน logs ของแอดมิน, /health ตอบอย่างรวดเร็ว, และ middleware ไม่เพิ่ม latency ที่สังเกตได้ภายใต้โหลดเบา

ขั้นตอนต่อไป: ตั้งเวิร์กโฟลว์การปรับแต่งที่คุณรักษาได้

ปฏิบัติต่อการส่งออกทุกครั้งเหมือนการ build ใหม่ที่คุณสามารถทำซ้ำได้ โค้ดกำหนดเองของคุณควรรู้สึกเหมือนเลเยอร์เสริม ไม่ใช่การเขียนทับ

ตัดสินใจว่ารอบหน้าควรอยู่ในโค้ดหรือโมเดล no-code กันแน่ กฎธุรกิจ รูปร่างข้อมูล และโลจิก CRUD มักอยู่ในโมเดล หนึ่ง-ออฟ-อินทิเกรชันและ middleware เฉพาะบริษัทมักอยู่ในโค้ดกำหนดเอง

ถ้าคุณใช้ AppMaster (appmaster.io) ออกแบบงานกำหนดเองของคุณเป็นเลเยอร์ขยายรอบ backend Go ที่สร้าง: เก็บ middleware, routes, และ helper ในชุดโฟลเดอร์เล็ก ๆ ที่คุณสามารถนำไปต่อเมื่อ re-export และเก็บไฟล์ที่ generator ครอบครองไม่ให้แตะ

การตรวจสอบสุดท้ายปฏิบัติ: ถ้าเพื่อนร่วมทีมสามารถ re-export ทำตามขั้นตอนของคุณ และได้ผลลัพธ์เหมือนกันในเวลาน้อยกว่าหนึ่งชั่วโมง แปลว่าเวิร์กโฟลว์ของคุณดูแลรักษาได้

คำถามที่พบบ่อย

ฉันจะแก้ไขไฟล์ Go ที่ส่งออกโดยตรงได้ไหม?

อย่าแก้ไขไฟล์ที่เป็นของ generator โดยตรง ให้ใส่การเปลี่ยนแปลงของคุณในแพ็กเกจที่เป็นเจ้าของชัดเจน (เช่น internal/custom/) แล้วเชื่อมต่อผ่านจุดรวมขนาดเล็กใกล้จุดเริ่มต้นของเซิร์ฟเวอร์ ด้วยวิธีนี้เมื่อ re-export ส่วนที่สร้างได้จะถูกแทนที่ ส่วนของคุณจะยังคงอยู่

ฉันจะรู้ได้อย่างไรว่าส่วนไหนของ repo ที่ส่งออกจะถูกสร้างใหม่?

ให้สมมติว่าไฟล์ที่มีคอมเมนต์เช่น “Code generated” หรือ “DO NOT EDIT” จะถูกเขียนทับอีกครั้ง นอกจากนี้โฟลเดอร์ที่มีโครงสร้างสม่ำเสมอ ชื่อที่ทำซ้ำ และคอมเมนต์น้อย ๆ มักเป็นลายเซ็นของ generator กฎที่ปลอดภัยที่สุดคือถือเอาไว้เป็น read-only แม้มันจะคอมไพล์ได้หลังจากแก้ไข

หน้าตาของ “single integration point” ที่ดีควรเป็นอย่างไร?

เก็บไฟล์ “hook” เดียวที่นำเข้าแพ็กเกจกำหนดเองของคุณและลงทะเบียนทุกอย่าง: middleware เส้นทางเพิ่มเติม และการเชื่อมต่อเล็ก ๆ หากคุณเริ่มแตะหลายไฟล์ routing หรือ handler ที่สร้างขึ้น แปลว่าคุณกำลังเริ่มแยก fork ที่จะยากต่อการอัปเกรด

ฉันจะเพิ่ม middleware แบบกำหนดเองโดยไม่ทำให้อัปเกรดพังได้อย่างไร?

เขียน middleware ในแพ็กเกจของคุณเองและทำให้มันแคบ เช่น request ID, การบันทึก audit, rate limit หรือ header พิเศษ แล้วลงทะเบียนมันเพียงครั้งเดียว ณ จุดสร้าง router หรือ stack HTTP ไม่ใช่ต่อ route ภายใน handler ที่สร้างไว้ การตรวจสอบด้วย httptest ให้ค่าสถานะหรือ header ที่คาดหวังมักเพียงพอที่จะจับการถดถอยหลังการ re-export

ฉันจะเพิ่ม endpoint ใหม่โดยไม่ทำการ fork backend ที่สร้างได้อย่างไร?

กำหนดสัญญาของ endpoint ก่อน แล้วเขียน handler ในแพ็กเกจกำหนดเองของคุณ จากนั้นลงทะเบียนเส้นทางที่จุดรวมเดียวกับที่ใช้สำหรับ middleware ให้ handler ทำงานเรียบง่าย: ตรวจสอบอินพุต เรียกใช้บริการที่มีอยู่ ส่งคืนรูปแบบข้อผิดพลาดที่สอดคล้อง และหลีกเลี่ยงการคัดลอกโลจิกของ handler ที่สร้างไว้ วิธีนี้จะทำให้การเปลี่ยนแปลงของคุณพกพาได้เมื่อต้องส่งออกใหม่

ทำไมเส้นทางและลำดับ middleware ถึงเปลี่ยนหลังจากการ re-export?

การเปลี่ยนแปลงลำดับการลงทะเบียนเส้นทางหรือกลุ่มสามารถเกิดขึ้นได้เมื่อ generator เปลี่ยนลำดับการลงทะเบียนหรือโครงสร้าง เพื่อป้องกัน ให้ใช้จุดรวมการลงทะเบียนที่เสถียรและจดลำดับ middleware ไว้ใกล้บรรทัดที่ลงทะเบียน หากลำดับสำคัญ (เช่น auth ก่อน audit) ให้ระบุไว้ชัดและตรวจสอบด้วยเทสต์เล็ก ๆ

ฉันจะหลีกเลี่ยงการทำสำเนาโลจิกระหว่างโมเดล no-code กับโค้ด Go ได้อย่างไร?

หากคุณทำกฎเดียวกันทั้งในโมเดล no-code และในโค้ดกำหนดเอง มันจะเริ่มเบี่ยงเบนเมื่อเวลาผ่านไป ให้เก็บกฎธุรกิจที่คนไม่ใช่นักพัฒนาควรปรับได้ (ฟิลด์ การตรวจสอบความถูกต้อง เวิร์กโฟลว์ สิทธิ์) ไว้ในโมเดล no-code และเก็บข้อกังวลด้านโครงสร้างพื้นฐาน (logging, การผสาน auth, rate limits, headers) ไว้ในชั้น Go แบบกำหนดเอง แบ่งแยกควรชัดเจนสำหรับคนที่อ่าน repo

ฉันจะหยุดโค้ดกำหนดเองของฉันไม่ให้พึ่งพา types ภายในของโค้ดที่สร้างขึ้นได้อย่างไร?

DTO ที่สร้างขึ้นและ struct ภายในอาจเปลี่ยนได้ระหว่างการส่งออก ดังนั้นแยกความเปลี่ยนแปลงเหล่านั้นไว้ที่ขอบเขต แปลงอินพุตที่สร้างเป็น struct ภายในของคุณเอง ทำงานโดเมนกับ struct เหล่านั้น แล้วแปลงผลลัพธ์กลับที่ขอบ วิธีนี้เมื่อประเภทเปลี่ยนหลังการ re-export คุณจะอัปเดตตัวแปลงเพียงจุดเดียว

เวิร์กโฟลว์ Git ที่ดีที่สุดสำหรับการ re-export และการปรับแต่งคืออะไร?

แยกการอัปเดตที่สร้างออกจากงานกำหนดเองใน Git เพื่อให้เห็นได้ว่าอะไรเปลี่ยนและเพราะเหตุใด โฟลว์ปฏิบัติคือ commit การเปลี่ยนแปลงที่ re-export มาก่อน แล้วจึง commit การเชื่อมต่อและการปรับแต่งแบบกำหนดเองไม่กี่บรรทัด การเก็บ changelog สั้น ๆ ที่บอกว่าคุณเพิ่มอะไรและมันอยู่ที่ไหนจะเร่งการอัปเกรดครั้งต่อไปได้มาก

ฉันวางแผนอัปเกรดอย่างไรเพื่อให้การ re-export ไม่กลายเป็นหลายวันของ conflict?

ทำการ re-export ทดลองบนสาขาแยก สร้างและรันเวอร์ชันที่ส่งออกใหม่ก่อน แล้วรันการตรวจสอบการถดถอยสั้น ๆ ก่อนนำชั้นกำหนดเองของคุณกลับเข้าไป หลังจากนั้นให้ใช้จุดรวมเดิมเพื่อสมัครการปรับแต่ง แล้วตรวจสอบ endpoint สำคัญ ๆ และเส้นทางที่ล้มเหลวหนึ่งทาง หากมีสิ่งใดที่ไม่สามารถแสดงผ่าน seam ได้ ให้เพิ่ม seam ใหม่ครั้งเดียวแล้วใช้ต่อไป

ง่ายต่อการเริ่มต้น
สร้างบางสิ่งที่ น่าทึ่ง

ทดลองกับ AppMaster ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม
ขยาย backend Go ที่ส่งออกแล้วด้วย middleware แบบกำหนดเองอย่างปลอดภัย | AppMaster