Applying Configurations
Overview
When customizing Maximo Mobile applications, the method you choose for applying configurations has significant implications for maintainability, upgrade compatibility, and long-term support. This guide explains the recommended approaches and why certain practices should be avoided.
There are three primary methods for applying configurations:
- Using AppCustomizations.js (✅ Recommended)
- Extending base JavaScript classes (✅ Recommended for complex scenarios)
- Directly modifying base controllers (❌ Not Recommended)
Understanding when and how to use each approach is critical for creating maintainable customizations that survive version upgrades.
Why This Matters:
The configuration approach you choose directly impacts:
- Upgrade success - Whether your customizations survive version updates
- Maintenance effort - How much work is required to maintain customizations
- Technical debt - Long-term cost of supporting your customizations
- Team productivity - How easily others can understand and modify your work
Method 1: Using AppCustomizations.js (Recommended)
Overview
The AppCustomizations.js file is the primary and recommended location for all application customizations. This file is specifically designed to persist through Maximo Mobile upgrades, making it the safest place for your custom logic.
The AppCustomizations.js file acts as a global controller for your entire application. Any event handler that can be defined in datasources, dialogs, pages, or the application itself can be implemented within AppCustomizations.js. This includes:
- Application lifecycle events (
applicationInitialized,applicationResumed) - Page lifecycle events (
pageInitialized,pageResumed,pagePaused) - Dialog lifecycle events (
dialogInitialized,dialogOpened,dialogClosed) - Datasource events (
onDatasourceInitialized,onAfterLoadData,onBeforeLoadData) - Custom event handlers defined in your XML configuration
This centralized approach means you can intercept and customize any event in your application from a single, upgrade-safe location.
Why Use AppCustomizations.js
Benefits:
- ✅ Upgrade-safe - This file carries over during version upgrades
- ✅ Centralized - All customizations in one location for easy maintenance
- ✅ Global scope - Can handle events from any component in the application
- ✅ Clear separation - Distinguishes custom code from base framework code
- ✅ Event interception - Can intercept any event before or after base controllers
Trade-offs:
- ⚠️ May require adjustments when APIs are deprecated between versions
- ⚠️ Developers need to monitor release notes for API changes
- ⚠️ Functions may need refactoring if underlying framework behavior changes
- ⚠️ Can grow quite large as a singular file, making readability and navigation challenging
Basic Structure
The AppCustomizations.js file follows a standard class-based structure with lifecycle methods:
/*** Application customization file* This file persists through upgrades - place all custom logic here* Acts as a global controller for the entire application*/const TAG = 'AppCustomizations';class AppCustomizations {
Common Customization Patterns
Pattern 1: Wrapping Existing Methods
When you need to add logic before or after an existing method without replacing it entirely:
/*** Wrap an existing controller method to add custom logic*/pageInitialized(page, app) {if (page.name === 'workDetail') {const controller = page.findController((c) => c.constructor.name === 'WorkOrderDetailController');if (!controller) {this.app.log.w(TAG, 'WorkOrderDetailController not found');
Pattern 2: Event Handler Registration
Subscribe to application or datasource events:
/*** Register custom event handlers*/applicationInitialized(app) {this.app = app;const methodTag = `${TAG}.applicationInitialized`;// Listen for datasource eventsthis.app.on('onAfterLoadData', (event) => {
Pattern 3: Datasource Customization
Modify datasource behavior and queries:
/*** Customize datasource when it initializes*/onDatasourceInitialized(datasource, owner, app) {const methodTag = `${TAG}.onDatasourceInitialized`;if (datasource.name === 'workorderDS') {this.app.log.d(methodTag, 'Customizing work order datasource');
Pattern 4: Dialog Customization
Customize dialog behavior during initialization:
/*** Called when a dialog is initialized* @param {Object} dialog - The dialog instance*/dialogInitialized(dialog) {const methodTag = `${TAG}.dialogInitialized`;this.app.log.d(methodTag, `Dialog initialized: ${dialog.name}`);if (dialog.name === 'woStatusChangeDialog') {
Pattern 5: Custom Event Handlers from XML
You can define custom event handlers in your XML and implement them in AppCustomizations.js:
In app.xml:
<button label="Custom Action"on-click="handleCustomAction"on-click-arg="{item}"id="btn_001"/>
In AppCustomizations.js:
/*** Handle custom action button click* This method is called from the XML event handler*/handleCustomAction(item) {const methodTag = `${TAG}.handleCustomAction`;this.app.log.d(methodTag, `Custom action for item: ${item.wonum}`);// Implement your custom logic
Best Practices
✅ Do:
- Place all custom business logic in this file
- Use clear, descriptive function names
- Add comprehensive comments explaining the purpose of each customization
- Use the
TAGconstant for consistent logging - Test customizations after each Maximo Mobile upgrade
- Monitor release notes for deprecated APIs
- Wrap original methods when extending functionality (preserve base behavior)
- Use
bind()when saving method references to preserve context - Implement event handlers for any component (datasource, dialog, page, app)
❌ Don’t:
- Assume APIs will remain unchanged between versions
- Skip testing after upgrades
- Mix custom logic with base framework files
- Ignore deprecation warnings in release notes
- Modify the file structure or class name
- Remove the
export defaultstatement
Handling API Changes During Upgrades
When upgrading Maximo Mobile versions, you may need to adjust your AppCustomizations.js code to accommodate API changes:
/*** Example: Handling deprecated API* Version 8.3: Used app.datasources.workorderDS* Version 8.4: Changed to app.findDatasource('workorderDS')*/applicationInitialized(app) {this.app = app;const methodTag = `${TAG}.applicationInitialized`;
Real-World Example
Here’s a complete example showing multiple customization patterns:
/*** Custom Application Logic for Technician App* Adds validation, logging, and custom business rules* Acts as global controller for all application events*/const TAG = 'TechnicianCustom';class AppCustomizations {
Summary
The AppCustomizations.js file is your primary tool for customizing Maximo Mobile applications. It provides:
- Upgrade safety - Your customizations persist through version updates
- Global controller - Can handle events from any component in the application
- Centralized management - All custom logic in one place
- Flexibility - Can wrap, extend, or add new functionality
- Maintainability - Clear separation from base framework code
- Event interception - Can intercept and customize any application event
While you may need to adjust for API changes between versions, this approach is far superior to directly modifying base controllers, which would require complete reapplication after each upgrade.
Learn More:
Method 2: Extending Base JavaScript Classes (Recommended)
Overview
For more complex customizations, extending base JavaScript classes provides a structured, object-oriented approach that maintains separation between custom and base code while surviving upgrades. This method is particularly useful when AppCustomizations.js becomes too large or when you need to create reusable controller logic.
Why Extend Classes
Benefits:
- ✅ No manual reapplication - Custom logic doesn’t need to be manually merged after upgrades
- ✅ Object-oriented - Leverages inheritance for clean code organization
- ✅ Override flexibility - Can selectively override specific methods
- ✅ Maintains base functionality - Inherits all base class capabilities
- ✅ Better organization - Separates concerns into focused class files
When to Use:
- Complex controller customizations with many methods
- Multiple related method overrides in a single controller
- When
AppCustomizations.jsbecomes too large and unwieldy - When you want better code organization and separation of concerns
Trade-offs:
- ⚠️ Requires understanding of JavaScript inheritance
- ⚠️ Must test after upgrades to ensure base class changes don’t break extensions
- ⚠️ Need to track which base classes are extended
- ⚠️ More complex than AppCustomizations.js for simple customizations
- ⚠️ Requires XML configuration changes
Creating an Extended Controller Class
Step 1: Create the Extended Controller File
Create a new JavaScript file with a unique name. The file should import the base controller and the logging utility:
/*** ExtendedSchedulePageController.js* Extended controller for schedule page* Location: /app/controllers/extended/ExtendedSchedulePageController.js*/import { log } from "@maximo/maximo-js-api";import SchedulePageController from "../SchedulePageController";const TAG = "ExtendedSchedulePageController";
Step 2: File Organization
Organize your extended controllers in a clear directory structure:
Recommended Structure:
app/├── controllers/│ ├── extended/ (Custom extended controllers)│ │ ├── ExtendedSchedulePageController.js│ │ ├── ExtendedWorkOrderController.js│ │ └── README.md (Document what each does)│ ├── SchedulePageController.js (Base controller)│ ├── WorkOrderController.js (Base controller)│ └── ...
Alternative Structure (Co-located):
app/├── controllers/│ ├── SchedulePageController.js (Base controller)│ ├── ExtendedSchedulePageController.js (Extended controller)│ ├── WorkOrderController.js (Base controller)│ ├── ExtendedWorkOrderController.js (Extended controller)│ └── ...
Note: While extended files can coexist in the same location as the original controller, we recommend creating a subfolder (e.g.,
extended/) for better readability and organization.
Step 3: Register in XML Configuration
Update your application’s XML file to reference the extended controller instead of the base controller:
Before (using base controller):
<page id="schedule"controller="SchedulePageController"><!-- Page configuration --></page>
After (using extended controller):
<page id="schedule"controller="ExtendedSchedulePageController"><!-- Page configuration --></page>
For dialogs:
<dialog id="statusChangeDialog"controller="ExtendedStatusChangeController"><!-- Dialog configuration --></dialog>
Best Practices for Extended Classes
✅ Do:
- call
super.methodName()when overriding methods to preserve base functionality - Use descriptive class and method names that clearly indicate purpose
- Document what each override accomplishes and why it’s needed
- Test thoroughly after upgrades to ensure base class changes don’t break your extensions
- Add comprehensive comments explaining custom logic
- Maintain a README documenting all extended classes
❌ Don’t:
- Override methods without calling the parent implementation unless absolutely necessary
- Modify the base class files directly
- Create deep inheritance hierarchies (keep it simple - one level of extension)
- Assume base class method signatures won’t change between versions
- Forget to update XML configuration to reference the extended controller
- Mix extended classes with direct controller modifications
Summary
Extending base JavaScript classes provides:
- Upgrade safety - Extended classes can be carried over after upgrade
- No manual reapplication - Custom logic survives upgrades automatically
- Better organization - Separates complex logic into focused class files
- Object-oriented design - Leverages inheritance for clean code structure
- Flexibility - Can selectively override methods while preserving base functionality
Registration Process:
- Create extended controller file with unique name
- Import base controller and logging utility
- Extend base class and override methods as needed
- Update XML configuration to reference extended controller
When to Choose This Method:
- Complex controller customizations with many methods
- Need for better code organization
- Reusable components across applications
AppCustomizations.jsis becoming too large
When to Use AppCustomizations.js Instead:
- Simple, straightforward customizations
- One-off event handlers
- Quick method wrapping
- Application-wide event subscriptions
Method 3: Directly Modifying Base Controllers (Not Recommended)
Why This Approach Should Be Avoided
Directly modifying base controller files is strongly discouraged and should never be used in production environments. This section explains why this approach creates significant problems and what you should do instead.
The Problems with Direct Modification
Critical Issues:
❌ Upgrade Complications
- Base files are completely overwritten during upgrades
- All custom changes are lost and must be manually reapplied
- High risk of introducing bugs when merging changes back in
❌ Maintenance Burden
- Extremely difficult to track what was changed and why
- Hard to distinguish custom code from framework code
- Increases technical debt significantly
- Other developers may not know which files contain customizations
❌ Version Compatibility
- Base methods can change implementation details between versions
- Your modifications may conflict with new framework behavior
- Framework improvements may be incompatible with your changes
- Debugging becomes extremely difficult
❌ Team Collaboration
- Version control conflicts are more likely
- Knowledge transfer is complicated
- Code reviews become difficult
- Onboarding new developers is harder
❌ Support Issues
- IBM support cannot help with modified base files
- Troubleshooting requires reverting to base files first
- Cannot easily test if issues are framework bugs or custom code problems
Migration Path
If you’ve already modified base controllers, migrate immediately:
Step 1: Document All Modifications
Create a document listing every modified file:
# Modified Base Controllers## WorkOrderDetailController.js- Line 45-55: Added custom validation for completed work orders- Line 78: Added custom logging- Line 120: Modified error handling## SchedulePageController.js- Line 89: Added custom map initialization
Step 2: Choose Migration Approach
For each modification, decide:
- Simple logic → Migrate to AppCustomizations.js
- Complex logic → Create extended controller
Step 3: Implement in Recommended Approach
Move the custom code to AppCustomizations.js or extended controller.
Step 4: Test Thoroughly
- Verify all custom functionality works
- Test edge cases
- Ensure no framework features are broken
Step 5: Restore Base Files
Replace modified base files with original framework files.
Summary
Why Direct Modification Fails:
- ❌ Changes are lost during upgrades
- ❌ Requires manual reapplication every upgrade
- ❌ High risk of introducing bugs
- ❌ Difficult to maintain and track
- ❌ Breaks team collaboration
- ❌ Voids support from IBM
What to Do Instead:
- ✅ Use AppCustomizations.js for simple customizations
- ✅ Use extended controllers for complex customizations
- ✅ Both approaches survive upgrades
- ✅ Both approaches are maintainable
Key Takeaway: Never modify base framework files. Always use AppCustomizations.js or extended controllers. The time saved by “quick” direct modifications is lost many times over during upgrades.
Comparison Matrix
Quick Reference Table
| Aspect | AppCustomizations.js | Extended Classes | Direct Modification |
|---|---|---|---|
| Upgrade Safety | ✅ High - File persists | ✅ High - Files persist | ❌ Low - Changes lost |
| Maintenance | ✅ Easy - Centralized | ✅ Moderate - Organized | ❌ Difficult - Scattered |
| API Changes | ⚠️ May need updates | ⚠️ May need updates | ❌ Must manually merge |
| Complexity | ✅ Simple | ⚠️ Moderate | ❌ High risk |
| Setup Time | ✅ Quick | ⚠️ Moderate | ✅ Quick (but costly later) |
| Code Organization | ⚠️ Can become large | ✅ Well organized | ❌ Mixed with base code |
| Testing After Upgrade | ⚠️ Moderate | ⚠️ Moderate | ❌ Extensive |
| IBM Support | ✅ Supported | ⚠️ Limited support | ❌ Not supported |
| Recommended | ✅ Yes | ✅ Yes | ❌ Never |
| Best For | Simple to moderate customizations | Complex controller logic | Never use |
*IBM Support means the AppCustomization.js will automatically be caried over to the new version. This does not mean IBM will assist with the configurations.
Detailed Comparison
Upgrade Safety
AppCustomizations.js:
- ✅ File is preserved during upgrades
- ⚠️ May need to update for deprecated APIs
- ✅ Framework changes don’t affect file location
- ⚠️ Requires moderate testing after upgrade
Extended Classes:
- ✅ Files are preserved during upgrades
- ⚠️ May need to update for base class changes
- ✅ Inherits new base class functionality automatically
- ⚠️ Requires moderate testing after upgrade
Direct Modification:
- ❌ Files are completely overwritten
- ❌ All changes are lost
- ❌ Must manually reapply every upgrade
- ❌ Requires extensive testing and high risk of bugs
Maintenance Effort
AppCustomizations.js:
- ✅ All customizations in one place
- ✅ Easy to find and review
- ⚠️ Can become large and unwieldy
- ✅ Clear separation from framework
Extended Classes:
- ✅ Organized into focused files
- ✅ Clear inheritance structure
- ✅ Easy to understand purpose
- ✅ Scales well with complexity
Direct Modification:
- ❌ Changes scattered across base files
- ❌ Hard to identify what was customized
- ❌ Difficult to review
- ❌ Mixed with framework code
IBM Support
AppCustomizations.js:
- ✅ Fully supported by IBM
- ✅ Documented approach
- ✅ Recommended by IBM
Extended Classes:
- ⚠️ Limited support from IBM
- ⚠️ Custom code responsibility
- ⚠️ May need to isolate issues
- ⚠️ Use at your own discretion
Direct Modification:
- ❌ Not supported by IBM
- ❌ Must revert to base files for support
- ❌ Cannot troubleshoot with modifications
- ❌ Voids support agreement
Development Time
AppCustomizations.js:
- ✅ Quick to implement
- ✅ No file structure changes needed
- ✅ Immediate testing possible
- ✅ Low learning curve
Extended Classes:
- ⚠️ Requires file creation
- ⚠️ Requires XML configuration update
- ⚠️ Need to understand inheritance
- ⚠️ Moderate learning curve
Direct Modification:
- ✅ Appears quick initially
- ❌ Costly during every upgrade
- ❌ High long-term cost
- ❌ Technical debt accumulates
Summary
Quick Decision:
- Need IBM support? → AppCustomizations.js
- Simple customization? → AppCustomizations.js
- Complex controller logic? → Extended Class (limited support)
- AppCustomizations.js too large? → Extended Class (limited support)
- Need reusable components? → Extended Class (limited support)
- Unsure? → Start with AppCustomizations.js
Never:
- ❌ Directly modify base controllers
- ❌ Mix custom code with framework code
- ❌ Skip documentation of customizations
Support Considerations:
- ✅ AppCustomizations.js is fully supported by IBM
- ⚠️ Extended classes have limited IBM support
- ❌ Direct modifications void IBM support
Upgrade Considerations
Overview
Maximo Mobile upgrades can introduce API changes, deprecated methods, and new framework features. Understanding how to prepare for and handle upgrades is critical for maintaining your customizations.
Pre-Upgrade Checklist
Complete these steps before upgrading Maximo Mobile:
1. Document All Customizations
For AppCustomizations.js:
# AppCustomizations.js Inventory## Event Handlers- applicationInitialized: Registers global event handlers- pageInitialized (workDetail): Wraps saveWorkOrder method- dialogInitialized (statusChangeDialog): Adds custom validation## Custom Methods- validateCustomRequirements: Validates completed work orders
For Extended Classes:
# Extended Controllers Inventory## ExtendedWorkOrderController.js- Extends: WorkOrderDetailController- Overrides: pageInitialized, saveWorkOrder- Custom Methods: validateByWorkType, validateByStatus- XML Reference: workDetail page## ExtendedScheduleController.js
2. Review Release Notes
Before upgrading, review the Maximo Mobile release notes for:
- ✅ Deprecated APIs
- ✅ Changed method signatures
- ✅ New features that might replace custom code
- ✅ Breaking changes
- ✅ Performance improvements
Example Release Note Items to Watch:
Version 8.4 Release Notes:- DEPRECATED: app.datasources.* - Use app.findDatasource() instead- CHANGED: saveWorkOrder() now returns Promise<SaveResult> instead of Promise<boolean>- NEW: Built-in validation framework - consider migrating custom validation- BREAKING: Dialog lifecycle events now pass different parameters
3. Create Backup
Backup these files:
- AppCustomizations.js
- All extended controller files
- Modified XML files
- Custom configuration files
# Example backup structurebackup/├── pre-upgrade-8.4/│ ├── AppCustomizations.js│ ├── controllers/│ │ ├── extended/│ │ │ ├── ExtendedWorkOrderController.js│ │ │ └── ExtendedScheduleController.js│ ├── app.xml
4. Test Current Functionality
Document expected behavior before upgrade:
# Pre-Upgrade Test Results## Work Order Save Validation- ✅ Completed WOs require actfinish- ✅ Completed WOs require actlabcost- ✅ Toast message displays for validation failures- ✅ Save is prevented when validation fails## Status Change Dialog
During Upgrade
1. Note Warnings and Errors
Pay attention to:
- Deprecation warnings in console
- Build errors
- Runtime errors
- Changed behavior
2. Review Framework Changes
Check if base classes you’ve extended have changed:
// Before upgrade - your extended classclass ExtendedWorkOrderController extends WorkOrderDetailController {async saveWorkOrder() {// Your custom logicreturn await super.saveWorkOrder();}}// After upgrade - check if base class signature changed
Upgrade Testing Strategy
1. Smoke Testing
Quick tests to verify basic functionality:
## Smoke Test Checklist (15 minutes)- [ ] Application loads without errors- [ ] Can navigate to main pages- [ ] Can open dialogs- [ ] Can save records- [ ] No console errors- [ ] Custom features visible
2. Functional Testing
Detailed testing of customizations:
## Functional Test Checklist (1-2 hours)### AppCustomizations.js- [ ] Test each lifecycle method- [ ] Test each event handler- [ ] Test each wrapped method- [ ] Verify custom logic executes- [ ] Verify original functionality preserved
3. Regression Testing
Ensure nothing broke:
## Regression Test Checklist (2-4 hours)- [ ] All standard features work- [ ] All custom features work- [ ] No new errors in console- [ ] Performance is acceptable- [ ] Mobile functionality works- [ ] Offline functionality works
Summary
Key Takeaways
Three Methods for Applying Configurations:
AppCustomizations.js (Recommended)
- ✅ Primary method for most customizations
- ✅ Fully supported by IBM
- ✅ Upgrade-safe - file persists
- ✅ Centralized location
- ⚠️ Can become large for complex projects
Extended Classes (Recommended for Complex Scenarios)
- ✅ Better organization for complex logic
- ✅ Upgrade-safe - files persist
- ✅ Object-oriented approach
- ⚠️ Limited IBM support
- ⚠️ Requires XML configuration
Direct Modification (Never Recommended)
- ❌ Changes lost during upgrades
- ❌ No IBM support
- ❌ High maintenance burden
- ❌ Never use in production
Decision Framework
Choose AppCustomizations.js when:
- Simple to moderate customizations
- Need IBM support
- Quick implementation required
- Application-wide logic
Choose Extended Classes when:
- Complex controller logic
- AppCustomizations.js too large
- Need better organization
- Reusable components
Never choose Direct Modification:
- Always use one of the recommended approaches
Critical Success Factors
For Upgrade Success:
- Document all customizations
- Review release notes before upgrading
- Test thoroughly after upgrading
- Use feature detection over version checking
- Implement defensive programming
For Maintainability:
- Use consistent logging with TAG constants
- Add comprehensive comments
- Keep customizations organized
- Document business logic
- Use descriptive names
For Team Collaboration:
- Centralize customizations in AppCustomizations.js or extended classes
- Never modify base controllers
- Document all extended classes
- Use version control
- Code review customizations
Quick Reference
AppCustomizations.js Template:
import { log } from "@maximo/maximo-js-api";const TAG = "AppCustomizations";class AppCustomizations {applicationInitialized(app) {this.app = app;log.t(TAG, "Customizations initialized");}
Extended Class Template:
import { log } from "@maximo/maximo-js-api";import BaseController from "../BaseController";const TAG = "ExtendedController";class ExtendedController extends BaseController {constructor() {super();log.t(TAG, "Extended controller initialized");
Related Resources
Prerequisites
Before implementing configurations, understand these concepts:
- Basic Concepts - Core MAF concepts and terminology
- Datasources Guide - Understanding datasources and their lifecycle
- Page Component Guide - Page structure and components
- Overview - Getting started with MAF configuration
Related Practices
Simple Configurations:
- Add Tooltips - Simple UI customizations
- Making Fields Read-Only - Field configuration
- Marking Required Fields - Validation patterns
- Client Side Datasource Filtering - Data filtering
Advanced Configurations:
- Additional Logic to Controllers - Extending methods without replacing
- Replace Existing Method - Method wrapping technique
- Set Default Values - Using lifecycle events
- Restrict Complete Status - Complex validation example
Troubleshooting
- Debugging UI - Tools and techniques for debugging
- FAQ - Common questions and solutions
External Documentation
- IBM Maximo Mobile Documentation - Official documentation
- Maximo Mobile: Customization using MAF and Configuration - IBM training course
Additional Notes
Performance Considerations
AppCustomizations.js:
- Keep methods lightweight
- Avoid heavy processing in lifecycle events
- Use async/await for I/O operations
- Consider impact on mobile devices
Extended Classes:
- Call super methods to preserve base functionality
- Don’t override methods unnecessarily
- Profile performance after customizations
- Test on actual mobile devices
Security Considerations
Best Practices:
- Validate all user input
- Don’t expose sensitive data in logs
- Follow principle of least privilege
- Test security implications of customizations
- Review custom code for vulnerabilities
Common Pitfalls:
- Logging sensitive data (passwords, tokens)
- Bypassing framework security checks
- Exposing internal implementation details
- Not validating data from external sources
Mobile-Specific Considerations
Offline Functionality:
- Test customizations in offline mode
- Ensure custom logic works without server connection
- Handle sync conflicts appropriately
- Consider data storage limitations
Performance:
- Mobile devices have limited resources
- Test on actual devices, not just emulators
- Optimize for battery life
- Consider network latency
User Experience:
- Provide clear feedback for custom actions
- Use appropriate loading indicators
- Handle errors gracefully
- Test on various screen sizes
Documentation Best Practices
Document Your Customizations:
Create a README.md in your customization directory:
# Application Customizations## OverviewBrief description of what customizations exist and why.## AppCustomizations.js- **Purpose**: Centralized customization logic- **Key Features**:- Work order validation
Continuous Improvement
Regular Reviews:
- Review customizations quarterly
- Remove unused code
- Refactor as complexity grows
- Update documentation
- Test with new framework versions
Code Quality:
- Use consistent coding standards
- Implement code reviews
- Add unit tests where possible
- Monitor performance
- Track technical debt
Remember: The configuration approach you choose today will impact your ability to upgrade and maintain your application for years to come. Always prefer AppCustomizations.js or extended classes over direct modification of base controllers.