Acumatica Customization: The Ultimate Do's and Don'ts Guide
Common mistakes developers make and how to avoid them
Acumatica's customization framework is one of its greatest strengths—but also one of its biggest risks when done incorrectly. Poorly designed customizations often lead to upgrade failures, performance issues, and long-term maintenance challenges. This guide compiles the most common mistakes developers make and provides clear guidance on how to avoid them.
⚡ The Reality: Acumatica releases major updates twice a year (March R1 and September R2). Customizations that don't follow platform conventions can become upgrade blockers, performance bottlenecks, and maintenance burdens .
Part 1: Critical Do's
✅ DO Use the "Usr" Prefix for Custom Fields
Custom columns added to Acumatica's standard tables must start with "Usr" – this is what tells the upgrade process to preserve them. Columns without the prefix may be removed during an upgrade.
// ✅ CORRECT
[PXDBString(50)]
[PXUIField(DisplayName = "UsrCustomField")]
public virtual string UsrCustomField { get; set; }
// ❌ WRONG - May be removed during upgrade
[PXDBString(50)]
[PXUIField(DisplayName = "CustomField")]
public virtual string CustomField { get; set; }
✅ DO Use the Validate Project Prefix Tool
Acumatica provides a built-in tool to validate that all new objects in your solution comply with naming conventions. Select File > Validate Project Prefix in the Customization Project Editor menu. This ensures:
- All new objects follow proper naming conventions
- Customizations remain upgrade-safe
- ISV certification requirements are met
✅ DO Follow Naming Conventions
Acumatica has specific naming conventions for tables (DACs) and columns (fields) :
| Suffix | Usage |
|---|---|
| ID | Surrogate keys (e.g., CustomerID) |
| CD | Natural keys (e.g., CustomerCD) |
| Nbr | Numbering identifiers (e.g., OrderNbr) |
| Amt | Amounts (e.g., FreightAmt) |
| Qty | Quantities (e.g., OrderQty) |
| Date | Dates (e.g., OrderDate) |
⚠️ Critical: Do not use the underscore symbol (_) in table or column names—it is a reserved symbol in the Acumatica Framework [citation:3].
✅ DO Use Development Environment First
Always conduct customization work in a separate development environment rather than directly in production. Acumatica recommends a three-environment workflow: development → staging → production .
✅ DO Use Version Control
Every customization should be developed using version control (Git or equivalent systems), with a dedicated repository and full commit history. Each commit should be tied to a specific requirement and represent a logical, reviewable change .
✅ DO Use Generic Event Handlers
Modern Acumatica development favors generic event handlers over classic ones—they're easier to declare, use, and validate in Visual Studio.
// ✅ RECOMMENDED - Generic handler
protected virtual void _(Events.RowSelected<SOOrder> e)
{
// Business logic here
}
// ❌ LESS PREFERRED - Classic handler
protected virtual void SOOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
// Business logic here
}
✅ DO Package All Database Changes in Customization Projects
All solution-specific database schema changes must be packaged into a customization project. SQL scripts are strictly prohibited unless they are provided as part of database upgrade scripts .
💡 Pro Tip: Use the Customization Project Editor to package all changes—this ensures they can be cleanly removed if needed and remain upgrade-safe .
Part 2: Critical Don'ts
❌ DON'T Use PXDefault Without Proper PersistingCheck
The PXDefault attribute used without PersistingCheck = PXPersistingCheck.Nothing on custom fields defined in PXCacheExtension can prevent persisting of records to the database [citation:2].
// ❌ WRONG - Causes PX1030 warning/error
[PXDefault(0)]
public virtual int? UsrLineCounter { get; set; }
// ✅ CORRECT - With PersistingCheck
[PXDefault(0, PersistingCheck = PXPersistingCheck.Nothing)]
public virtual int? UsrLineCounter { get; set; }
// ✅ CORRECT - Alternative using PXUnboundDefault
[PXUnboundDefault]
public virtual bool? Selected { get; set; }
⚠️ Note: If a DAC extension includes a bound field with PXDefault without PersistingCheck = PXPersistingCheck.Nothing, a warning is displayed. For unbound fields, an error is displayed [citation:2].
❌ DON'T Use .Result or .Wait() in Acumatica Code
Just like in standard ASP.NET Core, blocking on asynchronous code in Acumatica can cause deadlocks and thread-pool starvation. Always use await and propagate async all the way.
// ❌ WRONG - Can cause deadlocks
var result = service.GetDataAsync().Result;
// ❌ WRONG - Also dangerous
service.SaveDataAsync().Wait();
// ✅ CORRECT - Async all the way
var result = await service.GetDataAsync();
❌ DON'T Use ServiceLocator Pattern for Dependencies
In Acumatica 2026 R1, the framework has been refactored to support constructor-based dependency injection. The ServiceLocator pattern limits testability and makes plug-ins harder to maintain [citation:1].
// ❌ OLD - ServiceLocator pattern (limited testability)
protected async Task<HttpResponseMessage> ExecuteAsync(HttpRequestMessage request)
{
using (var client = ServiceLocator.Current.GetInstance<IHttpClient>())
{
var response = await client.SendAsync(request);
return response;
}
}
// ✅ NEW - Constructor-based DI (2026 R1+)
public class MyPaymentPlugin : ICCProcessingPlugin
{
private readonly IHttpClient _httpClient;
public MyPaymentPlugin(IHttpClient httpClient)
{
_httpClient = httpClient;
}
protected async Task<HttpResponseMessage> ExecuteAsync(HttpRequestMessage request)
{
var response = await _httpClient.SendAsync(request);
return response;
}
}
❌ DON'T Modify Core Acumatica Code Directly
Never modify base Acumatica code directly—always use the extension model . Direct modifications will be lost during upgrades and can cause significant maintenance issues.
- Use
PXGraphExtensionfor graph customizations - Use
PXCacheExtensionfor DAC extensions - Use
PXOverridefor method overrides - Use
PXActionfor custom actions
❌ DON'T Skip Documentation
Document everything throughout the customization process. Not only will this help with current projects, but it will also be invaluable for future modifications or troubleshooting .
- Maintain a Functional Specification Document (FSD) with screen-by-screen behavior, field logic, and edge cases
- Document all customizations in the Customization Project Editor
- Keep a changelog for all modifications
❌ DON'T Start Coding Without Requirements
Before estimating or designing anything, focus on discovery and validation :
- Which Acumatica version is in use?
- Are there out-of-the-box features that already solve part of the request?
- Is a hybrid approach (OOTB + targeted customization) possible?
💡 Pro Tip: Use visual aids like screen sketches, workflow diagrams, and UI behavior examples to align expectations before development begins .
Part 3: Best Practices Summary
✅ Start Simple
Use no-code and low-code options (Business Events, Screen Customizations, Generic Inquiries) before writing code .
✅ Use AI-Assisted Development
Leverage AI tools to generate C# code—describe the functionality you want and it can generate the necessary code .
✅ Publish Before Adding Controls
When adding a database-backed custom field, publish first so the system creates the column, then add the corresponding control .
✅ Group Related Changes
Group related changes in the same customization project for easier management and conflict resolution .
✅ Follow Coding Standards
Use standard .NET coding conventions and Acumatica development guidelines. Keep classes and methods small and readable .
✅ Test Thoroughly
Rigorously test custom features to confirm they work as intended—preferably in staging environment first .
Common Pitfalls Summary
❌ Customizing Without Upgrade Planning
Always consider how customizations will survive major releases. Use the "Usr" prefix for custom fields and follow Acumatica guidelines .
❌ Overlooking Performance
Avoid heavy operations in frequently fired events like RowSelected and FieldDefaulting.
❌ Creating SQL Scripts
SQL scripts for database changes are strictly prohibited—use Customization Projects instead .
❌ Skipping Staging Environment
Test in staging (a copy of production) before going live, including interactions with other customizations .
❌ No Version Control
Every customization must be in version control with proper commit history and traceability .
❌ Not Using Debug Settings
Configure web.config with debug settings to surface errors and automation steps during development .
Development Environment Configuration
To optimize your development experience, add these settings to your web.config file :
<configuration>
<appSettings>
<!-- Enable website debugging -->
<add key="AutomationDebug" value="True" />
<!-- Optimize startup time -->
<add key="InstantiateAllCaches" value="False" />
<add key="CompilePages" value="False" />
<!-- Disable scheduler during development -->
<add key="DisableScheduleProcessor" value="True" />
</appSettings>
<system.web>
<compilation debug="true" />
</system.web>
</configuration>
These settings enable page validation warnings, show automation steps, and optimize instance startup—saving significant development time .
The Bottom Line
📌 Key Takeaway: Successful Acumatica customization requires discipline, planning, and adherence to platform conventions. Use the extension model, follow naming conventions, test thoroughly, and always think about upgrade safety. The extra effort upfront saves countless hours of maintenance and upgrade headaches later.
🔍 Further Reading: Explore Acumatica's official documentation for detailed guidance on specific topics. The Acumatica Developer Network (ADN) provides comprehensive resources for developers at all levels .