- Makers Make by SaaSykit
- Posts
- Distribution: The Only Moat Left? SaaS 2.0, Laravel 12.20, Software In The Era Of AI & more
Distribution: The Only Moat Left? SaaS 2.0, Laravel 12.20, Software In The Era Of AI & more
Hey Makers 👋
Hope you're staying cool and productive—whether you're deep in code or just enjoying some well-earned summer slowdown. 🏖️
Laravel quietly dropped v12.20 this week, and it’s packed with smart little features: from customizable pluck()
logic to a handy Context::remember()
method, and even new queue exception handling improvements. Clean, powerful additions that make your life easier. 🧰
Let’s dive deeper into those! 👇
Context::remember(): A cleaner way to cache values in context. Instead of checking and setting manually, you can now just use:
// Before
if (Context::has('user-permissions')) {
$permissions = Context::get('user-permissions');
} else {
$permissions = auth()->user()->permissions;
Context::set('user-permissions', $permissions);
}
// After
$permissions = Context::remember('user-permissions', fn() => auth()->user()->permissions)
New String Helpers:
Str::doesntStartWith()
andStr::doesntEndWith()
make it easier to check for non-matching prefixes/suffixes:
use Illuminate\Support\Str;
Str::doesntStartWith('Laravel Rocks', 'Symfony'); // true
Str::doesntEndWith('Laravel Rocks', ['PHP', 'Rocks']); // false
Queue Throttling Upgrade: You can now use
failWhen()
inThrottlesExceptions
to mark jobs as failed (instead of deleting them) under specific conditions.
public function middleware(): array
{
return [
(new ThrottlesExceptions(3, 600))
->deleteWhen(SpecificException::class)
->failWhen(fn(Throwable $e) => $e instanceof ValidationException),
];
}
Queue::fakeFor() & fakeExceptFor(): Scoped and selective job faking now possible — no more global fakes lingering around your test cases.
Pluck with Closures: The beloved
pluck()
now supports closures for dynamic key-value pairs:
Country::all()->pluck(
fn($c) => "{$c->emoji} {$c->name}",
fn($c) => $c->code
);
Iterable Fluent: Fluent objects now work directly in
foreach
loops — no need to calltoArray()
anymore.
// before
foreach ($user->orders->toArray() as $key => $value) {
// ...
}
// after
foreach ($user->orders as $key => $value) {
// ...
}
@context Blade Directive: Blade got smarter with
@context
, making it easier to conditionally render based on context values.Config::collection(): Fetch config values directly as a collection with
Config::collection('some.key')
.Uri is JSON-ready: The
Uri
class now serializes nicely when returned in JSON responses.
From the Community
Before we start optimizing performance we need ways to effectively measure it. But even before we can measure it we need to know what exactly we want to measure.
Here are some of the most important performance measures of an API/backend service.
PHP 8.5 introduces a diff
option for the php --ini
flag that makes it easy to identify changed INI values in your configuration. The --ini
flag is helpful to show the loaded php.ini configuration file, as well as additional .ini files parsed.
Laravel's toUri()
method transforms string-based URL extraction into fluent URI manipulation, enabling dynamic parameter addition and modification through method chaining.
Traditional URL processing requires complex string manipulation and parsing logic:
Recently, people started talking about a malware called “Androxgh0st” specifically targeting Laravel apps. In a recent edition of Securing Laravel, Stephen Rees-Carter wrote a good explanation of how it works.
The malware targets apps with APP_DEBUG
set to true
. When enabled, Laravel will give detailed error messages, and some security features will be disabled. In production, you always want this value to be set to false
.
When you think about the $fillable
property, you may come to the conclusion that this is actually related to validation: we're telling Laravel which attributes are allowed to be filled and this, at least in my opinion, correlates to our validation rules, that determine how these attributes are allowed to be filled.
Laravel's custom object casting transforms database attributes into rich value objects, enabling sophisticated data handling while maintaining clean model interfaces. This feature bridges the gap between flat database storage and complex application logic.
This is the fourth part of my series on Integrating Third-Party APIs in Laravel. If you haven’t been following along, I highly recommend checking out the previous posts to gain the necessary context for a better understanding of this post.
Laravel's Rule::contains()
method brings array validation into the fluent interface ecosystem, providing consistent syntax that matches other validation rules while improving code readability.
All about SaaS
Your SaaS company is dead without strong distribution. And the distribution game is changing fast…
Cloudflare’s CEO recently shared some shocking stats about the effectiveness of content creation in 2025. Cloudflare handles ~20% of global web traffic so he has the data.
In my first startup job, I was trying to figure out whether I should work in product or marketing.
The CEO asked me a question which has rattled around in my head ever since: do you want to build, or do you want to sell?
My experience since then has added some nuance to that framework. Some jobs are a mix of more than one - for example the role I landed on after that conversation was in growth, which is a mix of building and selling.
A good rule of thumb for a good software business is the toothbrush test: Is your product good enough for people to use at least twice a day?
But AI agents are creating a new test, what I’m calling the magic minimum: Can your product periodically deliver enough unexpected value to be irreplaceable, even if users only engage with it once or twice a month?
One way to describe Salesforce’s flagship product—their customer relationship management tool, in which salespeople keep track of who they’re selling to and how those deals are going—is that it is a bunch of lists. People make this point sometimes: Salesforce is just a database with a point-and-click user interface on top. Businesses buy Salesforce to help their sales teams sell stuff, and when a business wants to sell stuff, they need to maintain a bunch of interconnected lists:
Funnels are a funny thing. Everyone gets excited about the top (’oooh, shiny new prospects, let’s get their attention’) and the bottom (’money, money, money, plz’) but no one gets excited about the middle of the funnel. That’s what I would call the ‘engine room’ of your go-to-market function. That place where you need to get your hands dirty. The place you need to really grind.
Videos
Keep building, keep rocking! 🤘