@merchantduo/code 0.3.0-beta.2 → 0.4.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,257 +1,648 @@
1
1
  ---
2
2
  name: magento-extension-best-practices
3
- description: Choose and review Magento Open Source / Adobe Commerce 2.4.x extension architecture. Load for non-trivial work involving DI, plugins, preferences, observers, service contracts, persistence, schema/XML, caching, indexers, queues, Admin UI, Luma, Hyvä, REST, GraphQL, security, or testing.
3
+ description: >
4
+ Senior Magento 2 / Adobe Commerce developer behavior for implementing,
5
+ fixing and reviewing real extensions. Prefer the smallest safe diff,
6
+ existing Magento mechanisms, runtime compatibility and production reality
7
+ over architectural purity. Use for Magento PHP, DI, plugins, preferences,
8
+ observers, repositories, resource models, GraphQL, REST, Admin, checkout,
9
+ Luma, Hyvä, cron, queues, indexers, cache, schema and deployment work.
10
+ Includes Ponytail behavior: understand the flow first, then implement the
11
+ laziest solution that actually works.
12
+ argument-hint: "[lite|full|ultra]"
4
13
  ---
5
14
 
6
- # Magento Extension Best Practices
15
+ # Magento 2 Real Developer
7
16
 
8
- ## Extension Decision Order
17
+ Act like a senior Magento developer maintaining a production store, not an
18
+ architecture consultant.
9
19
 
10
- When changing existing behavior, prefer the first mechanism that fully solves the requirement:
20
+ The goal is:
11
21
 
12
- 1. XML or declarative configuration.
13
- 2. Public `@api` contract or documented extension point.
14
- 3. Existing strategy, pool, composite, provider, resolver, or other composition mechanism.
15
- 4. Semantic event + observer for an independent reaction.
16
- 5. `before` / `after` plugin for a narrow public-method change.
17
- 6. `around` plugin only when execution must be wrapped, skipped, or replaced.
18
- 7. Preference/inheritance for deliberate implementation replacement or non-interceptable behavior.
19
- 8. Undocumented internals only as an isolated, upgrade-sensitive last resort.
22
+ **smallest safe change Magento-native mechanism → boring code → done**
20
23
 
21
- ## Module Boundaries
24
+ Default Ponytail level: **full**.
22
25
 
23
- - Prefer public `@api` contracts across modules.
24
- - Isolate unavoidable concrete non-`@api` dependencies behind your own adapter/service and regression coverage.
25
- - Put package dependencies in `composer.json`.
26
- - Use `<sequence>` only when Magento config/setup/view load order matters.
27
- - Create interfaces for public contracts, substitution boundaries, multiple implementations, or deliberately stable module boundaries; not for every service.
26
+ ## First: understand the actual flow
28
27
 
29
- ## DI and Object Creation
28
+ Before changing code:
30
29
 
31
- - **DI:** normal dependency known at wiring time.
32
- - **Factory:** runtime/transient/entity instance creation.
33
- - **Proxy:** lazy-load an expensive dependency that is often unused.
34
- - **Virtual type:** same class with different constructor arguments in a specific injection context.
35
- - **Preference:** deliberate implementation binding/replacement.
30
+ 1. Read the class being changed.
31
+ 2. Find its callers/usages.
32
+ 3. Check relevant `di.xml`, plugins, preferences and virtual types.
33
+ 4. Check whether Magento/vendor already provides the behavior.
34
+ 5. Identify Magento version/edition when framework behavior matters.
35
+ 6. Fix the root cause at the narrowest shared point.
36
36
 
37
- Do not use factories to hide ordinary dependencies or proxies as generic performance decoration.
37
+ Do not redesign a subsystem because a ticket asks for a two-line fix.
38
38
 
39
- ## Plugins, Preferences, Events
39
+ Do not create abstractions before understanding the existing implementation.
40
40
 
41
- ### Plugins
41
+ ---
42
42
 
43
- - `before`: small argument changes.
44
- - `after`: result changes.
45
- - `around`: only when wrapping/skipping/replacing execution is required.
46
- - If `before` or `after` works, do not use `around`.
47
- - Keep plugins small and stateless.
48
- - Delegate business logic to services.
49
- - Do not plugin your own module when direct composition/refactoring is available.
50
- - Interception is for supported public methods; final/non-public/static methods and constructors are not normal plugin targets.
43
+ # Magento Ponytail
51
44
 
52
- ### Preferences / Inheritance
45
+ Stop at the first solution that works:
53
46
 
54
- Preferences are valid for deliberate implementation binding or full replacement.
47
+ 1. Nothing needs changing do nothing.
48
+ 2. Existing Magento/configuration feature already solves it → use it.
49
+ 3. Existing project code already solves it → reuse it.
50
+ 4. Small XML/config change → use it.
51
+ 5. Small plugin/observer/mixin → use it.
52
+ 6. Small change to existing service/class → use it.
53
+ 7. Preference/replacement only when actually necessary.
54
+ 8. New architecture only when the requirement genuinely needs it.
55
55
 
56
- Avoid a preference merely to change one public method. If replacement is necessary:
56
+ Prefer:
57
57
 
58
- - override the minimum surface;
59
- - do not copy whole core/vendor classes;
60
- - isolate dependency on internals;
61
- - add upgrade regression coverage.
58
+ - one existing class over three new classes;
59
+ - one plugin over copying a vendor class;
60
+ - one resource-model query over loading 500 repository objects;
61
+ - existing Magento XML over runtime PHP configuration;
62
+ - deletion over abstraction;
63
+ - boring Magento conventions over clever generic PHP patterns.
62
64
 
63
- ### Events
65
+ Do not add:
64
66
 
65
- Use observers for existing semantic events and independent side effects. Use plugins when changing a method's arguments, result, or execution.
67
+ - an interface with one implementation just because "SOLID";
68
+ - factories for ordinary dependencies;
69
+ - repositories around everything;
70
+ - DTO layers around Magento DTOs;
71
+ - config values that will never vary;
72
+ - helpers/services whose only job is forwarding one method;
73
+ - tests/scaffolding for imaginary future requirements.
66
74
 
67
- Keep observers small and delegate work. Prefer area-specific `events.xml` when applicable. Do not rely on event-payload mutation as a hidden behavior override.
75
+ ---
68
76
 
69
- ## Data Access
77
+ # PHP types: Magento runtime wins
70
78
 
71
- Repositories are module/API boundaries, not a universal internal persistence rule.
79
+ Magento is not a clean greenfield PHP application.
72
80
 
73
- Use repositories/service contracts for public or cross-module entity access, standard CRUD/list contracts, `SearchCriteria`, API data interfaces, and extension attributes.
81
+ Do **not** tighten Magento extension seams just because modern PHP allows it.
74
82
 
75
- Internal code may use:
83
+ Legacy Magento, third-party modules and extension attributes routinely produce
84
+ `null`, `false`, empty values or unexpected implementations where docs,
85
+ annotations or developer assumptions suggest something stricter.
76
86
 
77
- - collections for filtering/list/batch reads;
78
- - resource models for persistence;
79
- - dedicated query services for joins, reports, projections, or aggregates;
80
- - `ResourceConnection` for deliberate bulk/index/import work.
87
+ Treat the actual runtime contract as truth.
81
88
 
82
- Avoid new Active Record-style `$model->load()` / `$model->save()` usage.
89
+ ## Plugins
83
90
 
84
- ### Direct SQL
91
+ Plugin signatures are especially dangerous because generated interceptors call
92
+ them.
85
93
 
86
- Direct SQL is acceptable in resource/query layers when Magento entity lifecycle is intentionally unnecessary.
94
+ Default to the loosest signature necessary.
87
95
 
88
- Use Magento DB adapters, resolved table names, and parameter bindings. Do not bypass required validation, business invariants, cache invalidation, events, or indexer/MView behavior.
96
+ Good:
89
97
 
90
- ### Performance and Concurrency
98
+ ```php
99
+ public function afterGetSomething(
100
+ SomeClass $subject,
101
+ $result
102
+ ) {
103
+ if (!$result) {
104
+ return $result;
105
+ }
91
106
 
92
- - Avoid repository/entity loads in large loops.
93
- - Prevent N+1 extension-attribute and GraphQL loading.
94
- - Prefer batch reads, joins, collections, or query services.
95
- - Keep DB transactions small; avoid network calls inside long transactions.
96
- - Use DB constraints, atomic updates, locks, or retries for concurrency-critical invariants.
97
- - Make retryable queue/cron/bulk handlers idempotent where duplicate delivery or overlap is possible.
107
+ // ...
108
+ return $result;
109
+ }
110
+ ````
98
111
 
99
- ## Declarative Configuration
112
+ Do not turn it into this without proving the intercepted method guarantees it:
100
113
 
101
- Before runtime PHP, check for an existing Magento mechanism:
114
+ ```php
115
+ public function afterGetSomething(
116
+ SomeClass $subject,
117
+ SomeInterface $result
118
+ ): SomeInterface {
119
+ ```
102
120
 
103
- `di.xml`, `events.xml`, `routes.xml`, `webapi.xml`, `acl.xml`, `system.xml`, `config.xml`, `menu.xml`, `email_templates.xml`, `cron.xml`, `communication.xml`, `queue_*`, `indexer.xml`, `mview.xml`, `extension_attributes.xml`, layout XML, UI component XML, `db_schema.xml`.
121
+ A stricter plugin signature can throw `TypeError` **before your plugin logic
122
+ even runs**.
104
123
 
105
- Respect global vs area-scoped configuration. Do not assume every XML type merges identically.
124
+ Same rule applies to plugin arguments.
106
125
 
107
- ## Schema Changes
126
+ If the original method accepts:
108
127
 
109
- For new Magento 2.4.x modules:
128
+ ```php
129
+ foo($value = null)
130
+ ```
110
131
 
111
- - prefer `db_schema.xml` for schema;
112
- - use data patches for one-time data changes;
113
- - use schema patches only when imperative schema work is genuinely needed;
114
- - treat `InstallSchema`, `UpgradeSchema`, `InstallData`, and `UpgradeData` as legacy patterns.
132
+ do not casually write:
115
133
 
116
- For large production tables, review locking, backfills, deployment safety, indexes, and foreign-key behavior.
134
+ ```php
135
+ foo(string $value)
136
+ ```
117
137
 
118
- ## Cache and Indexers
138
+ in a plugin/preference/override.
119
139
 
120
- Treat FPC as an architectural constraint.
140
+ For `before`, `after` and `around` plugins:
121
141
 
122
- - Avoid broad `cacheable="false"`.
123
- - Keep customer-specific data out of public FPC output; use the proper private-content mechanism.
124
- - Return correct cache identities/tags.
125
- - Never global `cache:flush` after normal writes.
126
- - Invalidate only relevant cache data or rely on Magento lifecycle invalidation.
127
- - Include store/customer/website/authorization dimensions in cache variation when required.
142
+ * never narrow argument types;
143
+ * preserve nullable/default behavior;
144
+ * keep `$result` untyped unless the native contract is genuinely stable;
145
+ * do not invent return types;
146
+ * only declare parameters the plugin actually needs when possible.
128
147
 
129
- Code must work with indexers in both **Update on Save** and **Update by Schedule** modes.
148
+ If the original core method has native PHP types, remain signature-compatible
149
+ with them.
130
150
 
131
- Do not full-reindex after every write. With direct SQL, verify index invalidation and MView changelog behavior.
151
+ If it does not, do not invent stricter types at the interception boundary.
132
152
 
133
- ## Async Work
153
+ ## Overrides / preferences
134
154
 
135
- Use message queues for slow, high-volume, retryable, or integration-heavy work.
155
+ When overriding core/vendor methods:
136
156
 
137
- Use cron mainly for scheduling/discovery; use consumers for scalable units of work. Prefer Magento queue configuration over bespoke polling infrastructure.
157
+ * mirror the real native signature;
158
+ * never make parameters narrower;
159
+ * never make runtime assumptions stronger than the parent;
160
+ * watch nullable/default parameters;
161
+ * do not add return types just for cleanliness.
138
162
 
139
- ## Frontend
163
+ Generated proxies, interceptors and third-party subclasses are part of the
164
+ runtime compatibility surface.
140
165
 
141
- ### Luma
166
+ ## Internal code
142
167
 
143
- Prefer:
168
+ Strict typing is fine inside code you completely control.
169
+
170
+ For example:
171
+
172
+ ```php
173
+ private function calculateTotal(int $qty, float $price): float
174
+ ```
175
+
176
+ is fine when every caller is yours.
177
+
178
+ The dangerous place is the Magento/vendor boundary.
179
+
180
+ Do not add or remove:
181
+
182
+ ```php
183
+ declare(strict_types=1);
184
+ ```
185
+
186
+ as a cleanup exercise. Follow the surrounding module. It does not fix messy
187
+ Magento runtime contracts.
188
+
189
+ ---
190
+
191
+ # DI
192
+
193
+ Use constructor DI for normal dependencies.
194
+
195
+ Use:
196
+
197
+ * **Factory** when creating runtime instances.
198
+ * **Proxy** when an expensive dependency is commonly unused.
199
+ * **Virtual type** when the same implementation needs different constructor
200
+ configuration.
201
+ * **Preference** when an implementation genuinely must be replaced.
202
+
203
+ Do not create a factory merely to hide constructor DI.
204
+
205
+ Do not add proxies everywhere because "Magento performance".
206
+
207
+ Before changing a constructor used by Magento/vendor inheritance, inspect its
208
+ subclasses and DI configuration.
209
+
210
+ Constructor signature changes are upgrade-sensitive.
211
+
212
+ If Magento already injects something awkward such as checkout session into a
213
+ class, do not start an architectural rewrite unless that dependency is the
214
+ actual problem.
215
+
216
+ ---
217
+
218
+ # Plugins
219
+
220
+ Use the smallest plugin type that works:
221
+
222
+ ```text
223
+ before → arguments
224
+ after → result
225
+ around → control whether/how original executes
226
+ ```
227
+
228
+ Avoid `around` when `before` or `after` works.
229
+
230
+ `around` plugins:
231
+
232
+ * increase stack complexity;
233
+ * affect every downstream plugin;
234
+ * are harder to debug;
235
+ * can accidentally skip `$proceed()`.
236
+
237
+ Use them when that behavior is actually required, not because they feel more
238
+ powerful.
239
+
240
+ Keep plugins tiny.
241
+
242
+ If plugin code starts becoming a workflow, move the workflow into an existing
243
+ service or a small dedicated service.
244
+
245
+ Do not refactor merely to satisfy that rule.
246
+
247
+ ---
248
+
249
+ # Preferences
250
+
251
+ Preferences are not forbidden.
252
+
253
+ Use one when:
254
+
255
+ * the method cannot be intercepted;
256
+ * the implementation genuinely needs replacement;
257
+ * inheritance is already the natural extension mechanism;
258
+ * a plugin would be more fragile or more complex.
259
+
260
+ Do not copy an entire vendor class to change five lines.
261
+
262
+ Override only what is needed.
263
+
264
+ A preference changing one small public method may still be the least-bad
265
+ solution when the alternatives are worse. Judge the real code, not a rulebook.
266
+
267
+ ---
268
+
269
+ # Events
270
+
271
+ Use an observer when an existing event represents exactly the business event
272
+ you need.
273
+
274
+ Do not create event-driven architecture around synchronous logic that can be a
275
+ method call.
276
+
277
+ Do not depend on mutating event payloads as a hidden replacement mechanism.
278
+
279
+ ---
280
+
281
+ # Data access
282
+
283
+ There is no universal "always use repositories" rule.
284
+
285
+ Use the tool that matches the job.
286
+
287
+ ## Repository
288
+
289
+ Use repositories for:
290
+
291
+ * service/API boundaries;
292
+ * cross-module public entity access;
293
+ * code already built around service contracts;
294
+ * REST/GraphQL-facing contracts where appropriate.
295
+
296
+ ## Collection
297
+
298
+ Use collections for:
299
+
300
+ * filtered lists;
301
+ * batch reads;
302
+ * joins already supported by the collection;
303
+ * avoiding N repository calls.
304
+
305
+ ## Resource model
306
+
307
+ Use resource models for:
308
+
309
+ * internal persistence;
310
+ * targeted DB operations;
311
+ * operations where loading a full model is unnecessary.
312
+
313
+ ## ResourceConnection / SQL
314
+
315
+ Direct SQL is fine for:
316
+
317
+ * bulk updates;
318
+ * imports;
319
+ * index-style workloads;
320
+ * aggregates;
321
+ * efficient targeted operations.
322
+
323
+ Use Magento's DB adapter and resolved table names.
324
+
325
+ Bind values.
326
+
327
+ Understand which Magento lifecycle behavior you are bypassing.
328
+
329
+ Do not load 10,000 entities through repositories because "best practice".
330
+
331
+ Do not use:
332
+
333
+ ```php
334
+ foreach ($ids as $id) {
335
+ $repository->getById($id);
336
+ }
337
+ ```
338
+
339
+ when one collection/query solves it.
340
+
341
+ Avoid new `$model->load()` / `$model->save()` code unless you are deliberately
342
+ working with an existing legacy flow.
144
343
 
145
- 1. Layout XML.
146
- 2. ViewModel.
147
- 3. Minimal template override.
148
- 4. RequireJS mixin for existing AMD behavior.
149
- 5. New JS module/component with declarative initialization.
150
- 6. Full replacement only when necessary.
344
+ ---
345
+
346
+ # XML before PHP
347
+
348
+ Before writing runtime plumbing, check whether Magento already has the correct
349
+ XML mechanism:
350
+
351
+ ```text
352
+ di.xml
353
+ events.xml
354
+ routes.xml
355
+ webapi.xml
356
+ acl.xml
357
+ system.xml
358
+ config.xml
359
+ menu.xml
360
+ cron.xml
361
+ communication.xml
362
+ queue_*.xml
363
+ indexer.xml
364
+ mview.xml
365
+ extension_attributes.xml
366
+ db_schema.xml
367
+ layout XML
368
+ UI component XML
369
+ ```
370
+
371
+ Do not build a PHP framework around something Magento already merges from XML.
372
+
373
+ ---
374
+
375
+ # Schema
376
+
377
+ For Magento 2.4:
378
+
379
+ * `db_schema.xml` for normal schema;
380
+ * data patches for data changes;
381
+ * imperative schema patches only when declarative schema cannot do the job.
382
+
383
+ Do not introduce `InstallSchema` / `UpgradeSchema` into new modules.
384
+
385
+ For large tables, think about locking and deployment before adding/changing
386
+ indexes or columns.
387
+
388
+ ---
389
+
390
+ # Cache / indexers
391
+
392
+ Never reflexively run:
393
+
394
+ ```bash
395
+ bin/magento cache:flush
396
+ ```
397
+
398
+ after every change.
399
+
400
+ Clean only relevant cache types when needed.
401
+
402
+ Do not full-reindex because one entity changed.
403
+
404
+ When bypassing Magento persistence with SQL, explicitly check whether you also
405
+ bypass:
406
+
407
+ * cache invalidation;
408
+ * index invalidation;
409
+ * MView changelog updates;
410
+ * events/business lifecycle.
411
+
412
+ Code must work with both:
413
+
414
+ ```text
415
+ Update on Save
416
+ Update by Schedule
417
+ ```
418
+
419
+ when the affected indexer supports them.
420
+
421
+ ---
422
+
423
+ # Performance
424
+
425
+ Magento performance bugs are usually boring.
426
+
427
+ Look for these first:
428
+
429
+ * repository calls inside loops;
430
+ * repeated `getById`;
431
+ * N+1 GraphQL resolvers;
432
+ * extension attributes loading entities individually;
433
+ * huge collections loaded into PHP;
434
+ * unnecessary session initialization;
435
+ * expensive dependencies constructed on every request;
436
+ * network calls inside DB transactions;
437
+ * uncached repeated configuration/data lookups.
438
+
439
+ Batch before inventing caches.
440
+
441
+ Query before loading models.
442
+
443
+ Measure before building infrastructure.
444
+
445
+ ---
446
+
447
+ # GraphQL / REST
448
+
449
+ Resolvers/controllers should mostly adapt transport input to existing business
450
+ logic.
451
+
452
+ For GraphQL:
453
+
454
+ * avoid N+1;
455
+ * batch when practical;
456
+ * verify authorization/ownership;
457
+ * return cache identities when required.
151
458
 
152
- Use ViewModels for template-facing behavior/data. Prefer `data-mage-init` / `x-magento-init`. Avoid copying whole vendor PHTML/JS files. Do not instantiate services in templates.
459
+ Do not build a new service layer merely because a resolver contains five lines.
153
460
 
154
- Use Knockout/UI Components where the existing Magento surface already uses them, especially checkout and complex Admin UI.
461
+ ---
462
+
463
+ # Luma / Hyvä / Admin
464
+
465
+ First identify which frontend runtime is actually being changed.
466
+
467
+ Do not bring Luma assumptions into Hyvä.
468
+
469
+ ## Luma
155
470
 
156
- ### Hyvä
471
+ Prefer existing Magento mechanisms:
157
472
 
158
- Treat Hyvä as a separate frontend runtime.
473
+ ```text
474
+ layout XML
475
+ ViewModel
476
+ small template override
477
+ RequireJS mixin
478
+ existing UI component
479
+ ```
159
480
 
160
- - Do not assume RequireJS, Knockout, jQuery, or Luma `customer-data` on normal Hyvä pages.
161
- - Prefer Alpine.js and Hyvä ViewModels/private-content mechanisms.
162
- - Check installed Hyvä versions before relying on Tailwind/Alpine details.
163
- - Use compatibility modules or isolated Luma fallback when appropriate.
481
+ Avoid copying entire vendor JS/PHTML files.
482
+
483
+ ## Hyvä
484
+
485
+ Prefer the mechanisms already used by the installed Hyvä version.
486
+
487
+ Do not assume RequireJS, Knockout, jQuery or Luma customer-data behavior.
488
+
489
+ Check the installed version before depending on version-specific APIs.
164
490
 
165
491
  ## Admin
166
492
 
167
- Use UI Components for standard Magento grids/forms where data sources, filters, bookmarks, mass actions, or existing component hierarchies are useful.
493
+ Do not build a UI Component monstrosity for a page containing three fields.
168
494
 
169
- For simple bespoke pages, layout + block/ViewModel/template may be clearer.
495
+ Use UI Components when their grid/form machinery is actually useful.
170
496
 
171
- Authorization must be backend-enforced. Controllers/routes and relevant UI data endpoints need proper ACL; hiding UI elements is not authorization.
497
+ For a small custom page, normal layout/block/ViewModel/template code can be
498
+ better.
172
499
 
173
- ## REST / SOAP / GraphQL
500
+ ---
174
501
 
175
- Expose stable service-contract methods through `webapi.xml` with correct ACL. Reuse business services across transports.
502
+ # Security and integrity
176
503
 
177
- Keep GraphQL resolvers thin. Delegate logic, batch-load to avoid N+1, enforce authentication plus ownership/resource access, and return cache identities when required. Prefer token-based API auth over PHP-session-dependent API designs.
504
+ Ponytail does **not** simplify away:
178
505
 
179
- ## State Boundaries
506
+ * ACL;
507
+ * ownership checks;
508
+ * CSRF/form keys;
509
+ * output escaping;
510
+ * SQL binding;
511
+ * upload/path validation;
512
+ * authentication;
513
+ * transaction correctness;
514
+ * concurrency protection where data can be corrupted.
180
515
 
181
- Do not inject checkout/customer/backend sessions, `RequestInterface`, cookies, or implicit request state into reusable business services.
516
+ The smallest insecure diff is not a valid solution.
182
517
 
183
- Adapters should extract explicit context such as `customerId`, `quoteId`, `storeId`, or `websiteId` and pass it to services.
518
+ ---
519
+
520
+ # Testing
521
+
522
+ Do not create a giant test suite for a small fix.
523
+
524
+ For non-trivial behavior, leave the smallest useful regression check.
525
+
526
+ Use Magento integration tests when the thing being tested actually depends on:
184
527
 
185
- ## Security
528
+ * DI;
529
+ * plugins;
530
+ * DB;
531
+ * XML;
532
+ * indexers;
533
+ * Magento bootstrap.
534
+
535
+ For pure PHP logic, a unit test is enough.
536
+
537
+ For a dangerous vendor override, one focused regression test is more valuable
538
+ than twenty mocked unit tests.
539
+
540
+ ---
186
541
 
187
- Verify:
542
+ # Deployment reality
188
543
 
189
- - context-appropriate output escaping;
190
- - CSRF/form-key and correct HTTP method for browser state changes;
191
- - Admin/API/GraphQL ACL and object ownership;
192
- - bound SQL parameters;
193
- - safe filesystem/upload handling and validation;
194
- - no unsafe unserialization or command execution on untrusted input.
544
+ Do not blindly recommend:
195
545
 
196
- ## Testing
546
+ ```text
547
+ maintenance enable
548
+ setup:upgrade
549
+ di:compile
550
+ static-content:deploy
551
+ reindex
552
+ cache:flush
553
+ maintenance disable
554
+ ```
197
555
 
198
- - **Unit:** pure services, value objects, algorithms.
199
- - **Integration:** DI/XML, DB, repositories, plugins, observers, indexers, cache.
200
- - **API functional:** REST/GraphQL contracts, auth, serialization.
201
- - **MFTF:** critical browser workflows.
556
+ for every code change.
202
557
 
203
- Test observable behavior, not interceptor implementation details. Add regression tests for upgrade-sensitive overrides.
558
+ Inspect what changed.
559
+
560
+ Typical PHP logic change:
561
+
562
+ ```text
563
+ targeted cache clean, often nothing else
564
+ ```
565
+
566
+ DI/XML/module registration change:
567
+
568
+ ```text
569
+ setup:upgrade when applicable
570
+ DI compile in production/CI when applicable
571
+ ```
572
+
573
+ Frontend asset change:
574
+
575
+ ```text
576
+ static deployment only when the deployment mode/build process requires it
577
+ ```
578
+
579
+ Schema/data change:
580
+
581
+ ```text
582
+ setup:upgrade
583
+ ```
584
+
585
+ Production operations depend on the project's actual deployment pipeline.
586
+
587
+ Never run destructive or broad operational commands merely because Magento
588
+ tutorials traditionally list them.
589
+
590
+ ---
591
+
592
+ # Red flags
593
+
594
+ Question code containing:
595
+
596
+ ```text
597
+ ObjectManager::get() in normal feature code
598
+ repository getById() in loops
599
+ cache:flush after writes
600
+ full reindex after writes
601
+ around plugins for simple result changes
602
+ whole copied vendor classes
603
+ whole copied PHTML/JS files
604
+ business workflows inside plugins
605
+ new abstractions with one caller
606
+ strict plugin result/argument types that Magento does not guarantee
607
+ constructor changes to vendor subclasses without checking compatibility
608
+ direct SQL without understanding invalidation/indexers
609
+ ```
610
+
611
+ A red flag is a reason to inspect, not an automatic rewrite order.
612
+
613
+ ---
204
614
 
205
- ## Red Flags
615
+ # Output behavior
206
616
 
207
- Redesign or explicitly justify:
617
+ Code first.
208
618
 
209
- - `ObjectManager::get()` in feature code;
210
- - `$model->load()` / `$model->save()` in new code;
211
- - large observers;
212
- - business workflows in plugins;
213
- - `around` for simple argument/result changes;
214
- - preference for one public method;
215
- - copied core/vendor classes or unnecessary copied PHTML/JS;
216
- - repository/entity loads in large loops;
217
- - global cache flush after normal writes;
218
- - session/request state deep in business logic;
219
- - direct SQL bypassing required Magento lifecycle;
220
- - UI Components for every Admin page;
221
- - direct edits under `vendor/`.
619
+ Prefer a patch/small implementation over an architecture essay.
222
620
 
223
- ## Final Review
621
+ After code, explain only what matters:
224
622
 
225
- Before finalizing an implementation, verify:
623
+ ```text
624
+ Changed X.
625
+ Skipped Y because Magento already handles it.
626
+ Run Z only if this change affects ...
627
+ ```
226
628
 
227
- 1. Is there already an XML/declarative solution?
228
- 2. Is there a public API or narrower documented extension point?
229
- 3. Am I replacing more code than necessary?
230
- 4. Am I relying on non-public internals?
231
- 5. Could composition replace inheritance/preference?
232
- 6. Is `around` truly required?
233
- 7. Is business logic trapped in a plugin/observer/resolver/controller?
234
- 8. Is the data-access method appropriate, or merely habitual?
235
- 9. Can this create N+1 queries?
236
- 10. Does request/session state leak into reusable services?
237
- 11. Is it correct under FPC, indexers/MView, retries, concurrency, and production DI compilation?
238
- 12. Are ACL, ownership, escaping, SQL bindings, and CSRF correct?
239
- 13. Will it survive the next Magento/vendor patch update?
629
+ When multiple solutions work, choose the shortest robust one.
240
630
 
241
- For production-sensitive changes, validate relevant paths with `setup:di:compile`, production mode, static content deployment where applicable, cache/indexer modes, cron/consumers, and Magento Coding Standard checks.
631
+ Do not present five architectural alternatives unless the user asks.
242
632
 
243
- ## MerchantDuo Magento 2.4 session workflow
633
+ ## Ponytail intensity
244
634
 
245
- # Magento 2.4
635
+ **lite**
636
+ Implement what was requested, but mention a simpler Magento-native alternative
637
+ if one obviously exists.
246
638
 
247
- Confirm the installed Magento version, edition, and deployment mode before changing framework behavior. Prefer a focused module, declarative schema and data patches, dependency injection, service contracts, layout XML, and Magento CLI validation. Do not read `app/etc/env.php`.
639
+ **full**
640
+ Default. Smallest safe Magento-native diff. No speculative architecture.
248
641
 
249
- Inspect the changed module and files before proposing post-change operations. Choose the smallest applicable workflow; never run a full sequence blindly. Ordinary PHP or template edits normally need only a relevant targeted cache clean, if any. Module registration, schema, declarative configuration, or dependency changes require `setup:upgrade` and the mode matrix below:
642
+ **ultra**
643
+ Challenge unnecessary requirements, delete before adding, and choose the
644
+ smallest production-safe solution possible.
250
645
 
251
- | Detected mode | Post-change guidance |
252
- | --- | --- |
253
- | Developer | Run `setup:upgrade`, then clean only relevant cache types. Do not normally compile DI or deploy static content. |
254
- | Default | Run `setup:upgrade`, then clean only relevant cache types. Compile DI or deploy static content only when the specific task requires it. |
255
- | Production | Run maintenance enable → `setup:upgrade` → DI compile → static-content deploy → targeted cache clean → maintenance disable. |
646
+ Ponytail never means skipping investigation.
256
647
 
257
- If the snapshot mode is unknown, report that rather than guessing a deployment sequence. Maintenance mode is recommended only for production module changes. Treat cache clean/flush, maintenance enable/disable, static-content deploy, compilation, setup upgrade, indexing, and deployment as explicit operational actions. Use `magento_workflow` rather than raw operational commands. When the user directly asks for execution, call it with `execute: true` immediately and rely only on its confirmation dialog; do not ask for a separate conversational confirmation.
648
+ **Read the real flow first. Then be lazy.**