Added post: Why I disallow Laravel Facade usages - #129
Conversation
| Following is a piece of code that uses a Laravel Facade to store some data in cache: | ||
| </p> | ||
|
|
||
| @TODO change this example to use a different facade: something that sends an email notification would be nice |
There was a problem hiding this comment.
Thinking PaymentProcessor, Notification, or perhaps even EventBus
| ~~~ | ||
|
|
||
| <p> | ||
| @TODO we need a stronger example here: what's a good "whoops" example, like a checkout on the wrong banking coordinates? |
There was a problem hiding this comment.
I think we need an example where the application configuration changes in-between, or where two applications are bootstrapped, yet a single facade is being re-used.
This is relevant also for long-running processes, so perhaps a foreach () { could help too
| <p> | ||
| @TODO expose how this problem occurs especially when: | ||
| * an application grows in size, accommodating for multiple service instances | ||
| * tests: multiple services needed, one per test! |
There was a problem hiding this comment.
We should add an example with two tests, possibly
| <h2>Problem 3: hidden complexity</h3> | ||
|
|
||
| <p> | ||
| @TODO expose here how adding a facade introduces magic method calls, stack frames, static analysis complexity. |
There was a problem hiding this comment.
A screenshot of a facade stack trace could help here too.
Also worth mentioning the static analysis effort: need to run a facade with a small phpstan example
| </p> | ||
|
|
||
| <p> | ||
| @TODO expose concept of "simple" != "easy". Systems can be complex and easy, or harder to use, but simple. |
There was a problem hiding this comment.
Need a good resource for simple != easy
|
|
||
|
|
||
| <p> | ||
| By adding 2 lines of code, we: |
| <h2>But what about my test helpers?</h2> | ||
|
|
||
| <p> | ||
| @TODO Laravel has MyFacade::spy() and MyFacade::mock() helpers - let's document those |
There was a problem hiding this comment.
The example below kinda shows this: I need to check the example in a real test case though
| <h2>Disallowing facades</h2> | ||
|
|
||
| <p> | ||
| From my point of view, Facades are technical debt, and of a particularly bad and sneaky kind. |
| </p> | ||
|
|
||
| <p> | ||
| There is no reason to skip the extra legwork to keep a system simple: systems increase complexity |
There was a problem hiding this comment.
"the extra legwork" is redundant
| final class MyServiceTest extends \PHPUnit\Framework\TestCase { | ||
| function test_something_is_being_done(): void | ||
| { | ||
| $cache = $this->createMock(Cache::class); |
There was a problem hiding this comment.
Not sure if teaching Mocks is the best thing to do? I'd ask if a Spy would be smarter to use here?
mintopia
left a comment
There was a problem hiding this comment.
Just noticed a few typos in the example non-mocking cache example test.
| final class CacheSpy implements Cache | ||
| { | ||
| /** @var array<string, mixed> */ | ||
| private array $recorded = []; |
|
|
||
| // look! No testing framework either! | ||
| function my_test() { | ||
| $cache = new CacheSpy() |
| month: 07 | ||
| day: 18 | ||
| published: true | ||
| summary: "Laravel Facades: why I disallow their usage in my software project" |
There was a problem hiding this comment.
"Disallow" is strong (maybe true, but...) - consider "Why I Avoid Laravel Facades" or "The Hidden Costs of Laravel Facades" for broader appeal
There was a problem hiding this comment.
It's supposed to be strong: it's my opinion, after all :P
| In this case, we wanted a cache, not the entire framework. | ||
| </p> | ||
|
|
||
| <h2>Problem 3: hidden complexity</h3> |
There was a problem hiding this comment.
| <h2>Problem 3: hidden complexity</h3> | |
| <h2>Problem 3: hidden complexity</h2> |
| <?php | ||
|
|
||
| final class MyService implements SomeContract { | ||
| public function __construct(private readonly SomeCache) {} |
There was a problem hiding this comment.
| public function __construct(private readonly SomeCache) {} | |
| public function __construct(private readonly SomeCache $cache) {} |
There was a problem hiding this comment.
As noted on Discord, I should also mention that this is "picked up by autowiring" by default
| ~~~ | ||
|
|
||
| <p> | ||
| The above is obviously brought to extremes, but it highlights the added degrees of fredom that are introduced. |
There was a problem hiding this comment.
| The above is obviously brought to extremes, but it highlights the added degrees of fredom that are introduced. | |
| The above is obviously brought to extremes, but it highlights the added degrees of freedom that are introduced. |
| </p> | ||
|
|
||
| <p> | ||
| In addition to the disadvantages of service-location, you also have the hidden dependency of the facade's |
There was a problem hiding this comment.
| In addition to the disadvantages of service-location, you also have the hidden dependency of the facade's | |
| In addition to the disadvantages of service location, you also have the hidden dependency of the facade's |
as above.
|
Worth noting: https://github.com/JoeyMckenzie/facadeless New one, hope it does what it says 🤞 |
…true when using facades
| @@ -0,0 +1,338 @@ | |||
| --- | |||
| layout: post | |||
| title: Why I disallow Laravel Facade usages | |||
There was a problem hiding this comment.
I discussed this on Discord, and perhaps "positive reinforcement" is indeed better here.
I really dislike the idea, but it may have a better effect on the community this targets.
There was a problem hiding this comment.
- Facades have a better alternative: Autowiring
- Prefer Autowiring over Facades
- Disallow Facades and enjoy Autowiring
Associate the explanation of the issue with a clear solution from the start, and the mind of the junior dev immediately tags Facades as old and bad, and Autowiring as the future and good
No description provided.