บทนำ
การอัปเดตเป็นส่วนหนึ่งของซอฟต์แวร์ทุกตัว ต้องแก้บั๊ก เพิ่มฟีเจอร์ใหม่ และปรับปรุงโดยรวมเพื่อให้ปลอดภัย น่าเชื่อถือ หรือเร็วขึ้น
แต่การผูกมัดฟีเจอร์ใหม่หรือการเปลี่ยนแปลงใดๆ กับการปรับใช้ (deployment) ไม่ใช่ความคิดที่ดีเสมอไป บางทีมปฏิบัติตามตารางการปรับใช้ที่ซ้ำซ้อน แต่อาจไม่พร้อมสำหรับการประกาศฟีเจอร์ใหม่ หากการเปลี่ยนแปลงถูก hardcode ทีมการตลาดอาจต้องขอนักพัฒนามาเปิด/ปิดฟีเจอร์ให้
Feature flags (หรือ feature toggles) เป็นวิธีแยกการเปลี่ยนแปลงออกจากการปรับใช้ คุณนำการเปลี่ยนแปลงไปใช้ แต่ซ่อนไว้เบื้องหลัง if-statement: ตราบใดที่ตัวแปรงั้นเป็น false ซอฟต์แวร์จะใช้เวอร์ชันก่อนการเปลี่ยนแปลง หากตัวแปรเป็น true จะเป็นเวอร์ชันใหม่ หากค่าตัวแปรมาจากฐานข้อมูลที่ทีมการตลาดเข้าถึงได้ พวกเขาสามารถสลับการเปลี่ยนแปลงเหล่านี้ได้โดยไม่ต้องแก้โค้ด
Upstash Redis® เป็นฐานข้อมูลแบบนี้ มันเป็น key-value store เรียบง่ายที่ใช้เก็บ feature flags ได้ เนื่องจากมันเร็วมาก จึงไม่เพิ่มเลย์เทนซีมากนักเมื่อตรวจสอบหลาย flags ในแอปพลิเคชัน
บทช่วยสอนนี้จะสอนวิธีสร้างกลไก feature flag เรียบง่ายสำหรับแอป Next.js เราจะปรับใช้บน Vercel และใช้ Upstash Redis® เป็นที่จัดเก็บ
ข้อกำหนดเบื้องต้น
คุณต้องมีบัญชี GitHub, Vercel และ Upstash
เนื่องจากเราใช้ NPM จึงต้องมี Node.js ติดตั้งอยู่แล้ว
เริ่มต้นโปรเจกต์ Next.js
ขั้นตอนแรกคือสร้างโปรเจกต์ Next.js ใหม่ ด้วยคำสั่ง:
$ npx create-next-app upstash-next-feature-flags
สร้างการเชื่อมต่อ Upstash Redis®
ต่อไปเราต้องเชื่อมแอป Next.js กับฐานข้อมูล Upstash Redis® ติดตั้งแพ็กเกจ @upstash/redis และสร้าง wrapper เล็กๆ
$ npm i @upstash/redis
สร้างไฟล์ที่ upstash-next-feature-flags/lib/featureFlags.js ด้วยโค้ดนี้:
import { Redis } from "@upstash/redis";
const { UPSTASH_TOKEN, UPSTASH_URL } = process.env;
const redis = new Redis({ token: UPSTASH_TOKEN, url: UPSTASH_URL });
export async function flagIsActive(flagName) {
const flag = await redis.get(flagName);
return Boolean(flag);
}
เราจะดึงข้อมูลประจำตัว API จากตัวแปรสภาพแวดล้อมเพื่อตั้งค่าภายหลัง ฟังก์ชัน flagIsActive จะสืบค้น Redis® และแปลงค่าที่ได้เป็น boolean การแปลงนี้จำเป็นเพราะเราจะเก็บ flag เป็นตัวเลขใน Redis
สร้าง API Route
เราต้องสร้าง API route พร้อมสองการนำไปใช้งานที่แตกต่างกัน ใช้ feature flag สลับระหว่างกัน
สร้างไฟล์ที่ upstash-next-feature-flags/pages/api/sort-numbers.js ด้วยโค้ด:
import { flagIsActive } from "../../lib/featureFlags";
export default async function handler(request, response) {
let sort = bucketSort;
const newSortingAlgorithm = await flagIsActive("newSortingAlgorithm");
if (newSortingAlgorithm) sort = selectionSort;
const { numbers } = request.body;
const sorted = sort(numbers);
response.status(200).json({ numbers: sorted, newSortingAlgorithm });
}
// อัลกอริทึมจัดเรียงเก่า
const bucketSort = (arr, size = 5) => {
const min = Math.min(...arr);
const max = Math.max(...arr);
const buckets = Array.from(
{ length: Math.floor((max - min) / size) + 1 },
() => [],
);
arr.forEach((val) => {
buckets[Math.floor((val - min) / size)].push(val);
});
return buckets.reduce((acc, b) => [...acc, ...b.sort((a, b) => a - b)], []);
};
// อัลกอริทึมจัดเรียงใหม่
const selectionSort = (arr) => {
const a = [...arr];
for (let i = 0; i < a.length; i++) {
const min = a
.slice(i + 1)
.reduce((acc, val, j) => (val < a[acc] ? j + i + 1 : acc), i);
if (min !== i) [a[i], a[min]] = [a[min], a[i]];
}
return a;
};
API route นี้เป็นตัวจัดเรียงตัวเลขง่ายๆ รับ array ตัวเลขที่ยังไม่เรียง และใช้หนึ่งในสองอัลกอริทึมจัดเรียง
อัลกอริทึมทั้งสองแทนการเปลี่ยนแปลงใดๆ ที่คุณต้องการ "สลับ" ในแอปพลิเคชัน ใส่ทั้งสองลงในโค้ดและสลับกันผ่านฟังก์ชัน flagIsActive โดยเปลี่ยนค่าใน Redis
เราเรียก flagIsActive ใส่ชื่อ flag ที่ต้องการตรวจสอบ หาก flag ตั้งเป็น 1 ใน Redis ฟังก์ชันคืนค่า true มิฉะนั้นคืน false
หลังจัดเรียงเสร็จ API route ตอบกลับพร้อม array ที่เรียงแล้ว
ส่งโปรเจกต์ขึ้น GitHub
หลังนำทุกอย่างไปใช้งาน ต้อง push โปรเจกต์ขึ้น repository บน GitHub เพื่อให้บริการปรับใช้ของ Vercel เข้าถึงได้ GitHub มีเอกสารสอนสร้าง repository
การสร้าง repository ว่างจะแสดงวิธีเชื่อมโยงกับโปรเจกต์ที่สร้างไว้
สร้างฐานข้อมูล Upstash Redis®
สร้างฐานข้อมูลใหม่ในคอนโซล Upstash รูปที่ 1 แสดงการตั้งค่าที่เหมาะสมสำหรับบทช่วยสอนนี้

ตามรูปที่ 1 ฐานข้อมูลหนึ่งอันฟรี จึงไม่ต้องจ่ายเงินสำหรับบทช่วยสอนนี้
เก็บแท็บคอนโซล Upstash ไว้เปิด เพราะต้องใช้เก็บข้อมูลประจำตัวฐานข้อมูลภายหลัง
เชื่อม GitHub Repository กับ Vercel
หากล็อกอิน Vercel ด้วยบัญชี GitHub การเพิ่ม GitHub repository ใช้ไม่กี่คลิก ในรูปที่ 2 มุมซ้ายบนของแดชบอร์ด Vercel คลิก "Add New…" เลือก "Project"

เลือก "Continue with GitHub" และปุ่ม "import" หนึ่งปุ่มที่ตรงกับ repository ที่สร้างในขั้นตอนก่อน

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

ตัวแปรชื่อ UPSTASH_TOKEN และ UPSTASH_URL หาได้ในคอนโซล Upstash ที่สร้างฐานข้อมูลไว้ก่อนหน้า รูปที่ 5 แสดงปุ่มที่ต้องกดเพื่อคัดลอกค่าที่ต้องการ

กดปุ่ม "Deploy" ใหญ่ Vercel จะโคลน GitHub repository และปรับใช้บนโครงสร้างพื้นฐานของพวกเขา
สลับการนำไปใช้งานด้วย Redis
ตอนนี้ควรเรียก API endpoint ได้แล้ว และสลับระหว่างการนำไปใช้งานได้โดยตั้งค่าใน Redis Redis CLI ในคอนโซล Upstash เป็นวิธีง่ายที่สุด
รูปที่ 6 แสดงที่หา CLI และคำสั่งที่ต้องรันเพื่อตั้งค่า
set newSortingAlgorithm 1

หากลบ newSortingAlgorithm หรือตั้งเป็น 0 จะสลับ flag กลับเป็น false และ API route จะใช้อัลกอริทึมเก่า
การใช้งาน API Route
สิ่งที่ขาดคือการใช้งาน API route จริง ต้องส่ง JSON object ผ่าน POST request ไปที่ route วิธีง่ายคือใช้ cURL
คำสั่งต่อไปนี้ส่ง object ดังกล่าว:
$ curl --header "Content-Type: application/json" \
--request POST \
--data '{"numbers":[1, 100, 10, 1000, 100000, 10000]}' \
API_ROUTE_URL
ต้องหา API_ROUTE_URL ที่ถูกต้องใน Vercel ควรเห็นหมวด "Domains" ในโปรเจกต์บนแดชบอร์ด Vercel ตามรูปที่ 7

คำตอบควรเป็น JSON object ที่มี numbers ที่เรียงแล้ว และฟิลด์ newSortingAlgorithm บ่งบอกว่าอัลกอริทึมไหนถูกใช้
สรุป
Feature flags เป็นวิธียอดเยี่ยมในการแยกการปรับใช้ออกจากการปล่อยฟีเจอร์ พวกมันเปลี่ยนการเปลี่ยนแปลงที่ hardcode ไว้ ให้กลายเป็นการตั้งค่าเพียงค่าเดียวในฐานข้อมูล ให้ความยืดหยุ่นมากขึ้นกับทีม และให้อำนาจมากขึ้นกับคนฝ่ายไม่ใช่เทคนิค
ด้วยฐานข้อมูลเร็วอย่าง Upstash Redis การนำรูปแบบ feature flag ไปใช้ในแอปพลิเคชันง่ายขึ้น โดยไม่ต้องเสียประสิทธิภาพมากนัก Upstash Redis เข้าถึงได้ใน sub-second และสามารถปรับใช้ใกล้ FaaS ได้ หากต้องการ ทั้งนี้ด้วยราคาแบบจ่ายตามความต้องการ จึงไม่ต้องจ่ายสำหรับทรัพยากรที่ไม่ได้ใช้