Skip to content

Added post: Why I disallow Laravel Facade usages - #129

Draft
Ocramius wants to merge 3 commits into
sourcefrom
blogpost/why-i-disallow-facades
Draft

Added post: Why I disallow Laravel Facade usages#129
Ocramius wants to merge 3 commits into
sourcefrom
blogpost/why-i-disallow-facades

Conversation

@Ocramius

Copy link
Copy Markdown
Owner

No description provided.

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

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a good resource for simple != easy



<p>
By adding 2 lines of code, we:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 line of code

<h2>But what about my test helpers?</h2>

<p>
@TODO Laravel has MyFacade::spy() and MyFacade::mock() helpers - let's document those

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to reword this

</p>

<p>
There is no reason to skip the extra legwork to keep a system simple: systems increase complexity

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the extra legwork" is redundant

final class MyServiceTest extends \PHPUnit\Framework\TestCase {
function test_something_is_being_done(): void
{
$cache = $this->createMock(Cache::class);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if teaching Mocks is the best thing to do? I'd ask if a Spy would be smarter to use here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed the next part -_-

@mintopia mintopia left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need to be public


// look! No testing framework either!
function my_test() {
$cache = new CacheSpy()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing ;

month: 07
day: 18
published: true
summary: "Laravel Facades: why I disallow their usage in my software project"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Disallow" is strong (maybe true, but...) - consider "Why I Avoid Laravel Facades" or "The Hidden Costs of Laravel Facades" for broader appeal

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<h2>Problem 3: hidden complexity</h3>
<h2>Problem 3: hidden complexity</h2>

<?php

final class MyService implements SomeContract {
public function __construct(private readonly SomeCache) {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function __construct(private readonly SomeCache) {}
public function __construct(private readonly SomeCache $cache) {}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

@Slamdunk

Copy link
Copy Markdown
Contributor

Worth noting: https://github.com/JoeyMckenzie/facadeless

New one, hope it does what it says 🤞

@@ -0,0 +1,338 @@
---
layout: post
title: Why I disallow Laravel Facade usages

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants