Articles
Written for decision moments, not for traffic.
Editorial standard for everything here: if a piece contains nothing checkable, it doesn’t publish.
Decision guides
Start with the decision you’re facing.
These live as standing pages we keep current, not dated posts — the advice changes when the facts do.
Your agency left Magento. Now what?
Agency-abandonment triage: continuity, patch status, repo custody.
Is renewing Adobe Commerce worth it?
Exit economics, straight: when staying is right, and what leaving costs.
Hyvä performance, with receipts
Field data from real rebuilds, not vendor benchmarks.
Engineering Notes
Short action logs
Field notes from real closed tickets: the symptom, the cause, the fix, and the outcome.
2026-09-28
Free gifts, quantity squared: when three correct modules disagree
promotions
checkout
hyva
mage-os
- Symptom
- A store that runs free-gift promotions upgraded to the latest Mage-OS (Magento 2.4.9), Hyvä Checkout, and Amasty's Free Gift extension. Gifts started misbehaving: extra free items appeared in the cart and then vanished, some carts emptied themselves partway through checkout, and gift lines reached orders at their quantity squared. 7 became 49, and 36 became 1,296.
- Cause
- None of the three modules was wrong on its own; it took the latest release of all three together. Magento 2.4.9 changed how products are cached as they load, so core and the free-gift extension now end up holding the same product object instead of separate copies. Core marks that shared object with configurable-product details meant for somewhere else, and the gift enters the cart carrying them, as a line tied to itself that is hidden from view and missed by the extension's own checks. Hyvä Checkout then saves the cart at every step, as it should, and each save reprocesses that line: gifts get re-added, paid lines of the same product merge into gift lines, and the extension removes the merged line as an invalid gift. Roll back any one of the three and the chain breaks.
- Fix
- Rather than patch one vendor's code to suit another, we fixed the interactions between them. A product entering the cart gets an isolated copy only when it carries those stray details; everything else passes through untouched. Gift lines and paid lines no longer count as the same item, even for the same product. A one-time cleanup repaired carts that were already affected.
- Measurement
- Two small plugins fixed it, in time for the next promotion.
2026-09-22
Upgrading to 2.4.9: the breakage is in the dependencies
mage-os
upgrades
maintenance
- Symptom
- Across our fleet's moves to Mage-OS 3.4 and 3.5 (Magento 2.4.9 baseline), problems ranged from loud to silent. Common issues:
- Cause
- Core had updated its libraries and third-party code hadn't caught up. Symfony Console 7 requires an int return type on execute(), so one untyped console command anywhere kills the CLI as soon as it loads. less.php 5 changed its default math mode, so bare division inside ceil() no longer evaluates. jQuery UI 1.14 deleted a core module and its RequireJS alias. An extension that still named it made RequireJS fetch one file under two IDs, bind it to one, and wait on the other forever, with no error anywhere. Zend_Http_Client is gone entirely. And a new DI argument on a core rule class was inherited, by name, by a subclass that used the same name for a different type.
- Fix
- Where a fixed release existed, we bumped to it, after diffing it to confirm the change was additions only. Where the package was abandoned (two modules last released in 2022 and 2023), we patched it. We re-declared the RequireJS shim without the deleted module, parenthesized the LESS division, moved the HTTP client to Laminas, and fixed the DI argument conflict. Then we checked every custom class against the new vendor tree to catch signature errors before a customer could.
- Measurement
- Five kinds of breakage across five stores, each traced to a single mechanism rather than worked around. In all cases, QA and CI tests caught it before it reached a merchant.
2026-09-09
Composer's -a flag can't see Magento's generated code on old versions
composer
deployment
performance
- Symptom
- We switched our fleet to use composer's authoritative classmap, one step beyond normal optimizing. On a site running Magento 2.4.7, the next deployment failed with a missing generated class error.
- Cause
- With an authoritative classmap, Composer's loader answers "not found" for any class missing from the classmap before it ever checks the PSR-4 maps. Magento registers generated/code with that loader at runtime, which is exactly the path -a shuts off. So under -a no generated class loads at all: not the factories, proxies, or interceptors. Bootstrap fails on whichever one compiled DI happens to ask for first.
- Fix
- Add generated/code/ to the project's autoload.psr-0 so Composer scans the setup:di:compile output into the classmap, and pin the build order: composer install, patch:apply, setup:di:compile, then composer dump-autoload -a --no-dev. Magento made this same change starting in 2.4.8.
- Measurement
- Verified in an isolated project: with the entry, generated classes land in autoload_classmap.php under -a, and without it none do. Easy fix.
2026-08-01
Seven years on the same store
maintenance
stewardship
- Symptom
- Case studies get written about launches and rescues, so those look like the only two states a store can be in. Nobody publishes the store that just kept running.
- Cause
- The work that keeps a store off the rescue list is individually unremarkable — patches within days of disclosure, module updates, ERP sync fixes, small UX changes — and it only compounds if someone does it steadily for years.
- Fix
- We keep doing it. One B2B store carries 3,468 commits of continuous maintenance from 2018 through 2024; two more run unbroken from 2019 and 2020 to today.
- Measurement
- Client stores with six-to-eight-year continuous histories; none has been replatformed.
2026-07-15
The customer grid meets MySQL's 16-column FULLTEXT cap
mysql
erp
admin
- Symptom
- Admin customer grid reindex failing after an ERP integration added sync-ID attributes to the grid.
- Cause
- Magento folds every searchable grid column into one InnoDB FULLTEXT index, and MySQL caps FULLTEXT indexes at 16 columns. Nothing warns you; the 17th searchable attribute simply breaks the index.
- Fix
- Excluded ERP sync IDs from FULLTEXT indexing — they're exact-match keys, not search terms, so they never belonged in it.
- Measurement
- Grid search and reindex stable since; sync IDs still filterable exact-match.
2026-07-10
Platform upgrades as a fleet process, not annual projects
mage-os
maintenance
process
- Symptom
- A new platform release lands and the store owner braces for a project: scoping, a quote, a multi-week branch, a risky deploy weekend.
- Cause
- The cost was never the release itself; it was treating each store's upgrade as a bespoke engagement with its own plan and its own definition of "looks fine to me."
- Fix
- Upgrades run as one batch across the fleet: Mage-OS 3.0 tracking issues opened across nine-plus client repositories the same day, with identical CI process (builds, static analysis, store smoke tests) proving each upgrade PR the same way on every store.
- Measurement
- One store's cadence this year: 2.2.1 in March, 2.3.0 in May, security patches between — as routine maintenance, not fires.
2026-04-01
Authorize.net still doesn't speak IPv6. In 2026.
payments
networking
- Symptom
- A merchant's payment stack couldn't follow their infrastructure onto IPv6: Authorize.net's API remains IPv4-only, and IPv6 customer addresses don't survive its request fields.
- Cause
- Gateway limitation, not a store bug. No IPv6 support, no timeline published for adding them.
- Fix
- Migrated the merchant to CyberSource — same Visa family, so tokenized customer profiles had a migration path — and added full IPv6 support to our CyberSource module's IP validation and sanitization (released April 2026).
- Measurement
- IPv6 traffic transacting cleanly since cutover.
2026-03-10
The compromise that lasted 85 minutes
security
incident-response
maintenance
- Symptom
- At 7:25 one February morning, an FTP login from an unfamiliar country uploaded a PHP shell to a store we maintain, modified two Magento core files, and dropped a database browser alongside. The goal, from what they touched, was checkout card skimming.
- Cause
- A legacy FTP account, one of five on the server, most unused for years. Whether the credentials were guessed or harvested elsewhere, the lesson is the same: every dormant account is standing attack surface.
- Fix
- Detection did its half: an hourly Sansec malware scan flagged the new files 42 minutes after they landed. Maintenance mode, every attacker file removed, core files restored, IP blocked. Site clean and back online at minute 85. Then prevention: all FTP accounts disabled and reissued only on demonstrated need; the encryption key rotated; the admin list cut from thirty-some accounts to the dozen in use; and PCI 4.0 password and inactivity enforcement deployed.
- Measurement
- 42 minutes from shell upload to detection; 85 minutes from upload to clean and back online. The skimming attempt failed; it tampered with a payment integration the store doesn't use.
2026-01-17
Leaving Adobe Commerce: what actually breaks (very little)
mage-os
migration
- Symptom
- A Commerce store moving to Mage-OS loses the Commerce-only features it actually used — here, target rules (related-product rules) and a couple of staging-schema leftovers (row_id) baked into custom code.
- Cause
- Years of customization quietly assuming Commerce internals. The list is always shorter than merchants fear, but it's never zero.
- Fix
- Built a drop-in target-rule replacement module and deployed it disabled ahead of the cutover; cleaned row_id/store-scope assumptions out of CMS seed patches. The composer-level platform swap itself executed over two days.
- Measurement
- Store has taken three routine Mage-OS version updates since migration with no Commerce-related regressions.
2025-06-30
What inheriting a Magento store actually involves
onboarding
maintenance
- Symptom
- A subscription store arrived with years of prior-team work — a heavy ERP integration, a custom subscription engine — and production that had drifted from the repo in ways nobody had documented.
- Cause
- Nothing in the repo said what was actually deployed. Untracked changes lived only on production, security was multiple patches behind, and PHP 8 incompatibilities and dead modules had accumulated unnoticed.
- Fix
- Two months of baseline before any feature: reconcile production drift back into git, update Magento to be current on security patches, fix everything static inspection surfaced, and remove dead modules and their dependencies. Feature work started once things were clean and ready.
- Measurement
- 103 commits in year one, every one of them building on a repo that finally matched production.
2025-03-31
A Hyvä re-front is an integration inventory, not a theme
hyva
frontend
performance
- Symptom
- Everyone agrees the frontend needs replacing, and someone has already found Hyvä. The quote everyone expects is for a theme. The work that shows up is not a theme.
- Cause
- Hyvä replaces Magento's entire frontend stack, so nothing a third party shipped for the old one survives: loyalty widgets, promo banners, brand pages, payment buttons, and gift card templates all need native re-implementation — and some modules can't make the trip at all.
- Fix
- Inventory every frontend integration before quoting, rebuild them natively on Hyvä while the backend and checkout stay put, and delete the old theme's leftovers instead of leaving them to rot. On one cutover the schedule was dominated by a single vendor's module suite.
- Measurement
- One launch: median page load 6.5s to 1.9s the night of cutover; CrUX good-LCP share 52% to 88% the following month.
The standard
No thought-leadership filler. Real work, real results.