Timeline Taxi Out now: my sci-fi novel Timeline Taxi is published!

Unfair Advantage

Note: this post was originally published on the Tempest blog

Someone asked me: why Tempest? What areas do I expect Tempest to be better in than Laravel or Symfony? What gives me certainty that Laravel or Symfony won't just be able to copy what makes Tempest currently unique? What is Tempest's unfair advantage compared to existing PHP frameworks?

I love this question: of course there is already a small group of people excited and vocal about Tempest, but does it really stand a chance against the real frameworks?

Ok so, here's my answer: Tempest's unfair advantage is its ability to start from scratch and the courage to question and rethink the things we have gotten used to.

Let me work through that with a couple of examples.

# The Curse

The curse of any mature project: with popularity comes the need for backwards compatibility. Laravel can't make 20 breaking changes over the course of one month; they can't add modern PHP features to the framework without making sure 10 years of code isn't affected too much. They have a huge userbase, and naturally prefer stability. If Tempest ever grows popular enough, we will have to deal with the same problem, we might make some different decisions when it comes to backwards compatibility, but for now it opens opportunities.

Combine that with the fact that Tempest started out in 2023 instead of 2011 as Laravel did or 2005 as Symfony did. PHP and its ecosystem have evolved tremendously. Laravel's facades are a good example: there is a small group of hard-core fans of facades to this day; but my view on facades (or better: service locators disguised behind magic methods) is that they represent a pattern that made sense at a time when PHP didn't have a proper type system (so no easy autowiring), where IDEs were a lot less popular (so no autocompletion and auto importing), and where static analysis in PHP was non-existent.

It makes sense that Laravel tried to find ways to make code as easy as possible to access within that context. Facades reduced a lot of friction during an era where PHP looked entirely different, and where we didn't have the language capabilities and tooling we have today.

That brings us back to the backwards compatibility curse: over the years, facades have become so ingrained into Laravel that it would be madness to try remove them today. It's naive to think the Tempest won't have its facade-like warts ten years from now — it will — but at this stage, we're lucky to be able to start from scratch where we can embrace modern PHP as the standard instead of the exception; and where tooling like IDEs, code formatters, and static analysers have become an integral part of PHP. To make that concrete:

That clean slate is an unfair advantage. Of course, it means nothing if you cannot convince enough people about the benefits of your solution. That's where the second part comes in.

# The courage to question

The second part of Tempest's unfair advantage is the courage to question and rethink the things we have gotten used to. One of the best examples to illustrate this is symfony/console: the de-facto standard for console applications in PHP for over a decade. It's used everywhere, and it has the absolute monopoly when it comes to building console applications in PHP.

So I thought… what if I had to build a console framework today from scratch? What would that look like? Well, here's what a console command looks like in Symfony today:

#[AsCommand(name: 'make:user')]
class MakeUserCommand extends Command
{
    protected function configure(): void
    {
        $this
            ->addArgument('email', InputArgument::REQUIRED)
            ->addArgument('password', InputArgument::REQUIRED)
            ->addOption('admin', null, InputOption::VALUE_NONE);
    }
    
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $email = $this->getArgument('email');
        $password = $this->getArgument('password');
        $isAdmin = $this->getOption('admin');
        
        // …  
    
        return Command::SUCCESS;
    }
}

The same command in Laravel would look something like this:

class MakeUser extends Command
{
    protected $signature = 'make:user {email} {password} {--admin}';
 
    public function handle(): void
    {
        $email = $this->argument('email');
        $password = $this->argument('password');
        $isAdmin = $this->option('admin');
    
        // …
    }
}

And here's Tempest's approach:

use Tempest\Console\ConsoleCommand;
use Tempest\Console\HasConsole;

final readonly class Make
{   
    use HasConsole;
    
    #[ConsoleCommand]
    public function user(string $email, string $password, bool $isAdmin): void 
    {
        // …
    }
}

Which differences do you notice?

use Tempest\Console\ConsoleCommand;
use Tempest\Console\HasConsole;
use Tempest\Console\ExitCode

final readonly class Package
{
    use HasConsole;
    
    #[ConsoleCommand]
    public function all(): ExitCode 
    {
        if (! $this->hasBeenSetup()) {
            return ExitCode::ERROR;
        }
        
        // …
        
        return ExitCode::SUCCESS;
    }
}
use Tempest\Console\Middleware\CautionMiddleware;

final readonly class Make
{   
    use HasConsole;
    
    #[ConsoleCommand(
        middleware: [CautionMiddleware::class]
    )]
    public function user(
        string $email, 
        string $password, 
        bool $isAdmin
    ): void {
        // …
        
        $this->success('Done!');
    }
}

Now, you may like Tempest's style or not, I realize there's a subjective part to it as well. Practice shows though that more and more people do in fact like Tempest's approach, some even go out of their way to tell me about it:

I must say I really enjoy what little I have seen from the Tempest until now and my next free-time project is going to be build with it. I have 20 years of experience at building webpages with PHP and Tempest is surprisingly close to how I envision web-development should look in 2024. — /u/SparePartsHere

I really like the way this framework turns out. It is THE framework in the PHP space out there for which I am most excited about […] — Wulfheart

# Decisions

Two months ago, I released the first alpha version of Tempest, making very clear that I was still uncertain whether Tempest would actually become a thing or not. And, sure, there are some important remarks to be made:

But.

I've also seen a lot of involvement and interest in Tempest since its first alpha release. A small but dedicated community has begun to grow. We now almost have 250 members on our Discord, the GitHub repository has almost reached 1k stars, we've merged 82 pull requests made by 12 people this past month, with 300 merged pull requests in total.

On top of that, we have a strong core team of experienced open-source developers: myself, Aidan, and Enzo, flanked by another dozen contributors.

We also decided to make Tempest's individual components available as standalone packages, so that people don't have to commit to Tempest in full, but can pull one or several of these components into their projects — Laravel, Symfony, or whatever they are building. tempest/console is probably the best example, but I'm very excited about tempest/view as well, and there are more.

All of that to say, my uncertainty about Tempest becoming a thing or not, is quickly dissipating. People are excited about Tempest, more than I expected. It seems they are picking up on Tempest's unfair advantage, and I am excited for the future.