Quản lý tính năng (Feature Flags) với Laravel Pennant
Trong môi trường phát triển hiện đại, việc deploy code lên production thường xuyên (CI/CD) là tiêu chuẩn. Nhưng làm sao để merge code tính năng mới lên nhánh chính (main) mà không làm ảnh hưởng người dùng khi tính năng đó chưa hoàn thiện? Hoặc làm sao để chỉ bật tính năng mới cho đội ngũ Beta Tester?
Câu trả lời là Feature Flags (cờ tính năng).
Laravel Pennant là một first-party package (gói chính chủ) giúp quản lý Feature Flags cực kỳ đơn giản, nhẹ nhàng và chuẩn bài Laravel.
Tại sao cần Feature Flags?
- Trunk-based Development: Developer merge code hàng ngày, ẩn code chưa xong sau một cái cờ
FALSE. - Canary Releases: Bật tính năng mới cho 1% user để theo dõi lỗi trước khi bung cho 100%.
- A/B Testing: Hiển thị giao diện A cho nhóm này, B cho nhóm kia để đo lường hiệu quả.
- Kill Switch: Tắt ngay lập tức một tính năng bị lỗi mà không cần rollback code.
Cài đặt Pennant
composer require laravel/pennant
php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider"
php artisan migrate
Mặc định, Pennant lưu flags trong database. Nó cũng hỗ trợ redis hoặc array (cho testing).
Định nghĩa Feature
Thông thường, bạn định nghĩa các tính năng trong AppServiceProvider:
use Laravel\Pennant\Feature;
use App\Models\User;
public function boot(): void
{
// Flag đơn giản: bật hoặc tắt cho tất cả
Feature::define('new-dashboard', fn (User $user) => match ($user->role) {
'admin', 'beta-tester' => true,
default => false,
});
// Lottery: Ngẫu nhiên cho 10% user
Feature::define('site-redesign', fn (User $user) => \Laravel\Pennant\Feature::lottery(1 in: 10));
}
Các tính năng này cũng có thể được định nghĩa bằng Class riêng nếu logic phức tạp.
Sử dụng trong Code
Trong Blade View
Helper @feature giúp code view cực sạch:
@feature('new-dashboard')
<!-- Giao diện mới -->
<x-dashboard-v2 />
@else
<!-- Giao diện cũ -->
<x-dashboard-v1 />
@endfeature
Trong Controller
public function index()
{
if (Feature::active('new-dashboard')) {
return view('dashboard.v2');
}
return view('dashboard.v1');
}
Middleware
Pennant cung cấp middleware để bảo vệ route:
Route::get('/analytics', AnalyticsController::class)
->middleware('features:view-analytics');
Class Based Features
Với các tính năng lớn, nên dùng Class để quản lý state tốt hơn.
php artisan pennant:feature NewBillingSystem
Class này sẽ có phương thức resolve. Giá trị trả về không nhất thiết là boolean. Nó có thể là string, number, hoặc object (Rich Feature).
// app/Features/NewBillingSystem.php
public function resolve(User $user)
{
return $user->isPremium() ? 'stripe' : 'paypal';
}
Cách dùng:
$gateway = Feature::value(NewBillingSystem::class); // trả về 'stripe' hoặc 'paypal'
Testing với Pennant
Một điểm mạnh của gói chính chủ là hỗ trợ test tận răng.
public function test_it_shows_new_dashboard_when_enabled()
{
Feature::activate('new-dashboard');
$response = $this->get('/dashboard');
$response->assertSee('Welcome to Dashboard V2');
}
Chiến lược Rollout
Pennant cực kỳ mạnh mẽ khi kết hợp với logic nghiệp vụ.
Ví dụ, bạn muốn bật tính năng AI Chat cho:
- Admin nội bộ.
- Khách hàng gói "Enterprise".
- Các user cũ đăng ký trước 2024.
Thay vì viết đống if/else rải rác khắp nơi, bạn gom nó vào một chỗ duy nhất trong Feature::define. Code base của bạn sạch sẽ, dễ bảo trì, và quan trọng nhất là bạn có thể bật/tắt toàn bộ logic đó chỉ bằng một dòng code mà không cần deploy lại (nếu dùng Driver database và có UI quản lý flags).
Tổng kết
Laravel Pennant biến việc quản lý Feature Flags từ một công việc thủ công, dễ lỗi thành một quy trình chuẩn hóa. Nếu bạn đang làm product có người dùng thật, hãy cân nhắc áp dụng Pennant ngay để quy trình release trở nên tự tin hơn.