Hydrating an enum from a request parameter with from() produced an uncaught ValueError on the first malformed input, which was about nine minutes after deploy.
OrderStatus::from('shipped'); // OrderStatus::Shipped
OrderStatus::from('shiped'); // ValueError, uncaught
OrderStatus::tryFrom('shiped'); // null
// so: from() at a trusted boundary, tryFrom() at an
// untrusted one — and the null has to be handled
$status = OrderStatus::tryFrom($request->input('status'))
?? throw new InvalidArgumentException('unknown status');
The two methods exist because the right behaviour genuinely differs by caller: a value read from your own database should throw, because a value there that is not a case means the data is corrupt; a value from an HTTP request should not, because a client sending nonsense is normal. Using from() everywhere turns a 422 into a 500, and using tryFrom() everywhere silently converts corruption into a null that travels further before failing.