For the last decade, customizing a Magento or Adobe Commerce store has meant exactly one thing: write a PHP module that runs inside the application. That's still true on Magento Open Source, but it's no longer where Adobe is sending new Commerce work, and a lot of Magento developers haven't noticed.
Over the last few weeks I've been asking teams that used to train their developers on M.academy what changed. One of the largest Adobe Commerce partners out there told me "the majority of our new work is focusing on OOPE and App Builder". A second large team said nearly the same thing: once they moved to Commerce as a Cloud Service, their training needs shifted to App Builder and Edge Delivery storefronts.
OOPE stands for "out-of-process extensibility". The name sounds a bit more extensive than the idea, so let's walk through it.
In-process: how we've always done it
Say you need to send every new order to an ERP. The classic answer, on both Magento Open Source and Adobe Commerce, is an observer.
You register it in etc/events.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="sales_order_place_after">
<observer name="vendor_erp_send_order" instance="Vendor\Erp\Observer\SendOrderToErp"/>
</event>
</config>And then write the class:
<?php
declare(strict_types=1);
namespace Vendor\Erp\Observer;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
class SendOrderToErp implements ObserverInterface
{
public function execute(Observer $observer): void
{
$order = $observer->getEvent()->getOrder();
// Call the ERP API with the order data...
}
}This works, and it's worked for years. But look at where that code lives: it runs in the same PHP process as checkout, during the same request, with full access to the application's internals.
If the ERP is slow, checkout is slow. If the ERP throws an error, you'd better have caught it. And every time you upgrade, this module is one more thing that can break, because it's coupled to core classes, events, and dependency injection config.
Multiply that by the 40 to 80 modules on a typical brownfield store, and you have the real reason upgrades are so painful.
Out-of-process: the code moves outside
Adobe's definition is pretty straightforward: custom logic runs as applications outside the Commerce application process, and those applications talk to Commerce through APIs, events, webhooks, and extension points, instead of adding PHP code to the Commerce codebase.
In practice, "outside" usually means Adobe App Builder, which is a serverless JavaScript layer running on Adobe's infrastructure.
Commerce and your app then talk in three ways:
- Commerce fires an event ("an order was placed") and your app reacts to it, asynchronously
- Commerce calls a webhook and waits for your app's answer before it continues (think: validate this, calculate that)
- your app calls back into Commerce through its REST or GraphQL APIs
Here's the same ERP integration done out of process on Adobe Commerce. First, you subscribe to the event. Adobe's own example for this exact use case uses plugin.magento.sales.api.order_management.place, which fires when a shopper places an order:
bin/magento events:subscribe plugin.magento.sales.api.order_management.placeNotice the name. Commerce eventing can emit events from both observers (observer.*) and plugins on service contracts (plugin.*), so you aren't limited to the events core happens to dispatch. You can also declare subscriptions in a module's etc/io_events.xml file, and pick only the fields you want sent along.
For example, here's a separate subscription to order saves:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module-commerce-events-client/etc/io_events.xsd">
<event name="observer.sales_order_save_after">
<fields>
<field name="entity_id" />
<field name="increment_id" />
</fields>
</event>
</config>Then the actual work happens in an App Builder runtime action, which is just a JavaScript function that receives the event payload:
const { Core } = require('@adobe/aio-sdk');
async function main(params) {
const logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' });
try {
const order = params.data.value;
await sendOrderToErpSystem(order);
return {
statusCode: 200,
body: order,
};
} catch (error) {
logger.error(error);
return { statusCode: 500, body: { error: 'server error' } };
}
}
exports.main = main;This ensures that the checkout no longer waits on the ERP. If the ERP is down, the action fails in its own runtime and gets logged there, not in the middle of someone's order. And when you upgrade Commerce, there's no PHP class of yours sitting in the request path to break.
Webhooks: when Commerce needs an answer
Events are fire-and-forget. Sometimes Commerce needs your app to answer before it can continue, and that's what webhooks are for.
Here's a trimmed-down version of Adobe's tax example from etc/webhooks.xml, which hooks into the tax calculation service contract and sends the quote details out to your app:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_AdobeCommerceWebhooks:etc/webhooks.xsd">
<method name="plugin.magento.tax.api.tax_calculation.calculate_tax" type="after">
<hooks>
<batch name="Order_Updates">
<hook name="update_order" url="{env:APP_URL}/calculate-taxes" method="POST" timeout="5000" softTimeout="1000" priority="300" required="false" fallbackErrorMessage="The taxes cannot be calculated">
<fields>
<field name="quoteDetails" />
<field name="storeId" />
</fields>
</hook>
</batch>
</hooks>
</method>
</config>Your app answers with a small JSON payload, and { "op": "success" } lets the process continue. Webhooks can also block.
For a validation hook, such as checking stock when a product is added to the cart, your app can return an exception operation, which stops the process with a message the shopper sees:
{
"op": "exception",
"message": "The product cannot be added to the cart because it is out of stock"
}That's how you'd handle validation that used to live in a plugin: the rule lives in your app, and Commerce just asks.
Why Adobe is pushing this so hard
Two reasons, and the second one is the one that matters to you.
The first is upgrades. Adobe says this separation lets you deploy an app independently from the Commerce release cycle and "reduce upgrade coupling". Anyone who has spent a week chasing a broken third-party module after a patch release knows exactly why that looks attractive.
The second is Adobe Commerce as a Cloud Service. It's a versionless SaaS platform, and Adobe's migration docs are blunt about it: the in-process model of modules, plugins and observers "no longer applies", and customizations run as out-of-process App Builder applications instead. On the SaaS product, there is no in-process option at all. If a merchant moves there, every customization you'd normally write as a module has to be rebuilt this way.
What about Magento Open Source?
Magento Open Source is largely unaffected by any of this. Adobe I/O Events for Commerce requires Adobe Commerce 2.4.4 or higher, and Adobe's install docs say it plainly: "Magento Open Source is not supported". Webhooks are listed for Adobe Commerce on Cloud and on-premises 2.4.4+ only, so treat them as Commerce-only too.
So if you work on Open Source stores, nothing about your day job changes tomorrow. In-process modules are still how you'll customize them, and that isn't going away anytime soon (see Magento Open Source vs. Adobe Commerce for how the two differ).
But the Open Source market keeps tightening, and a lot of Magento developers will end up working on Commerce projects (or on other platforms entirely) at some point. So it's worth knowing how this stuff works. A thin observer that publishes to a message queue, with the real work done by a separate consumer or service, gets you most of the same isolation on Open Source today. You won't get Adobe's included plumbing, but you'll be writing code that's already structured the way new Commerce work expects -- and have full control over the code.
It's not just Adobe
Shopify Functions follow the same idea, but are even stricter.
A cart transform function can only return three kinds of operations (lineExpand, linesMerge and lineUpdate), and it runs under a hard execution budget of 11 million instructions for carts up to 200 line items. Extension points across commerce platforms are getting narrower, more sandboxed and more metered, and the "just write a plugin around anything" freedom Magento developers grew up with is becoming the exception.
That narrowing is exactly what makes Magento Open Source stand out. It's one of the last serious commerce platforms where you get the ENTIRE codebase, run it on WHATEVER infrastructure you want, and can write a plugin around nearly ANY public method in the system without asking anyone for permission. There's no instruction budget, no list of approved extension points, and no vendor deciding which parts of the checkout you're allowed to touch. If something breaks, you can open the source, find the problem and fix it yourself (and with AI reading the codebase alongside you, that's often a five-minute job now).
I recently talked with a long-time Magento developer whose team moved their store to Shopify, and the thing he missed most was exactly that: being able to just fix and modify anything themselves. For merchants who really need that kind of control (custom pricing rules, unusual checkout flows, deep ERP integrations), Open Source is still the best tool for the job.
The Open Source market may be getting smaller, but the stores that stay on it are staying for the control, and that's exactly where deep Magento knowledge pays off the most.
In-process vs. out-of-process at a glance
| In-process (modules) | Out-of-process (App Builder) | |
|---|---|---|
| Where the code runs | Inside the application's PHP process | Adobe's serverless runtime |
| Language | PHP | JavaScript / Node |
| How it hooks in | Observers, plugins, preferences | Events, webhooks, APIs |
| Upgrade risk | Coupled to core code | Decoupled from the release cycle |
| Magento Open Source | Yes | No (Commerce-only) |
| Adobe Commerce on Cloud / on-prem | Yes | Yes (2.4.4+) |
| Adobe Commerce as a Cloud Service | No | Yes, the only option |
What to learn next
If you're a Magento developer and want to stay ahead of where new Adobe Commerce work is going:
- learn how Commerce events and webhooks are named and configured, because the
observer.*andplugin.*names map straight back to the observers and service contracts you already know - get comfortable writing small, stateless JavaScript/Node services, and deploying them somewhere other than the store's own server
- on your next Commerce customization, ask "does this really need to live inside the application?" before reaching for a plugin
Every event and webhook name above is just an observer or service contract with a prefix, and that should be reassuring. Everything you know as a Magento developer about how the platform is wired still matters. You just need to know it well enough to work from the outside.
Is your team doing any out-of-process work yet, or is it still all modules? I'd love to hear what it looks like on real projects.



