Paul M. Jones

Don't listen to the crowd, they say "jump."

AutoRoute 2.0.0 Released, With Value Object Support

I am proud to announce that I have released AutoRoute 2.0.0, now for PHP 8. As an alternative to regex and annotation-based routers, AutoRoute eliminates the need for route definitions by automatically mapping the HTTP action class hierarchy to the HTTP method verb and URL path, reflecting on typehinted action method parameters to determine the dynamic portions of the URL. It presumes that the action class names conform to a well-defined convention, and that the action method parameters indicate the dynamic portions of the URL. This makes the implementation both flexible and relatively maintenance-free.

Of special importance, you can now use value objects as action parameters, and AutoRoute will automatically instantiate them for you. This means things like self-validating value objects, including DDD Identity value objects, are now trivial to work with in your action classes.

For example, whereas you might have done something like this previously ...

// GET /company/1
class GetCompany
{
    public function __invoke(int $id)
    {
        $companyId = new CompanyId($id);
        $company = $this->serviceLayer->fetchCompany($companyId);
        // ...
    }
}

... AutoRoute can now instantiate the CompanyId for you from the appropriate path segment:

// GET /company/1
class GetCompany
{
    public function __invoke(CompanyId $companyId)
    {
        $company = $this->serviceLayer->fetchCompany($companyId);
        // ...
    }
}

AutoRoute 2.0.0 is still faster than FastRoute, though not by as much as the 1.x series; it has dropped from about three times as fast to about two times as fast. (This is due to some internal rework to support value objects as action arguments.) Even so, remember that routing is only one small part of your HTTP user interface, and is unlikely to be a bottleneck in any case.


AutoRoute is 3x Faster Than FastRoute

When I first published AutoRoute in April 2019, I benchmarked it out of habit, to see how it compared to the fastest routing system I knew, FastRoute. The benchmark results at that time typically looked like this on PHP 7.2:

AutoRoute Runtime: 0.11443305015564
FastRoute Runtime: 1.9010598659515
FastRoute Cached:  0.12522411346436

It turned out that AutoRoute was very slightly faster than FastRoute; I was pleased, but surprised. Getting anywhere close to FastRoute was enough for me.

Lately, though, I've been working on the 2.x version of AutoRoute, and I compared 2.x to the 1.x benchmark. I was very unhappy with the results: 2.x was something like 6x slower than 1.x. While investigating the causes, I noticed that I had XDebug turned on. Turning it off brought the comparative benchmark to something much more reasonable.

It turns out that I made the same oversight when benchmarking 1.x against FastRoute, so I have corrected my error and updated the 1.x AutoRoute benchmarks. I am even more surprised now: the scenario reports that AutoRoute is about 3x faster than FastRoute:

AutoRoute Runtime: 0.027965068817139
FastRoute Runtime: 0.21157002449036
FastRoute Cached:  0.10321187973022

Now, remember: routing is only a tiny part of your overall application performance. Using AutoRoute over FastRoute will not triple the speed of your applications. Still, it is satisfying to find better performance where you can.


Why Each Programmer Thinks He Is The Best

I write code that is easy to read and uses intuitive data structures. Everyone else uses weird solutions and writes them poorly.

My innovations are clever responses to unusual circumstances. Everyone else’s are obviously weird.

Every time I have to fix someone’s code, their code is terrible. I never hear about my own code being terrible and in need of fixing. Every time I need to touch my own code again it is because it is so good my customers want to make another use of it.

If someone uses my software it is because it is good code. If they use competing software it is because of their clever marketing.

If I copy code from Stack Exchange I’m being efficient. If someone else does it they couldn’t do it themselves.

If I use an obscure language or library, I’m leveraging my unusual skillset. If someone else does it, they’re prioritizing their own convenience over maintainability.

If someone like John Carmack is just better than me at programming in every way it is because they are in a special heroic reference class that it would be unfair to compare me to but that I obviously aspire to.

Lifted entirely from https://slatestarcodex.com/2020/01/02/why-doctors-think-theyre-the-best/#comment-836896 with light editing and reformatting. (You should read the parent article as well.)

See also The Devil's Dictionary for Developers.


DDD, Authentication, and Credential Exchange

In a typical DDD layered architecture, the Application layer will almost always need to know who is using it, even if only to help determine authorization. This is not a problem of authentication per se. Instead, it is a problem of taking the identification values provided by a prior authentication (such as a JWT or a session ID) and matching them to a Domain layer User object. Further, the problem must be solved in a way that does not tie the Application or Domain layers to any particular Presentation (User Interface) layer.

The Credential Exchange technique identifies the Domain layer User by passing the identifying information from the Presentation (User Interface) layer into the Application layer, and letting the Application layer coordinate the creation of the Domain layer User instance via Infrastructure implementations.

This technique eliminates any Presentation (User Interface) layer logic related to discovering the Domain layer User; for example, there is no need for error handling and other conditionals. The Application layer can capture any errors from Domain layer User identification into a Domain Payload for return back to the Presentation layer. The Domain layer can continue to depend only on its own interfaces for User modeling, independent of any other layer.

The components and collaborations in this technique are essentially a specialized variation on the Query Bus pattern, with the Credential as a Query, the CredentialExchange as a QueryBus, and the CredentialHandler as a QueryHandler.

You can read more about the Credential Exchange technique here, including source code examples.


Why Rationality Fails

Specifically, Bayesian rationality:

Rationality is a real thing. It says, “Use these formulas, from math and logic, in this way without error.” That’s fine and sure advice. But — here’s the big but — rationality is silent on what goes into the formulas. What goes in is what’s it’s all about.

There is no way to bootstrap rationality. All thought has to begin in inspiration, sometimes called intuition, of which there are many kinds, none of them rational.

That is, all rational processes begin at a non-rational starting point: a world-view, if you will, that filters which facts are relevant to the rational calculation. Different world-views accept different (and equally true!) facts as relevant, and so can arrive at widely different conclusions -- all completely rational.

Via Why Moldbug/Yarvin/Alexander’s Bayesian Rationality Fails.



Application and Domain Service Naming Conventions

In which a correspondent asks about Application Services, Domain Services, and the interactions between them.

I.

The word "service" is everywhere in my codebase. Can you suggest a naming convention, or directory structure, or other advice, regarding Application Services?

I hear you on the word Service popping up everywhere. Using the same word everywhere for different things makes it hard to differentiate between them.

Having that trouble myself, I have started using the term "Use Case" instead of "Application Service". Further, I do only one thing per Use Case, instead of doing several things in an Application Service.

I have a directory structure something like this in one project:

src/
    Domain/
        ...
    UseCase/
        Draft/
            AddDraft.php
            FetchDraft.php
            PublishDraft.php
            SaveDraft.php
            TrashDraft.php
        Post/
            FetchPost.php
            FetchPosts.php
            SavePost.php
            TrashPost.php

Note that the naming is "human" and does not have "UseCase" suffixed on it. (More on that in a bit.)

II.

I'm struggling to know exactly what goes in the blog Application Service and blog non-application service. Where does user authorization ("can this user create a blog?") go? Where does validation ("is the title long enough? is the publish date in the future?") go? Are those application or non-application services?

This is where the idea of a Domain Service (cf. the DDD books by Vernon and Evans) comes into play. They say those non-application services are called Domain Services.

If a non-application service is injected into other non-application services, should all this be moved out of the Application Service? If so, the Application Service don't really then contain anything as far as I can see.

Whereas an Application Service presents a face to the world outside the Domain, the Domain Service is internal to the Domain. As such, it's totally fine to have that non-application Domain Service get injected into different Application Services (and for Domain Services to be used by other Domain Services).

And yes, that means eventually the Application Service may then contain almost nothing at all except calls to coordinate different Domain Services. I think this is part of a natural progression:

  • You used to put everything in a Controller method or Action class; then you extracted the domain logic into an Application Service.

  • Next, different Application Services needed to do the same things over and over; you extracted the shared logic to different Domain Service classes.

  • Finally, you realized that your Application Service could just use one or more Domain Service objects in a lot of cases.

Voila: all non-trivial logic is down in the Domain now. The Application Service coordinates between one or more of those Domain Services, then presents a Domain Payload back to the user interface.

And how would I name those non-application services?

I have started keeping my Domain Services grouped with their "main" entity or aggregate. For example:

Domain/
    Content/
        Draft/
            Draft.php
            DraftRepository.php
            PublishDraftService.php
UseCase/
    ...

Note how the naming is still "human", but does have a "Service" suffix.

The problem I ran into was that, because they did not have differentiating suffixes, the "PublishDraft" Use Case had the same class name as the "PublishDraft" Domain Service. That caused local name conflicts when they were both used together. I would have had to alias one or the other in the use statement, so I figured I might as well give one (or the other) a class name suffix, to preempt name conflicts.

In this project I went with a suffix on the Domain Service, but might just as well have gone with a "UseCase" suffix on the Use Cases instead. (You get to pick your tradeoffs here.)

III.

How do you suggest I communicate these possible failures between two different Domain Services? One option is to use the Domain Payloads, but again I think they are only for returning to the action, not between domain services. So the only other option I see really is domain-specific custom exceptions, but I've always felt a bit weird about domain specific exceptions. Do you have any thoughts on this? Any obvious option I'm missing?

I would say: definitely not via Domain Payload while within the Domain. The Domain Payload is always-and-only for reporting back across the boundary to the User Interface layer. Instead, use Exceptions within the domain, and/or some other domain-specific notification or messaging system while within the domain.

Also, to keep domain-level exceptions from escaping to the User Interface layer, the Application Services should have a catchall for exceptions emanating from the Domain. For example, add some wrapper or decorator logic on your Application Services to catch all exceptions from Domain activity, and that catchall can return a Payload (with the error in it) back to the calling User Interface code.


See the Reddit conversation about this post here.


Are You Being Criticized -- Or Cancelled?

"How do we know if something is canceling vs. ordinary criticism?" I came out with a list of six things, kind of the warning signs of canceling. If you've got two or three of these, it's canceling and not criticism.

First: Is the intent of the campaign punitive? Are you trying to punish the person and take away their job, their livelihood, and their friends?

Second: Is the intent or predictable outcome of the campaign to deplatform someone and to get them out of the position that they hold where they can speak/be heard and out of any other such position?

Third: Is the tactic being used grandstanding? Is it not talking to the person about their point of view? Is it basically virtue signaling, posturing, denunciation, and sort of ritual in nature?

Fourth: Is it organized? Is it in fact a campaign? Is it a swarm? Do you have people out there saying, as is often the case, "We've got to get Nick Gillespie off the air" or "We've got to get this asshole fired"? If it's organized, then it's canceling. It's not criticism.

Fifth: A certain sign of canceling is secondary boycotts. Is the campaign targeting not only the individual but anyone who has anything to do with the individual? Are they not only saying, "We think what Nick Cannon is saying on the air is inappropriate"; are they going after the company by saying to boycott it? Are they going after his friends and professional acquaintances? If there's a secondary boycott to inspire fear so that no one wants to have anything to do with the guy for the fear that they'd be targeted, that's canceling.

Sixth: Is it indifferent to truth? Well-meaning criticism is often wrong, but if it's wrong, you're supposed to say, "Oh, gee. I'm sorry that was wrong." You're supposed to pay attention to facts. Cancelers don't. They'll pick through someone's record over a period of 20 years and find six items which they can use against them. This is what literally happened to [Harvard psychologist] Steve Pinker. Tear them out of context and distort them, and if they're corrected on them, they'll just find six other items. That's not criticism. That's canceling. These are weapons of propaganda.

From https://reason.com/2020/11/01/how-to-tell-if-youre-being-canceled/


The Proper Consequences for Speech

(Adapted from my talk Your Project Needs a Code of Conduct, OR ELSE.)

Leftists often say, regarding free speech, that "You might get freedom of speech, but you don't get freedom from consequences!" All right, then, let's go with that for a minute. What are the proper consequences for political speech that you disagree with?

Is it to burn down my house?

Is it to call in a SWAT team to shoot me?

Is it to drag me out of my car and beat me to death?

Is it to punch me in the face when you see me on the sidewalk?

Is it to cancel my financial accounts so that I cannot purchase goods and services?

Is is to threaten my employers into firing me, so that I cannot provide for myself and my family?

Is it to eject me from voluntary organizations?

No.

The proper consequences for political speech that you disagree with, are for you to offer opposing political speech; that is, not "incitements to action" but "arguments."


I Violated A Code of Conduct

Via Hacker News we have this from Jeremy Howard:

NumFOCUS found I violated their Code of Conduct (CoC) at JupyterCon because my talk was not “kind”, because I said Joel Grus was “wrong” regarding his opinion that Jupyter Notebook is not a good software development environment.

Joel (who I greatly respect, and consider an asset to the data science community) was not involved in NumFOCUS’s action, was not told about it, and did not support it.

NumFOCUS did not follow their own enforcement procedure and violated their own CoC, left me hanging for over a week not even knowing what I was accused of, and did not give me an opportunity to provide input before concluding their investigation.

I repeatedly told their committee that my emotional resilience was low at the moment due to medical issues, which they laughed about and ignored, as I tried (unsuccessfully) to hold back tears.

The process has left me shattered, and I won’t be able to accept any speaking requests for the foreseeable future.

I support the thoughtful enforcement of Code of Conducts to address sexist, racist, and harassing behavior, but that is not what happened in this case.

Jeremy exhibits Stockholm Syndrome in his article. He extols the virtues of Codes of Conduct, even though the actual application of this COC left him "shattered."

As with Communism, this is not a problem of the COC "not being applied properly" or "not being done right." Social Justice codes of conduct cannot be "applied properly" or "done right" according to their popularly-assumed intentions. They are always-and-only in place to allow the exercise of arbitrary power over others. The way the COC was enforced here is the norm, not the exception.