สวัสดีครับ! ผมชื่อโจเซฟ เป็นนักศึกษาวิทยาการคอมพิวเตอร์ที่วอเตอร์ลู และเพิ่งจบการฝึกงานด้านวิศวกรรมซอฟต์แวร์ที่ Slash ตั้งแต่เดือนกันยายนถึงธันวาคม 2025 สรุปสั้นๆ ในบล็อกนี้ – ผมสนุกมากและคิดว่าคุณควรเข้าร่วมด้วย ขอบคุณที่อ่านครับ

โอเค จริงจังแล้วนะครั้งนี้ ฉันเคยฝึกงานที่บริษัทหลากหลายแห่งมาก่อน ตั้งแต่สตาร์ทอัพเล็กๆ ที่เกี่ยวกับดนตรีไปจนถึงบริษัทเทคโนโลยีขนาดใหญ่ แต่ Slash มอบประสบการณ์ที่ผสมผสานระหว่างความเข้มข้นและความรับผิดชอบในแบบที่เป็นเอกลักษณ์ ซึ่งฉันไม่เคยพบที่อื่นมาก่อน ทีมมีความฉลาดและทำงานร่วมกันอย่างเหนียวแน่น และฉันได้เรียนรู้อย่างลึกซึ้งเกี่ยวกับหัวข้อที่ซับซ้อนมากมาย ตั้งแต่การดูแล Kubernetes ไปจนถึงประสิทธิภาพของฐานข้อมูล ที่สำคัญกว่านั้นคือมีความท้าทายที่น่าสนใจมากมายที่ฉันได้มีโอกาสแก้ไข ซึ่งเป็นเหตุผลที่ฉันตัดสินใจเขียนบล็อกเกี่ยวกับประสบการณ์ของฉัน!

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

เหตุผลหลักที่ทำให้ฉันเลือก Slash แทนบริษัทอื่น ๆ คือ:

  • มันเป็นบริษัทซีรีส์ B – ฉันไม่เคยทำงานที่สตาร์ทอัพขนาดนี้มาก่อน และต้องการสัมผัสกับสภาพแวดล้อมนั้น
  • พวกเขามีรายได้ต่อปีอยู่ที่ 150 ล้านดอลลาร์ ซึ่งถือว่าสูงมากสำหรับบริษัทที่อยู่ในรอบการระดมทุนซีรีส์ B – พวกเขาไม่ได้แค่เอาตัวรอดหรือประคองตัวไปวัน ๆ เท่านั้น แต่พวกเขากำลังขยายธุรกิจอย่างจริงจัง อีกทั้งยังเป็นข้อดีเพิ่มเติมที่บ่งบอกว่าพวกเขาน่าจะมีมาตรฐานด้านวิศวกรรมและการบริหารจัดการที่ดี ซึ่งสตาร์ทอัพขนาดเล็กหลายแห่งยังขาดอยู่
  • พวกเขากำลังเติบโตอย่างแข็งขัน – พวกเขาทำรายได้เพิ่มขึ้น 6 เท่าภายในหนึ่งปี และเพิ่งระดมทุนรอบซีรีส์ B ไปเมื่อไม่นานมานี้ บริษัทนี้อยู่ในยุคทองของการเติบโตอย่างรวดเร็ว และผมอยากทำงานกับบริษัทที่กำลังมีแรงขับเคลื่อนแบบนั้น
  • เป็นทีมที่อายุน้อยและมีพลังงานสูง – จากการสัมภาษณ์ของฉัน ฉันสามารถบอกได้ว่าผู้คนที่นี่เป็นคนสบายๆ และสนุกที่จะพูดคุยด้วย ฉันรู้สึกตื่นเต้นที่จะได้สร้างสิ่งต่างๆ ร่วมกับเพื่อนร่วมงานที่ฉันสามารถเข้าใจและเข้ากันได้
  • เป็นบริษัทขนาดเล็กและกระชับ – ผมได้บอกหรือยังว่ายอดรายได้ประจำปี 150 ล้านนี้ทำโดยทีมวิศวกรประมาณ 12 คน? นั่นคือ 12.5 ล้านต่อปี ต่อ วิศวกร ซึ่งมันสุดยอดมาก ชัดเจนว่าคนที่ Slash เป็นคนเก่งระดับแนวหน้า และผมจะได้รับความรับผิดชอบสูงพร้อมกับการเป็นพี่เลี้ยงที่ยอดเยี่ยม
  • ค่าตอบแทนและสวัสดิการสำหรับบริษัทขนาดเล็กอยู่ในระดับแนวหน้าสำหรับการฝึกงาน... 🤫

สรุปสั้นผมคิดว่าสภาพแวดล้อม ผู้คนที่มีความสามารถ และพลังงานของสตาร์ทอัพจะเป็นสถานที่ที่ยอดเยี่ยมในการเติบโต ผมคิดว่าผมจะมีโอกาสได้เป็นเจ้าของโครงการตั้งแต่ต้นจนจบ ตั้งแต่การตั้งโครงสร้างพื้นฐาน การวางแผนเพื่อรองรับการขยายตัว ไปจนถึงการคิดเกี่ยวกับการออกแบบระบบ และอื่นๆ อีกมากมาย ดังนั้นผมจึงตัดสินใจคว้าโอกาสนี้!

โครงการ: การแจ้งเตือน V2

นี่คือสิ่งพิเศษอย่างหนึ่งที่ฉันได้เรียนรู้เกี่ยวกับ Slash ทันทีที่เข้ามาทำงาน – พวกเขาจะมอบหมายโปรเจกต์สำคัญให้กับนักศึกษาฝึกงานและไว้วางใจว่าเราจะสามารถขับเคลื่อนงานนั้นให้สำเร็จได้ ในสัปดาห์ที่สองของฉัน ฉันได้รับมอบหมายให้ปรับปรุงระบบแจ้งเตือนการปฏิเสธการทำธุรกรรมบัตรใหม่ ฟังดูไม่ยากใช่ไหม? อย่างไรก็ตาม ระบบแจ้งเตือนเดิมมีปัญหาอยู่หลายจุด:

  • การเชื่อมต่ออย่างแน่นแฟ้นกับระบบเหตุการณ์ภายในของเรา โดยมีตรรกะทางธุรกิจฝังอยู่ในแกนหลักของบริการ
  • เมตริกอีเมลที่ไม่ดีและการส่งอีเมลถึงผู้รับที่แย่ลงเนื่องจากผู้ให้บริการที่เราใช้
  • ประสบการณ์ของนักพัฒนาที่ยากลำบาก ซึ่งทำให้นักวิศวกรต้องหลีกเลี่ยงระบบโดยสิ้นเชิงและเรียกใช้ API ของผู้ให้บริการโดยตรง

และแล้วภารกิจอันแยบยลก็มาถึง – เพื่อสร้างอีเมลแจ้งเตือนการลดลงที่ดีกว่าเดิม เราต้องการมากกว่าการแก้ไขเฉพาะจุด เราต้องการระบบแจ้งเตือนที่ได้รับการออกแบบใหม่ทั้งหมด และผมกลายเป็นผู้รับผิดชอบหลัก (DRI) สำหรับการออกแบบ การพัฒนา การเปิดตัว และการขยายระบบสำหรับโครงการทั้งหมด นั่นเป็นเรื่องที่น่ากลัวมาก... แต่ก็เป็นโอกาสอันยอดเยี่ยมในการออกแบบสถาปัตยกรรมที่จะรองรับการแจ้งเตือนนับล้านในอนาคต ผมรู้สึกตื่นเต้นที่จะได้เข้าร่วม

จากการศึกษาเกี่ยวกับระบบการแจ้งเตือนเดิมและการหารือกับวิศวกร ข้าพเจ้าได้ค้นพบหลักการสำคัญบางประการที่ระบบใหม่นี้ควรยึดถือ ได้แก่:

  1. การออกแบบที่ไม่ผูกติดกับเหตุการณ์ – บริการควรสามารถทำงานได้ทุกที่ ไม่ว่าจะเป็นสคริปต์ครั้งเดียวหรือตัวจัดการเหตุการณ์
  2. การออกแบบที่ไม่ผูกขาดกับผู้ให้บริการ – โอกาสในการถอดถอนและแนะนำผู้ให้บริการรายใหม่ได้อย่างง่ายดายโดยใช้ความพยายามเพียงเล็กน้อยนั้นประเมินค่าไม่ได้
  3. การออกแบบที่ให้ความสำคัญกับนักพัฒนาเป็นอันดับแรก – ไม่มีการบังคับใช้จากความรู้สึกหงุดหงิดอีกต่อไป ประสบการณ์ในการพัฒนาควรมีความเป็นธรรมชาติและง่ายที่สุดเท่าที่จะเป็นไปได้
  4. สามารถสังเกตได้และขยายขนาดได้ - การแจ้งเตือนควรมีการติดตามวงจรชีวิตอย่างสมบูรณ์ และในที่สุดเราควรสามารถรองรับอีเมลได้ถึง 3 ล้านฉบับต่อเดือน!

และจากสิ่งนี้ การออกแบบระบบ Notifications V2 ที่ใช้ผู้จัดการ (handler) เป็นศูนย์กลางจึงถือกำเนิดขึ้น ระบบนี้มุ่งเน้นไปที่ตัวจัดการการแจ้งเตือน (notification handlers) ซึ่งเปิดเผยฟังก์ชันการทำงานทั่วไปของผู้จัดการ เช่น การส่งการแจ้งเตือนหรือการประมวลผลเว็บฮุค สำหรับผู้ให้บริการช่องทางภายนอกเฉพาะ (เช่น Resend สำหรับอีเมล, Twilio สำหรับ SMS)

ที่แกนกลางของระบบคือ Intent แจ้งเตือน, เป็นวัตถุชั่วคราวทั่วไปที่ใช้สำหรับเก็บข้อมูลทั้งหมดสำหรับการแจ้งเตือน เช่น ผู้ให้บริการ, ช่องทาง, และอาร์กิวเมนต์ของข้อมูลที่ส่งต่อ แต่ละตัวจัดการจะกำหนดรูปแบบของเจตนาของตนเอง และวัตถุเหล่านี้มีประเภทที่ชัดเจนและรองรับการตรวจสอบประเภทแบบคงที่และการตรวจสอบภายใน ซึ่งหมายความว่านักพัฒนาไม่จำเป็นต้องรู้รายละเอียดทั้งหมดของผู้ให้บริการแต่ละราย – พวกเขาสามารถใช้ประโยชน์จากการเติมคำอัตโนมัติของ IDE และเชื่อถือว่าการตรวจสอบประเภทจะช่วยให้พวกเขาทำงานได้อย่างถูกต้อง

This example briefly illustrates how to use the service to send a variety of email notifications for an outgoing payment. All these create functions create notification intents, and they are strongly typed to suit the provider (in this case, Resend)
const sendOutgoingPaymentNotification = async (context: {
 transactionId: string;
 amount: number;
 recipientEmail: string;
 alertEmail: string;
}) => {
 const { transactionId, amount, recipientEmail, alertEmail } = context;
 const { originalSenderId } = getOriginalSenderForTransaction(transactionId);


 await notificationsV2Service.sendNotifications([
   // We can create a one-off notification easily...
   ResendNotification.create({
     email: {
       recipient: alertEmail,
       content: {
         subject: "INTERNAL ALERT: An employee just sent an outgoing payment",
         body: `An employee just sent an outgoing payment of $${amount}.`,
       },
     },
   }),
   // Or use a React Email template for pretty emails..
   ResendNotification.create({
     email: {
       recipient: recipientEmail,
       content: YouJustReceivedPaymentTemplate.withProps({ amount }),
     },
   }),
 ]);


 // Or send to specific users, respecting their preferences + registered emails
 await notificationsV2Service.sendNotificationsToUsers({
   users: [originalSenderId],
   preference: NotificationPreferences.PaymentConfirmation,
   createIntents: (user) => [
     ResendNotification.create({
       email: {
         recipient: user.registeredEmail,
         content: PaymentConfirmationTemplate.withProps({ amount }),
       },
     }),
   ],
 });
};

เพื่อให้แน่ใจว่าตรรกะทางธุรกิจ เช่น การตรวจสอบความชอบของผู้ใช้ ถูกนำไปใช้ในลักษณะที่เชื่อมโยงกันน้อย (low-coupled) เมื่อใดก็ตามที่นักพัฒนาเรียกใช้ฟังก์ชันส่ง ความตั้งใจ (intents) จะถูกส่งผ่านไปยัง ตัวกลางการแจ้งเตือน pipeline first. middleware สำหรับการแจ้งเตือนเป็นฟังก์ชันลำดับสูงที่มีขั้นตอนการประมวลผลล่วงหน้าเพื่อให้สามารถค้นหาฐานข้อมูลร่วมกันได้ และมีฟังก์ชันการประมวลผลที่อนุญาตให้เราอนุญาต, แปลง, หรือยกเลิกการแจ้งเตือนได้ ด้วยดีไซน์นี้ เราสามารถกำหนดฟังก์ชันการส่งที่แตกต่างกันซึ่งใช้ระบบพื้นฐานเดียวกันเพื่อกำหนดและแบ่งปันตรรกะการตรวจสอบได้ ตัวอย่างเช่น:

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

เป็นประโยชน์เพิ่มเติม นักพัฒนาในอนาคตสามารถเพิ่ม ลบ หรือแก้ไขฟังก์ชันการส่งใหม่ได้อย่างง่ายดาย โดยไม่ต้องติดตามฟังก์ชันที่ซับซ้อน


ตัวอย่างของบางโฟลว์ที่เราตัดสินใจใช้ โปรดสังเกตว่าฟังก์ชัน sendNotificationToUsers และ sendNotification ต่างก็ใช้ middleware แบบ heuristics แต่ฟังก์ชันที่ส่งโดยผู้ใช้จะเรียกใช้ validator ที่เฉพาะเจาะจงกับผู้ใช้มากกว่า


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


เมื่อผู้ให้บริการภายนอกใด ๆ ส่งการตอบกลับ webhook มาให้เรา ผู้จัดการจะทำการแปลงข้อมูลเหล่านั้นให้เป็นประเภทข้อมูลมาตรฐาน และส่งออกการกระทำที่มีประเภทชัดเจน (typed actions) การกระทำเหล่านี้ช่วยให้เราสามารถตอบสนองต่อเหตุการณ์ต่าง ๆ ได้ในรูปแบบที่มีโครงสร้างและปลอดภัยทางประเภทข้อมูล (type-safe) เมื่อได้รับการแจ้งเตือน โดยรักษาให้บริการของเราเป็นกลาง (unopinionated) มากที่สุดเท่าที่เป็นไปได้ ตัวอย่างเช่น

  • เรามีการดำเนินการสำหรับการอัปเดตผู้รับ – ด้วยวิธีนี้ ผู้จัดการสามารถอัปเดตผู้รับเป็น "ถูกแบนถาวร" หรือไม่ก็ได้ ขึ้นอยู่กับสิ่งที่ผู้ให้บริการและช่องทางกำหนดว่าเป็น "ถูกแบน"
  • เรามีการดำเนินการสำหรับการลองส่งการแจ้งเตือนอีกครั้ง – ตัวอย่างเช่น หากอีเมลถูกตีกลับชั่วคราว เราต้องการลองส่งอีเมลนั้นอีกครั้งแบบทวีคูณ หากผู้จัดการการดำเนินการตรวจสอบแล้วว่าการตอบกลับของผู้ให้บริการอีเมลคือ "ตีกลับ" ก็สามารถเรียกใช้การดำเนินการนี้ได้

ProcessWebhook returns the same body structure, allowing specific webhook logic to be handled by the actual provider itself.
const statusMap: Record<string, string> = {
 bounced: "failed",
 // more statuses here...
};


const ResendHandler = createHandler({
 // This is a simple example of something we could do
 processWebhook: (webhookBody: ResendWebhook) => {
   const notificationId = extractIdFromWebhook(webhookBody);


   const newInternalStatus = statusMap[webhookBody.state];
  
   const actions = newInternalStatus === "failed"
     ? [{ action: "retryNotification", notificationId }]
     : [];


   return {
     status: newInternalStatus,
     actions,
   };
 },
});

เฮ้อ! นั่นคือส่วนใหญ่ของกระบวนการแล้ว ระหว่างทาง ฉันได้พบกับปัญหาทางวิศวกรรมที่น่าสนใจอื่น ๆ อีกหลายอย่าง เช่น

  • รองรับการลองใหม่แบบทวีคูณ – เราจัดตารางงานลองใหม่ในอนาคตภายใน Temporal โดยขึ้นอยู่กับจำนวนครั้งที่ลองใหม่ไปแล้ว
  • การติดตามที่ดี – ตัวอย่างเช่น เราจัดเก็บรหัสการติดตาม รวมถึงการแจ้งเตือนและการลองใหม่ไว้ใน ชุดการแจ้งเตือน นิติบุคคล
  • การจัดกลุ่มการส่ง – การผสานรวมการส่งแบบรายบุคคลและการจัดกลุ่มการส่งช่วยให้เราหลีกเลี่ยงการเกินขีดจำกัดอัตราการส่งของผู้ให้บริการ และมอบความควบคุมให้กับนักพัฒนาเกี่ยวกับการส่งการแจ้งเตือนตามที่ต้องการ

ผมจะปล่อยให้วิศวกรใหม่ที่กำลังจะเข้าร่วมค้นพบรายละเอียดในโค้ดเอง เมื่อพวกเขาเข้าร่วมแล้ว 😉

โครงการ: ปรับโฉมใหม่

โครงการปรับโฉมใหม่เป็นการออกแบบใหม่ทั้งหมดของประสบการณ์การใช้งานเว็บไซต์ Slash โดยทีมงานของเรา (ดูบล็อกภาษาอังกฤษ ที่นี่โดย อัลเบิร์ต เทียน ผู้เป็นบิดาแห่งการดึงหน้า). ตอนนี้ฉันจะไม่ลงรายละเอียดมากเกี่ยวกับ Facelift ว่าคืออะไร (อีกครั้ง ดูบล็อกภาษาอังกฤษที่ยอดเยี่ยม) แต่โดยพื้นฐานแล้วมันคือการแนะนำไลบรารีคอมโพเนนต์ใหม่ + Tailwind ให้กับส่วนหน้าทั้งหมดของเรา ตอนนี้ เพื่อเตรียมตัวสำหรับการเปิดตัว ทีมวิศวกรตัดสินใจที่จะมีความสนุกเล็กน้อยและจัดแฮกวีคที่เน้นเฉพาะ! นี่ไม่ใช่สัปดาห์แฮ็กปกติที่ออฟฟิศของคุณหรอกนะ – เราขึ้นรถสองคัน ขับรถสามชั่วโมงไปยังกระท่อมที่ทะเลสาบแบส ใกล้กับโยเซมิตี และใช้เวลาทั้งสัปดาห์ในการสร้าง ปรับโครงสร้าง และสนุกกันอย่างแท้จริงกับเพื่อนร่วมงาน

ในสัปดาห์นี้ ฉันได้เปลี่ยนงานทั้งหมดจากงานด้านหลังบ้านมาช่วยปรับปรุงกระบวนการทำธุรกรรมและการจัดการข้อพิพาท ซึ่งส่วนใหญ่เป็นงานด้านหน้าบ้าน นี่เป็นการเปลี่ยนแปลงที่สดชื่นจากงานเดิมของฉัน และยังทำให้ฉันได้ทำงานกับโครงสร้างพื้นฐานการออกแบบด้านหน้าบ้านของเราหลายส่วน เช่น:

  • แกนพื้นฐาน: ตัวอย่างเช่น วิธีที่เราสร้างแผงด้านข้าง, โมดอล, และป๊อปอัพของเรา
  • ส่วนประกอบที่สามารถนำกลับมาใช้ใหม่ได้และสามารถประกอบเข้าด้วยกันได้: ตัวอย่างเช่น ค้นหาแบบเลือกได้
  • ผู้จัดการโต๊ะ: คอมโพเนนต์ที่พัฒนาขึ้นเองของเราสำหรับสร้างตารางพร้อมการจัดเรียงข้อมูล การกรอง การแสดงผลเป็นหน้า และการค้นหาที่ง่ายดาย
  • การตรวจสอบความถูกต้องของแบบฟอร์มย่อยที่แข็งแกร่ง: โดยใช้ Arktype, Tanstack forms และระบบสร้างโมเดลของเราเอง

การปรับโฉมครั้งนี้ไม่ได้เกี่ยวกับตรรกะของผลิตภัณฑ์ที่ซับซ้อนมากนัก แต่เป็นเรื่องของการได้เห็นทีมของเราสามารถทำงานได้อย่างรวดเร็วเพียงใดเมื่อมีเป้าหมายเดียวกันและร่วมมือกันในโครงการเดียว การได้เห็นเว็บไซต์ทั้งหมดเปลี่ยนแปลงไปตลอดทั้งสัปดาห์ พร้อมกับได้สร้างและสร้างความสัมพันธ์กับเพื่อนร่วมงานในกระท่อมเดียวกันนั้น เป็นช่วงเวลาที่โดดเด่นที่สุดในช่วงฝึกงานของฉันอย่างแน่นอน อย่างไรก็ตาม งานปรับโฉมไม่ได้จบลงเพียงแค่อาทิตย์นั้น – ในอีกสองสามสัปดาห์ถัดมา เราได้ทำงานร่วมกับฝ่ายสนับสนุนเพื่อให้แน่ใจว่าการเปิดตัวเป็นไปอย่างราบรื่น โดยแก้ไขปัญหาเล็กๆ น้อยๆ ที่อาจเกิดขึ้นกับผลิตภัณฑ์!

โครงการ: แม่แบบ

เมื่อสิ้นสุดทุกภาคการศึกษา มหาวิทยาลัยของฉันมักจะส่งแบบฟอร์มเล็กๆ ที่มีคำถามง่ายๆ มาให้ – "หลักสูตรที่คุณเรียนมีความเกี่ยวข้องกับงานของคุณมากน้อยเพียงใด?" ในช่วงที่ฉันทำงานที่ Slash ฉันได้นำแนวคิดบางอย่างที่เรียนในชั้นเรียนมาใช้โดยตรงจริงๆ! คุณเห็นไหม เราต้องการให้อีเมลธุรกรรมของเราสอดคล้องกับการออกแบบใหม่ของเรา ดังนั้นจึงสมเหตุสมผลที่ระบบแจ้งเตือนของเราจะรองรับเทมเพลตที่ปรับแต่งได้ เพื่อให้แน่ใจว่าเราใช้สีและระยะห่างเดียวกันกับส่วนหน้าของเรา เราจึงสร้างเทมเพลตเหล่านี้ในโค้ดโดยใช้ React Email ซึ่งเป็นไลบรารีเทมเพลตอีเมลที่อนุญาตให้เราใช้ JSX สำหรับเทมเพลตเหล่านี้ได้ นี่เหมาะสมอย่างยิ่งสำหรับเรา เนื่องจากทีมของเราคุ้นเคยกับ React JSX เป็นอย่างดี เราจึงสามารถรักษาความสอดคล้องของแบรนด์ได้โดยใช้ระบบออกแบบ Tailwind CSS v4 เดียวกันทั้งบนเว็บและอีเมล

อย่างไรก็ตาม มีข้อแม้อยู่หนึ่งข้อ – หนึ่งในข้อดีหลักของ TailwindCSS 4 คือความสามารถในการใช้ไฟล์ CSS แบบกำหนดเองแทนการใช้ตัวแปรกำหนดค่าแบบ JS แบบดั้งเดิม ซึ่งทีมของเราได้ใช้ประโยชน์จากมัน อย่างไรก็ตาม ส่วนประกอบ Tailwind ของ React Email นั้น จำเป็น วัตถุการกำหนดค่า JS

นั่นหมายความว่าฉันได้สร้างตัวแปลงภาษาแล้ว!

@import "tailwindcss";

@import "../custom-styles.css";

@custom-variant dark (&:where(.dark, .dark *):not(:where(.light, .light *)));

@theme {
  --color-*: initial;

  --spacing-base-scale-0: 0px;
  --spacing-base-scale-1: 1px;
  --spacing-base-scale-2: 2px;
  --spacing-base-scale-4: 4px;
  --spacing-base-scale-6: 6px;
  --spacing-base-scale-8: 8px;
  --spacing-base-scale-12: 12px;
  --spacing-base-scale-14: 14px;
  --spacing-base-scale-16: 16px;
  --spacing-base-scale-20: 20px;
  --spacing-base-scale-24: 24px;
  --spacing-base-scale-28: 28px;
  --spacing-base-scale-32: 32px;
  --spacing-base-scale-36: 36px;
  --spacing-base-scale-40: 40px;
  --spacing-base-scale-44: 44px;
  --spacing-base-scale-48: 48px;
  --spacing-base-scale-52: 52px;
  --spacing-base-scale-56: 56px;
  --spacing-base-scale-60: 60px;
  --spacing-base-scale-64: 64px;
  --spacing-base-scale-68: 68px;
  --spacing-base-scale-72: 72px;
  --spacing-base-scale-76: 76px;
  --spacing-base-scale-80: 80px;
  --spacing-base-scale-84: 84px;
  --spacing-base-scale-88: 88px;
  --spacing-base-scale-92: 92px;
  --spacing-base-scale-96: 96px;
  --spacing-base-scale-100: 100px;
  --spacing-base-scale-104: 104px;
// WARNING: This file is generated by a script. Do not edit it manually.
// Generated from: ./lib/tailwind.build.css
// To regenerate: yarn build:tailwind-config

import type { GenericAny } from '@slashfi/slash-base';

const plugin = require('tailwindcss/plugin');

export default {
	theme: {
		extend: {
			backgroundColor: {
				"surface-subtle": "#f9f8f7",
				"surface-neutral": "#ffffff",
				"neutral-subtle-default": "#fcfcfb",
				"neutral-subtle-hover": "#f9f8f7",
				"neutral-subtle-active": "#f9f8f7",
				"neutral-subtle-disabled": "#fcfcfb",
				"neutral-normal-default": "#f9f8f7",
				"neutral-prominent-default": "#f4f3f1",
				"neutral-muted-default": "#e9e7e3",
				"neutral-bold-default": "#958f82",
				"neutral-bold-hover": "#6d685f",
				"neutral-bold-active": "#6d685f",
				"neutral-bold-disabled": "#bdb7aa",
				"neutral-muted-hover": "#e0ddd6",
				"neutral-muted-active": "#d9d6ce",
				"neutral-muted-disabled": "#efeeeb",
				"neutral-prominent-hover": "#efeeeb",
				"neutral-prominent-active": "#efeeeb",
				"neutral-prominent-disabled": "#fcfcfb",
				"neutral-normal-hover": "#f4f3f1",
				"neutral-normal-active": "#f4f3f1",
				"neutral-normal-disabled": "#fcfcfb",
				"neutral-bolder-default": "#6d685f",
				"neutral-boldest-default": "#32312c",
				"neutral-boldest-hover": "#32312c",
				"neutral-boldest-active": "#32312c",
				"neutral-boldest-disabled": "#bdb7aa",
				"neutral-bolder-hover": "#32312c",
				"neutral-bolder-active": "#32312c",
				"neutral-bolder-disabled": "#bdb7aa",
				"interactive-neutral-subtle-default": "#4b3a1c",
				"interactive-neutral-normal-default": "#4b3a1c",
				"interactive-neutral-prominent-default": "#4b3a1c",
				"interactive-neutral-prominent-hover": "#4b3a1c",
				"interactive-neutral-prominent-active": "#4b3a1c",
				"interactive-neutral-prominent-disabled": "#4b3a1c",
				"interactive-neutral-normal-hover": "#4b3a1c",
				"interactive-neutral-normal-active": "#4b3a1c",
				"interactive-neutral-normal-disabled": "#4b3a1c",
				"interactive-neutral-subtle-hover": "#4b3a1c",
				"interactive-neutral-subtle-active": "#4b3a1c",

เพื่ออธิบายให้ชัดเจนยิ่งขึ้น ในขั้นตอนการสร้าง ฉันได้เพิ่มสคริปต์เพื่อสร้าง Abstract Syntax Tree (AST) จากไฟล์ CSS ของระบบออกแบบ AST นี้ให้การแทนที่มีโครงสร้างของกฎ CSS และช่วยให้ฉันสามารถแยกวิเคราะห์ไฟล์ได้อย่างง่ายดายเพื่อ:

  • กรองคลาส CSS ที่ไม่รองรับออก: ตัวอย่างเช่น รูปแบบการเลื่อนเมาส์และเครื่องมือช่วยชี้เมาส์จะไม่ทำงานในโปรแกรมอีเมลส่วนใหญ่
  • แปลงเป็นระบบสีที่เข้ากันได้: เราใช้ OKLCH เพื่อกำหนดสีของเรา แต่ลูกค้าอีเมลไม่สามารถแสดงผลได้ ดังนั้นจึงถูกแปลงเป็น HEX
  • สร้างอ็อบเจ็กต์การกำหนดค่า TS: นี่จะถูกคอมไพล์เป็น JS และจากนั้นสามารถนำเข้าได้ง่ายทุกที่ในโค้ดเบส

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

เป็นข้อสังเกตสุดท้าย ฉันยังได้สร้างแม่แบบการแจ้งเตือนให้ใช้ประโยชน์จากกรอบการลงทะเบียนภายในของเรา ซึ่งเป็นโครงสร้างข้อมูลที่เราสร้างขึ้นเอง (ดู บทความบล็อกนี้). โดยสรุปอย่างรวดเร็ว รีจิสทรีให้วิธีที่ปลอดภัยทางประเภทในการกำหนดเทมเพลต, แปลงเป็นและแปลงกลับจากฐานข้อมูล, และเก็บตรรกะทางธุรกิจไว้ด้วยกัน ตัวอย่างเช่น, card-authorization.notification-template.tsx อยู่ติดกับโค้ดตัวจัดการเหตุการณ์การอนุมัติบัตรโดยตรง ซึ่งยอดเยี่ยมมากสำหรับการดูอย่างรวดเร็วว่ามีเหตุการณ์ใดที่มีการแจ้งเตือน! เป็นเรื่องดีมากที่ได้ใช้รีจิสทรีในโปรเจกต์นี้ – มันแสดงให้เห็นถึงความมุ่งมั่นของทีมในการเขียนโค้ดที่ดีและสามารถนำกลับมาใช้ใหม่ได้


ฉันได้อะไรกลับไปบ้าง?

ฉันเข้ามาด้วยความคิดว่านี่จะเป็นประสบการณ์ที่ไม่เหมือนใคร และฉันดีใจที่ตัดสินใจเข้าร่วม – Slash สอนฉันเกี่ยวกับความเป็นเจ้าของ การออกแบบแพลตฟอร์ม และความเร็วในการทำงานด้านวิศวกรรมมากกว่าทุกบทบาทที่ฉันเคยมีมาก่อน สิ่งสำคัญที่ฉันได้รับคือ:

  • ความไว้วางใจมีค่ามหาศาล – สแลชไม่มีการประชุมสแตนด์อัพประจำวัน สแลชไม่มีการสปรินท์รายสัปดาห์หรือการจัดเรียงงานแบ็กล็อก เรามีการประชุมสัปดาห์ละครั้ง และนั่นก็แค่เพื่อสาธิตสิ่งที่เราสร้างให้กับทั้งทีมเท่านั้น วิธีนี้ได้ผลเพราะโดยรวมแล้วทีมมีการสื่อสารที่แข็งแกร่งและมีทักษะการเป็นเจ้าของงานที่น่าประทับใจ ไม่ได้หมายความว่าไม่มีการให้คำปรึกษา – ฉันมีการพูดคุยตัวต่อตัวรายสัปดาห์เพื่อแก้ไขปัญหาของตัวเอง – แต่แทนที่จะเป็นแบบนั้น Slash ไว้วางใจให้คุณทำงานให้ดีที่สุดและสร้างสภาพแวดล้อมที่คุณสามารถทุ่มเทได้มากที่สุดเท่าที่จะเป็นไปได้
  • ทีมนี้ยอดเยี่ยมมาก – ในสหกรณ์อื่น ๆ ของฉัน เพื่อนร่วมงานของฉันมีความเป็นมืออาชีพ แต่ที่นี่ เพื่อนร่วมงานของฉันคือ คนของฉัน. เห็นได้ชัดว่า Slash มีวัฒนธรรมองค์กรที่ยอดเยี่ยม เต็มไปด้วยความสนุก การสนับสนุน และคนที่ทำงานด้วยง่าย – ไม่มีใครเลยที่ผมไม่ชอบทำงานด้วยหรือรู้สึกเกรงกลัว และทุกคนปฏิบัติกับคุณเหมือนเป็นพนักงานประจำเต็มตัว นอกจากนี้ คุณยังสามารถไปเล่นบาสกับพวกเขาในวันหยุดสุดสัปดาห์ได้ด้วยนะ 😃
  • ในฐานะนักศึกษาฝึกงาน คุณจะเติบโตอย่างก้าวกระโดดที่นี่เมื่อเทียบกับที่อื่นใด – ที่บริษัทอื่น ๆ นักศึกษาฝึกงานคงไม่ได้รับมอบหมายให้ดูแลระบบแจ้งเตือนหลักทั้งหมดเป็นโปรเจกต์เดียว ปัญหาที่ยากและช่วยพัฒนาตัวเองได้มากที่สุดก็มักจะถูกแก้ไขโดยวิศวกรอาวุโสไปแล้ว หรือไม่ก็กำลังอยู่ในกระบวนการแก้ไข สิ่งที่ทำให้ Slash แตกต่างคือ การผสมผสานระหว่างทีมที่กระชับ ปัญหาสำคัญที่ส่งผลต่อธุรกิจจริง และวัฒนธรรมที่ไว้วางใจให้แม้แต่พนักงานฝึกงานได้รับมอบหมายงานสำคัญ

นี่คือความทรงจำสนุกๆ อื่นๆ จากช่วงฝึกงานนี้:

  • พวกเราทานสเต็กรวมกันประมาณ 20 ปอนด์ในหนึ่งสัปดาห์... ขอบคุณสัปดาห์แฮ็ก!
  • ฉันเอาชนะเพื่อนฝึกงานของฉันในการแข่งขันว่ายน้ำอย่างขาดลอย แล้วหลังจากนั้นก็ถูกพนักงานประจำสอนบทเรียนทันที
  • ในที่สุดฉันก็ยกน้ำหนักได้หนึ่งแผ่นบนม้านั่งแล้ว!
  • ฉันเป็นผู้มีส่วนร่วมหลักในการกินอย่างตะกละทั่วทั้งสำนักงาน (และอาจเกี่ยวข้องกัน ฉันเพิ่งค้นพบว่าฉันชอบ Shake Shack)

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

หากคุณสนใจที่จะพูดคุยเพิ่มเติม อย่าลังเลที่จะติดต่อฉันทาง LinkedIn! ยินดีตอบทุกคำถามที่คุณอาจมี

Read more from us