ECA's reported growth shows how far configuration-based automation has moved into Drupal site-building practice. The milestone also raises the stakes for a project that many sites now rely on for workflow decisions, integrations, and operational logic.
We've published an updated product strategy for Drupal CMS. Version 2.0 replaces the original Drupal Starshot strategy from August 2024, and it reflects where we are after nearly two years of building.
The updated strategy largely documents what we're already doing, and why, and makes some important clarifications.
Developers delivering for marketers
The initial strategy framed content creators and marketers as the primary target audience. That made sense as a signal about our ambitions: Drupal already has a reputation for being developer-friendly, so we wanted to emphasize the focus on end users.
In practice, though, it created some confusion. Marketers are the end users of the sites built with Drupal CMS, but they're not the ones installing it, configuring it, or (in most cases) choosing it. That decision usually belongs to agencies and professional developers.
So the updated strategy is clearer: Content creators and marketers remain the target person for the product as end users, and the primary audience for the builder experience is agencies and professional developers. We can only reach marketers if developers can succeed with Drupal.
Rather than representing a change in what we're focused on, this now more accurately captures it.
What's actually changed
The strategic frame has shifted to "making agencies and developers successful faster." The end goal of delivering great experiences for content teams is still central to the strategy, but we are explicit about doing that through agencies and developers.
A few other notable updates:
AI is now framed as infrastructure, rather than a feature. The original strategy positioned AI as one of several ways to win. Version 2.0 is more direct: every workflow in Drupal CMS should be operable by an AI agent. The goal is to be able to ship new AI-enabled workflows in days, not months.
Integrated hosting providers are now explicitly part of the strategy. These platforms are becoming real distribution channels for Drupal CMS, and the strategy names them as a priority. Making Drupal CMS excellent to provision and host is a prerequisite for those partnerships.
Vibe coding platforms are now named as a positioning opportunity. We're not competing with tools like Lovable or Bolt for prototyping. But we are positioning Drupal CMS as where those projects land when they need real content governance, multi-contributor workflows, and long-term maintainability.
The timeline has changed. Version 1.0 set a target of June 2027. Version 2.0 extends that to June 2028, acknowledging that the scope has grown and the strategy is more comprehensive.
What hasn't changed
We're still aiming to expand in the mid-market, with projects with total budgets in the $30,000-$120,000 USD range, and we're still explicitly not competing with entry-level website builders. We are also calling out that we will continue to maintain our leadership in the enterprise market.
And, of course, the differentiators against proprietary CMS solutions are the same: open source, no vendor lock-in, digital sovereignty.
Drupal 11.4.0 is the latest feature release in the Drupal 11 branch, bringing developer-focused enhancements, performance improvements, and a smoother upgrade experience. As a minor release, it introduces new capabilities while maintaining backward compatibility for public APIs, making it a recommended upgrade for production websites. Drupal 11.4.x will receive security support until June 2027âĻ
Drupal work now extends beyond implementation alone. Four Asheville sessions show where accessibility, AI, components, and local tooling shape delivery.
Recent Drupal developments point to a clearer direction for the project's AI and developer-experience work. The immediate story is not only that Drupal is adding AI features.
Core performance, upgrade tooling, workflow orchestration, and AI governance are beginning to converge around a more controlled operating layer for modern web teams. That makes maintainability, auditability, and human review central to Drupal's AI direction, not secondary safeguards.
Drupal 11.4.0, published on 1 July 2026, gives that direction a stronger core foundation. The release reduces database queries compared with Drupal 11.3, speeds up recipe-based site installation, improves translation file handling, and adds Brotli compression for aggregated CSS and JavaScript when ext-brotli is available. It also introduces an experimental extensible native command-line interface through ./vendor/bin/dr, improves password hashing with support for argon2id, and adds display-management changes intended to support tools such as Drupal Canvas.
The AI layer is moving in the same direction. Drupal AI 1.4.0 adds developer-focused drush generate commands for providers, automators, guardrails, operation types, API explorers, function calls, and related extension points. The release also introduces chat normalisation, Views Bulk Operations integration for AI Automators, failover foundations, and streaming guardrails.
Those additions support AI workflows that need clearer execution paths, safer handling, and extension points that contributed modules can build on. They also match the distinction now forming around Drupal's Inside AI and Outside AI work. Inside AI covers cases where a person uses Drupal and Drupal uses AI to assist, while Outside AI covers cases where a person uses an external agent and the agent uses Drupal.
In that model, Drupal's value is not just page rendering. It is the governed system of record for content structure, permissions, validation, moderation, revisions, and publishing workflows. Dries Buytaert's recent writing on agentic workflows frames the same challenge around setup, connection, context, governed action, validation, recovery, and launch.
The same question appears in Drupal's orchestration work. Recent discussions around ECA, FlowDrop, Maestro, and Drupal core focus on whether automation tools can share vocabulary and data-handoff contracts while keeping their different execution models. Randy Kolenko's recent Nextide post adds the durable-state side of that discussion, positioning Maestro around long-running workflows, human approval steps, and audit trails that persist beyond a single request or cache cycle.
Upgrade tooling is also becoming part of the maintainability story. As of Rector 2.5, Composer-based sets support Drupal, allowing Rector to inspect composer.json, detect installed Drupal and dependency versions, and apply relevant refactoring sets without manually listing each Drupal version in rector.php. For site owners and maintainers, that reduces configuration work as Drupal 11.4, Drupal 12, and later releases move through the upgrade path.
The broader open-source context came through at UN Open Source Week 2026, held from 22 to 26 June 2026 at United Nations Headquarters in New York. The official programme focused on open source, artificial intelligence, Digital Public Infrastructure, Open Source Program Offices, sustainable public infrastructure, and digital cooperation. Matthew Saunders' Drupal.org reflection connected those discussions to AI harnesses, orchestration, constraints, auditability, verification, and human-in-the-loop workflows.
For Drupal, the practical implication is clear. AI adoption depends less on isolated prompts and more on trusted systems that can govern what agents do, record what happened, and keep human responsibility visible.
In early 2025 I noticed an odd behavior with how responsive images were rendering in my Drupal site. This was a Drupal 10.4 site and the configuration I set was being ignored causing images to render smaller than expected.
I have worked with responsive images for many years and have written a 7-part blog series about them, and have never run into this issue until now.
Note: The issues described in this post do not affect the <picture> element. They are only present when using the srcset and sizes attributes of the <img> tag.
After some debugging and testing, I noticed the image rendering issue was directly related to the fallback image style used in the responsive images UI. The original purpose of the fallback image is to be used in the event the browser does not support responsive images. As of the date of this post, the browser support for srcset, sizes, and <picture> is at about 97%, which pretty much means it is no longer an issue in almost all cases.
The responsive_image core module in Drupal started explicitly adding width and height attributes to the <img> tag from the fallback image's dimensions. This was a fix for an issue in Drupal core 10.1.x-dev, which was closed (fixed) on August 9, 2023.
Rendering image at 325 x 217px (fallback image dimensions), but the loaded image is 2600 x 1733px.
Additional research led me to Drupal.orgissue #3377420 which seems to be where things may have changed and resulted in the bug I was experiencing. After reading through the comments in the issue page, issue #3359421 was referenced which was even older than #3377420, and goes back to Drupal 10.1. In either case, I see the main reason for the new changes was to address a well-known issue when loading images, Cumulative Layout Shift.
Cumulative Layout Shift, or CLS[1], has been a problem for many years when rendering images or other media content. CLS refers to the shifting of layouts or content as pages with images or other media content load. If your Drupal site is rendering images small and then "jumping" to full size, your real-world users are experiencing layout shifts. This is not only a bad user experience for your visitors, but it also affects accessibility and SEO[2] ranking.
While addressing CLS should be a priority, I don't think forcing the fallback image's dimensions into the rendered image is the way to go about it, but I do understand the community's intentions were in the right place. I just hoped this would have been a wider and more open process so developers had a better understanding of what was happening.
The original issue I referenced above gave me clues to correct the bug I was experiencing: revert the changes done in that issue by creating my own patch that I and others could use to resolve the issue and go back to the original state.
I created Issue #3516726, which removes the logic that was forcing the fallback image's width and height into the rendered image. Looking back I realize the problem was not the fact that the image's dimensions were those from the fallback image, but not having a better system for providing the rendered image with the correct dimensions during the page load process.
This is the real issue: A timing gap between the server and client when network requests for image information are triggered by the browser.
Whether you use Drupal or any other software stack, the data orchestration between the server and the browser experiences a delay. The server has the raw data (the image file), but the browser cannot access it when it needs it most (during initial page parsing).
The Browser's Goal: The browser reads the HTML document line by line. It wants to calculate the page layout immediately so it can show text to the user as fast as possible.
The Reality: When the browser encounters an image tag, it only sees a string URL (e.g., src="photo.jpg"). It cannot instantly know what that binary file looks like.
The Gap: The browser must pause, issue a secondary network request to fetch the image, and wait. Until that binary data arrives, the browser has zero visibility into the file's contents (i.e. dimensions).
The patch I wrote, while it prevented the fallback image dimensions from being used in my rendered images, didn't fix the issue. Instead, it directed it to the image style's dimensions I had configured as my responsive image styles bundle within Drupal's responsive image styles UI. You may be thinking: great, isn't that what the purpose of responsive image styles is? The answer is "yes", but there was a problem: The same "timing" issue described above. The browser still could not grab the proper image dimensions from the multiple image sources and pass it down to the rendered image, so Drupal again, trying to be helpful, this time was forcing the image dimensions from the last image style in my responsive image styles bundle (See the image below). The outcome is the same as the first issue because if the dimensions of the last image style are too small, the image renders smaller than the expected size.
In the image above, I have configured responsive images so when the viewport reaches 1040px or higher, the image should render at 1040px wide. However, since the dimensions of the last selected image style are 500px by 500px, that's the size in which my image will render. Same issue as before except now it originates from a different source.
Just like before, I filed Issue #3523451, which effectively updates Drupal's logic but this is simply another workaround that does not address the core issue of images rendering.
Organize image styles in the right order: Not always possible since they are automatically sorted alphabetically based on their machine name.
Use the <picture> element: Not the recommended approach unless you are managing art direction.
Try the patches in this post and see how things work for you
Or, could there be a new solution? Maybe...
Understanding the server/client relationship and why image rendering is still an issue will allow you to proactively plan for a "solution" that works for your projects. Looking back, I don't need either of the patches I wrote because they are both doing the same thing Drupal core is already doing, assigning an arbitrary width and height to images in an effort to address CLS.
Knowing this gives me a better context to determine an approach that could work well in my project based on its requirements. In my case, I know I need to set my fallback images to a size that would satisfy the most common image rendering use cases. It's not ideal, but is there any other choice? Maybe. Read on.
In theory, the srcset and sizes attributes for the <img> tag were supposed to address our responsive images, but as you have learned in this post, they don't. However, don't throw them away just yet; they are still critical pieces of the equation and we need them because they offer many benefits.
If you get excited about responsive images like I do, sorry, something is not well in your head đ. Actually, if you are a fan of responsive images, you may have heard of a brand new and promising solution that has surfaced in recent months: Using sizes="auto" in your image tag. It is new but all but one of the major browsers already support it (caniuse.com).
Google's Baseline still is still not providing correct data points, but give it some time and we should see these numbers improve.
For comparison, this is how a typical responsive image configuration may look at an image level:
<img srcset="image-small.jpg 300w, image-medium.jpg 800w, image-large.jpg 1200w" sizes="(min-width: 1200px) 1200px, (min-width: 760px) 800px, 100vw" src="image-medium.jpg" alt="A description of the image">
Notice the srcset attribute contains multiple image options/sources to satisfy any use case in this example. Also notice how each source includes a width (w) descriptor: 300w, 800w, etc. The w descriptor's job is to inform the browser how wide each image is. This is extremely important information for the browser.
Also important is the sizes attribute. The value of sizes can be as simple as 100vw which means 100% the viewport width, full width, or a more complex formula using a media query as shown in our example above to query the width of the viewport and conditionally serve the right image for the job.
The following technique is a new attempt at addressing the gap between the server/client requests when rendering images. It is probably the most promising technique I've seen in years, and several articles about it have been recently published including one from Matt Marquis, one of the founding members of the Responsive Images Community Group or RICG[3]. I have been following and learning from Matt since the days when he first introduced the <picture> element, a ground-breaking development to manage responsive images.
Rather than repeating what Matt so perfectly has explained, I'd encourage you to read his article along with others I've shared in the resources section at the end of this post, for a complete description of the sizes="auto" approach.
As a teaser, I have updated the previous code snippet by adding auto as a value to the sizes attribute along with the previous value as a fallback. Take a look:
As Matt Marquis perfectly puts it regarding the server/client latency issue:
the central issue with srcset/sizes was one of timing...
and he continues by explaining why previous attempts to address this issue were put in place:
...a browser makes decisions about image requests long before it has any information about the page's layout, so we had to provide it with that layout information.
To be clear, although this is still the default behavior: If your markup includes an <img> tag, it will trigger a request long before any page layout information can be known - that is, unless the image uses the loading="lazy" attribute, a well-known standard for any image that appears outside of the user's viewport at the time the page first loads.
By using loading="lazy", the image is now requested at the point the user interacts with it (user scrolls to display the image within the viewport), long after the browser has all the information it needs about the sizes of the rendered image thereby ensuring it meets the layout requirements and avoiding CLS.
You can use sizes="auto" now as shown in the previous snippet and browsers that support it will use it, while browsers that don't yet support it will ignore it and use the traditional media query shown in the snippet. Win-win đ
I feel we are at a point in time when we finally have a good handle on serving images the right way. I for one will be diving deep into sizes="auto" to see for myself the effects of this technique and whether this would address the bugs this post was written about. I shall report back.
We're announcing DDEV v1.25.3: faster ddev start and ddev stop, built-in Docker Compose, stable Podman and Docker Rootless support, MariaDB 12.3 LTS support, Node.js improvements, XDG_CONFIG_HOME changes, and more.
This release represents 131 PRs from the entire DDEV community: your suggestions, bug reports, code, and financial support made it possible.
Table of Contents
Faster ddev start, ddev stop, and ddev restart
ddev start in v1.25.3 (bottom) is faster than in v1.25.2 (top), including a faster warm start:
ddev stop in v1.25.3 (bottom) is significantly faster than in v1.25.2 (top), and the same improvement also applies to ddev poweroff and ddev delete, since all three share the same code path:
ddev restart in v1.25.3 (bottom) is significantly faster than in v1.25.2 (top), since it stops and starts a project and benefits from both improvements:
Post-healthcheck tasks now run concurrently instead of one after another, reducing overall ddev start time, thanks to @jonesrussell.
A bug in the web server startup script also added a ~10-second delay to ddev stop. That delay is now gone.
We benchmarked ddev start from a stopped state on both macOS and Linux, and v1.25.3 is faster on both. Numbers vary by machine, but you can reproduce it with scripts/compare-start-perf.sh:
git clone https://github.com/ddev/ddev ddev-upstream
cd ddev-upstream
bash scripts/compare-start-perf.sh v1.25.2 v1.25.3
On macOS, v1.25.3 is about 28% faster than v1.25.2 (benchmarked by @rfay):
Summary (ddev start from stopped state)
-------------------------------------------------------------------
A (v1.25.2): median=11.03s trimmed-mean=10.49s
B (v1.25.3): median=7.91s trimmed-mean=7.84s
B is FASTER than A by 3.12s (-28.3%) on median
On Linux, it's about 21% faster (benchmarked by @stasadev):
Summary (ddev start from stopped state)
-------------------------------------------------------------------
A (v1.25.2): median=18.03s trimmed-mean=18.25s
B (v1.25.3): median=14.18s trimmed-mean=14.96s
B is FASTER than A by 3.85s (-21.4%) on median
New Docker Compose Library
DDEV now uses the Docker Compose SDK directly instead of shelling out to a separate docker-compose binary. The $HOME/.ddev/bin/docker-compose binary DDEV used to download and manage can be removed. This switch was made possible by the Docker Compose maintainers, who exposed the SDK as a reusable library in Compose v5.0.0. Thank you very much!
Driving Compose through the SDK is also what gives you the cleaner output and live per-step timer in the GIFs above: DDEV now controls how progress is displayed instead of passing through whatever the external binary printed.
This is the same underlying change that added the optional ddev config global --docker-buildx-version setting in this release. See Docker Buildx Requirement in DDEV for the full background on Buildx and the Compose SDK switch.
MariaDB 12.3 LTS Support
DDEV now supports MariaDB 12.3, the latest LTS release. For new projects, set it with:
ddev config --database=mariadb:12.3
To migrate an existing project's database, use:
ddev utility migrate-database mariadb:12.3
Podman and Docker Rootless Are No Longer Experimental
Both Podman rootless and Docker rootless are now stable. We introduced this support as experimental in v1.25.0. See Podman and Docker Rootless in DDEV for the background, trade-offs, and the work behind it. Setup instructions:
The correct Node.js version is now used during the build phase of ddev start. Previously the build phase always used DDEV's default version, which could cause problems when a project specified a different one (see ddev-pnpm#14).
If you install global npm packages in post-start hooks, move them to extra Dockerfiles instead, since those now run against the correct Node.js version.
nodejs_version is now preserved in .ddev/config.yaml even when it matches DDEV's default (previously it was removed in that case).
Setting nodejs_version: "" in .ddev/config.yaml always uses the default Node.js version bundled with DDEV, currently Node.js 24.
You can install additional Node.js versions with ddev exec n install <version> inside the web container. This used to be a reason to use nvm, which was moved to the ddev-nvm add-on in v1.25.0; with n built-in, you no longer need nvm for it.
N_PREFIX moved from /usr/local to /usr/local/n.
See the updated nodejs_version documentation for more details.
XDG_CONFIG_HOME Is No Longer Respected, but DDEV_XDG_CONFIG_HOME Is Available
We received several reports of DDEV recreating $HOME/.ddev repeatedly:
Warning: multiple global DDEV configurations found, /home/stas/.config/ddev is used, /home/stas/.ddev is not used, delete one of them to avoid confusion
IDEs such as PhpStorm don't always see XDG_CONFIG_HOME from the terminal, so DDEV fell back to and recreated $HOME/.ddev repeatedly. See the upstream issue IJPL-1055 for details.
To avoid this problem, DDEV now reads its own environment variable, DDEV_XDG_CONFIG_HOME, and no longer respects XDG_CONFIG_HOME. If you had set XDG_CONFIG_HOME to something other than its default of $HOME/.config, set DDEV_XDG_CONFIG_HOME to that same value instead.
Support for using $HOME/.config/ddev as the global configuration directory on Linux is unchanged.
Everything Else
This release includes many more features and bugfixes. See the full release notes for the complete list.
From the entire team, thanks for using, promoting, contributing, and supporting DDEV!
If you have questions, reach out in any of the support channels.
It's often useful to let the machines do the work, and output something that's dynamically computed on an entity. By that I mean something that can't be hardcoded as a fixed value for all entities of a particular type, but that varies for each entity, in a way that allows it to be generated in code rather than laboriously entered into each entity form by humans.
For example, you might want a backlink for an entity reference, or a link to a view that has an argument for the entity's ID, or something that depends on field values on the entity.
There are several ways in Drupal of putting something dynamic on the entity's display output. You can of course add something to the build array yourself, in either the entity's view handler or hook_entity_view(). The extra fields system lets you then declare your additional build array item with hook_entity_extra_field_info() which allows it to be rearranged among normal fields in the field admin UI.
This is okay, but the extra fields system is Drupal 5-era stuff. Your piece of build array is just that, some render stuff; it can't participate in any data structures and nothing else will recognise it and work with it.
A better approach is a computed field. This involves a little more boilerplate code than the extra fields technique, but there are several benefits.
The first is that you are defining a field value, not a render array, and you get access to all the field formatters that apply to your type of data. So for example, if your computed data is a URL, you get all of the link field formatters at your disposal, in core and contrib.
The second is that anything that works with fields will be aware of your computed field. So you can add it to a view as a field (though not a sort or filter of course, since it has nothing in the database). You can add it to a SearchAPI index (and there, you actually can filter on it, because SearchAPI will index the computed value into its backend).
The code
Here's what you need to do. You need two things:
A FieldItemList class.
A declaration of your field in an entity class or field info hook.
Unlike declaring code fields, you don't need to declare a field storage: that's because a computed field doesn't store anything!
1. The FieldItemList class
Create a subclass of \Drupal\Core\Field\FieldItemList that uses \Drupal\Core\TypedData\ComputedItemListTrait. In this class, all you need to do is implement computeValue() to return your data.
<?php
// The namespace doesn't matter, but I like to put it under \Field.
namespace Drupal\my_module\Field;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;
use Drupal\Core\Url;
/**
* Field item list class for my computed field.
*/
class MyFieldFieldItemList extends FieldItemList {
use ComputedItemListTrait;
/**
* {@inheritdoc}
*/
protected function computeValue() {
// You have access to the complete entity, so you can use other field
// values.
$entity = $this->parent->getValue();
// Create a field item for your data. You can create more than one for a
// multi-valued field.
$this->list[] = $this->createItem(0, [
'value' => 'cake',
]);
}
}
?>
For most field types, the key to use in the item array is 'value', but some more specialised fields use something else. You can find this by looking in the field item class for the field type. For example, in \Drupal\link\Plugin\Field\FieldType\LinkItem::propertyDefinitions() you can see that for a link field, you need these array keys:
For the field definition, there are two things to consider:
Is it on an entity type you control, or somebody else's?
Do you want a base field or a bundle field?
If it's your own entity type, you define the field in the entity class, in either the baseFieldDefinitions() or bundleFieldDefinitions() method. If it's an existing entity type, you need to use hook_entity_base_field_info() or hook_entity_bundle_field_info().
In all cases, the code is broadly similar. For a base field, it looks like this:
/**
* Implements hook_entity_base_field_info().
*/
#[Hook('entity_base_field_info')]
function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
if ($entity_type->id() == 'node') {
$fields = [];
$fields['my_computed_field'] = BaseFieldDefinition::create('link')
->setLabel($this->t('My computed field'))
->setDescription($this->t('My field is amazing.'))
// This declares it as a computed field.
->setComputed(TRUE)
// This is the class you created earlier, which provides the values.
->setClass(MyFieldFieldItemList::class)
// Optional default view display options, which can be overriden in the admin UI.
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'link',
'weight' => '0',
]);
return $fields;
}
}
For a bundle field, you need the Drupal\entity\BundleFieldDefinition class from Entity module, and a few extra things need to be explicitly set on the definition because the field system doesn't handle them for you:
$fields['my_computed_field'] = BundleFieldDefinition::create('link')
->setName('my_computed_field')
->setTargetEntityTypeId($entity_type_id)
->setTargetBundle($bundle)
// Rest of the definition as above.
If you want to do it all with less boilerplate, or your computed field is something that's reusable across different entity types, consider the Computed Field module as an alternative to the code examples above. Instead of a Field class, the computational code does in a plugin, which the module then makes available in an admin UI where you can create and edit computed fields alongside the usual stored fields.
And if your computed field is purely a render array rather than data, the Computed Field module also provides a computed_render_array field type for that, with an accompanying field formatter.
Do you need help with data structures, and their integration with Views or SearchAPI? I'm available for hire - contact me!
Canvas Internals - JSON data types in differentes databases: It works on my machine!
Image
Drupal has been working to add a JSON data type since 2023, but that has not landed yet. Drupal Canvas jumps ahead of that in its inputs for a component tree item with
'inputs' => [
'description' => 'The input for this component instance in the component tree.',
'type' => 'json',
'pgsql_type' => 'jsonb',
'mysql_type' => 'json',
'sqlite_type' => 'json',
'not null' => FALSE,
],
Recently some of our tests started failing for MySQL and Postgres on CI, but passed in SQLite and MariaDB, which is what most of us use locally.
The problem was that the sorting of the keys of that field was not deterministic, and we used assertSame in our tests to see if operations added/removed the inputs as expected when components evolved.
To make lookups more efficient, MySQL also sorts the keys of a JSON object. You should be aware that the result of this ordering is subject to change and not guaranteed to be consistent across releases.
For PostgreSQL, the engine offers two different data types: json and jsonb, with the second being the option we (and core) opted for because of its efficiency. But that's key, as the docs explain:
In general, most applications should prefer to store JSON data as jsonb, unless there are quite specialized needs, such as legacy assumptions about ordering of object keys.
That's exactly what our problem was.
For MariaDB, the JSON type is just an alias. See their docs:
JSON is an alias for LONGTEXT COLLATE utf8mb4_bin introduced for compatibility reasons with MySQL's JSON data type. MariaDB implements this as a LONGTEXT rather, as the JSON data type contradicts the SQL:2016 standard, and MariaDB's benchmarks indicate that performance is at least equivalent.
And the last one, SQLite, has support for a jsonb format since 3.45, but the work in progress for introducing this in Core uses json, which, like MariaDB, is ordinary text and sorting of the keys is respected.
How did we fix this?
The actual sorting of the inputs in the database is, as of today, irrelevant to us. So we ended up with:
Our own assertSameInputs, which sorts the keys before comparison. assertEqualsCanonicalizing is not an option, as that sorts by value.
Our own PHPStan rule, which is not 100% accurate but detects most usages of assertSame with these inputs, and suggests using assertSameInputs instead.
Translating Drupal Canvas
This is just one of the many show-stoppers that we faced while working on the much-anticipated symmetric translation support for Drupal Canvas. If you want to test this experimental feature, check the release notes in Canvas 1.7.0, but please only on test sites for now!
Shibin Das explains why visual workflow tools need visible execution, audit trails, runtime ownership, and clear boundaries between deterministic workflows and AI-assisted decision-making.
Every Drupal developer eventually hits this wall: a client or editor requests that a standard text field (like a headline or subheadline) be expanded from 255 to 512 characters. You change the value in the field configuration YAML or try to update it programmatically via the Entity API, only to be hit with a fatal exception.
Randy Kolenko, partner and senior architect at Nextide, recently joined the Drupal Orchestration Initiative with Jurgen Haas, Shibin Das and Dries Buytaert. The Orchestration Initiative is still in its infancy, however, the discussions and (dis)agreements will continue until the vision of what Orchestration means to Drupal is sharpened.
Instead of manually adding dozens of configuration sets and keeping your list up to date as you upgrade to new Drupal releases, you can enable the new feature in your rector.php file. Rector will automatically inspect your composer.json, detect your exact installed versions of Drupal and its dependencies, and run the relevant refactoring sets. This means as you upgrade to newer versions of Drupal in the future, Rector will dynamically adapt and apply the correct upgrade rules without any manual config updates.
Two lines of configuration does the work: withSetProviders registers the Drupal rules, and withComposerBased(drupal: true) tells Rector to select them based on what's actually installed. No version numbers need to be in your config.
Rector reads the installed drupal/core version and loads every set up to and including that minor. A site on 11.4 loads the 11.0 â 11.4 rules. A site on 11.2 loads 11.0 â 11.2. When you upgrade core, the set selection moves with you. You don't need to change rector.php again.
That's the same mechanism Rector already uses for Symfony, Doctrine, Twig, and PHPUnit. Drupal is now a first-class citizen.
But the config was never the hardest part. The hard part was coverage. Automatic version selection is only worth anything if the rules behind it are good and preferably complete. That changed a lot when the Project Update Bot was refreshed for Drupal 12 readiness, pushing automated deprecation coverage past 80%. The bot and drupal-rector draw from the same well. Better coverage there is what made shipping this as the default setup defensible. The fact we also run all these rules against almost 10.000 contrib modules makes for some very good testing.
Even less work maintaing your Drupal site
Drupal rules will also appear on getrector.com/find-rule at a later date. That's the searchable catalogue of every rule Rector ships. Having Drupal in it means a maintainer can look up exactly which transformation handles a given deprecation, the same way they can for any other framework today.
Drupal 12 readiness isn't a one-time push, every new minor brings deprecations, and we will keep on adding any missing coverage. Because your setup selects rules by installed version, the rules you get tomorrow are the rules for the core you're running tomorrow. No migration step. You upgrade core, you run Rector, you're up-to-date.
Add it to a project this week. Point it at your custom modules, run vendor/bin/rector process, and open an issue when something doesn't transform the way it should. It's a beta because we want exactly that. Two lines of config, the correct rules for your version, automatically.
Also posted on Rector's blog, big thanks to the author of Rector, Tomas Votruba for the collaboration on making this happen.
Digital sovereignty often sounds abstract but, in practice, it comes down to technical decisions: where data is stored, who controls the platform, how systems are maintained over time, and how much privacy, transparency, and independence is built in from the start.
These choices directly affect how digital services are designed and delivered. That is why digital sovereignty is a key theme at DrupalCon Rotterdam 2026. The event's Digital Sovereignty & Open Web track connects platform strategy with architecture, governance, accessibility, regulation, and the long-term future of open digital ecosystems.
Image
Photo by Matthew Saunders
This is not only a policy discussion, it is also a practical one. Privacy-first architecture, public code, digital identity, accessibility, open-source infrastructure, and responsible AI all shape how organisations think about control and trust today. In that context, digital sovereignty is no longer a side topic, it's becoming part of how teams approach procurement, hosting, compliance, and long-term platform resilience.
That is what makes this conversation especially relevant in Rotterdam. Developers can connect values to implementation, digital leaders can look at governance and long-term control and public sector teams, accessibility advocates, and open-source contributors can all bring important perspectives to the same discussion.
Drupal has long been part of the open web story. At DrupalCon Rotterdam, digital sovereignty becomes a practical question: how do we build systems that remain open, secure, adaptable, and worthy of trust.
- Article by Daniela Moreira.
đī¸ Join Us at DrupalCon Rotterdam 2026
Continue the conversation at DrupalCon Rotterdam 2026, where the Digital Sovereignty & Open Web track explores the technologies, strategies, and decisions shaping open digital ecosystems.
Release timing is often treated as calendar work, but the 11.4.0 delay exposed a dependency problem beneath the schedule. The fix changes how selected Composer dependencies can move when security updates land.