Stop Passing Booleans As Arguments
If you've ever seen a method call like this:
1$order->cancel(silent: true);
You might pause and ask: what does silent actually mean here? Now imagine the caller doesn't use named arguments:
1$order->cancel(true);
That true tells you nothing. You have to dig into the method signature to understand what's happening. And when a second boolean flag inevitably gets added, things get worse:
1$order->cancel(true, false);
Before long, someone adds a reason and a notification override, and now you're staring at this:
1$order->cancel(true, false, 'customer_request', true, null);
Good luck figuring out what any of that means without reading the method signature.
Boolean flags hide intent. They're a code smell that signals your method is doing two different things based on a toggle, and that toggle is invisible at the call site.
The Before
Here's a controller that cancels an order. It accepts a bool $silent flag to optionally skip sending a notification to the customer:
1class OrderController extends Controller 2{ 3 public function cancel(Order $order, bool $silent = false) 4 { 5 $order->markAsCancelled(silent: $silent); 6 7 return redirect()->route('orders.index') 8 ->with('success', 'Order cancelled.'); 9 }10}
And the event listener that handles post-cancellation logic:
1class SendOrderCancellationNotification 2{ 3 public function handle(OrderCancelled $event): void 4 { 5 if (! $event->silent) { 6 $event->order->customer->notify( 7 new OrderCancelledNotification($event->order) 8 ); 9 }10 }11}
This works. But $event->silent is a boolean doing heavy lifting. It controls whether the customer gets notified. What happens when you need a third mode, say you want to cancel and issue a refund? Do you add another boolean? $order->markAsCancelled(silent: false, refund: true)?
That's a path you don't want to go down.
The Enum
Instead, replace the boolean with an enum that names the behavior:
1enum CancellationMode: string2{3 case Default = 'default';4 case Silent = 'silent';5}
Now the intent is explicit. CancellationMode::Silent says exactly what it does. And when you need that third mode six months from now, you add a case instead of a boolean.
The After
The controller now accepts a string and resolves it to the enum:
1class OrderController extends Controller 2{ 3 public function cancel(Order $order, string $mode = 'default') 4 { 5 $mode = CancellationMode::tryFrom($mode) ?? CancellationMode::Default; 6 7 $order->markAsCancelled(mode: $mode); 8 9 return redirect()->route('orders.index')10 ->with('success', 'Order cancelled.');11 }12}
The event now carries a typed mode instead of a bare boolean:
1class OrderCancelled 2{ 3 use Dispatchable; 4 use SerializesModels; 5 6 public function __construct( 7 public Order $order, 8 public CancellationMode $mode = CancellationMode::Default, 9 ) {}10}
And the listener reads like plain English:
1class SendOrderCancellationNotification 2{ 3 public function handle(OrderCancelled $event): void 4 { 5 if ($event->mode !== CancellationMode::Silent) { 6 $event->order->customer->notify( 7 new OrderCancelledNotification($event->order) 8 ); 9 }10 }11}
$event->mode !== CancellationMode::Silent is immediately understandable. No need to parse what ! $event->silent means in context.
Why Not Always an Enum?
Enums aren't always the answer. If the behavior you're toggling is more like a set of independent options rather than a single mode, a fluent builder can be a better fit:
1$order->cancel()2 ->notifyVia('email')3 ->withRefund()4 ->reason('Customer requested');
Each method call is self-documenting and the options are independent of each other. You'd never try to cram all of that into a single enum. Enums work best when you're picking one mode from a set of mutually exclusive behaviors. Fluent builders work best when you're composing multiple independent options together.
Why This Matters
Most of the time though, an enum is exactly what I reach for.
The call site actually reads. markAsCancelled(mode: CancellationMode::Silent) tells you everything. markAsCancelled(silent: true) only makes sense if you already know the parameter name, and without named arguments you're just guessing.
New modes are trivial. Need a NotifyViaEmail case down the road? Add one line to the enum.
You can actually search for them. CancellationMode::Silent across your codebase finds every silent cancellation.
Next time you reach for a bool $flag parameter, consider whether it should be an enum or a fluent method instead. Either way, stop passing booleans.
Cheers