Extensibility and Customization
The central tension of ERP: the product must fit every business, and no product fits any business exactly. How that gap gets closed determines whether the customer can ever upgrade again.
The ladder — always climb from the bottom
1. CHANGE THE PROCESS Adopt the standard. Free. Fastest. Hardest politically
2. CONFIGURE Settings, tables, rules. No code. Upgrade-safe
3. EXTEND (in-app) Custom fields, sanctioned events, low-code rules
4. EXTEND (side-by-side) Your own service, integrated via API
5. MODIFY THE CORE Change vendor code. Upgrade debt forever
Rule: never go up a rung until you’ve genuinely exhausted the one below.
A common project finding: 30–50% of “gaps” requiring development were actually configurable, and another 20% were requirements that existed only because “that’s how the old system did it.”
Level 1 — Change the process
The cheapest solution and the one nobody wants to hear.
Questions worth asking, politely:
- Is this a legal requirement, a competitive differentiator, or a habit?
- What breaks if we do it the standard way?
- Is the current process a workaround for a limitation of the old system?
- How many people, how many times a day, and how much does the workaround actually cost?
Fit-gap analysis is where this happens. See Requirements and Fit-Gap.
Level 2 — Configuration
The intended mechanism. See Master vs Transactional Data.
Document types · number ranges · account determination · pricing procedures
field status · validation and substitution rules · workflow definitions
output determination · approval limits · organisational structure
tolerance groups · movement types · partner determination
The skill here is knowing what’s configurable, which is why a good functional consultant saves more money than a fast developer. If you’re a developer on a packaged ERP, learning the config layer of your product is the single highest-ROI investment you can make.
Level 3 — In-app extension
Custom fields
Every ERP has a sanctioned way to add fields that survive upgrades:
- SAP: append structures, CDS extensions, key-user extensibility fields
- Dynamics 365: table extensions (AL), financial dimensions
- NetSuite: custom fields and custom records
- Odoo: inherited models with new fields
- Acumatica: DAC extensions
Rules for custom fields:
- Check for an existing standard field first. There are usually unused “material group 1–5” style fields.
- Add it to every relevant layer: table, screen, API, search help, data migration template, print form. Half-added fields are permanently empty.
- Add it to the data dictionary/documentation, or nobody in three years will know what it means.
- Consider whether it belongs on the header, the item, or an org-level view.
Business events / hooks
Sanctioned extension points that survive upgrades:
- SAP: BAdIs (Business Add-Ins), enhancement spots, BTEs, business events
- Dynamics: event handlers, chain-of-command (
[ExtensionOf]) - Odoo: method overriding via inheritance,
@api.onchange - NetSuite: user event / client / scheduled / map-reduce scripts
- Acumatica: graph extensions and event handlers
The contract: the vendor promises the hook will still exist after an upgrade. In exchange, you promise not to touch anything else.
Validation and substitution rules
Declarative rules configured rather than coded:
IF account IN 6xxxxx AND cost_center IS NULL
THEN ERROR "Cost centre required for expense accounts"
IF document_type = 'RE' AND profit_center IS NULL
THEN SET profit_center = material.profit_center
Low-code / citizen development
Power Apps + Power Automate + Dataverse (Microsoft), SAP Build, NetSuite SuiteFlow, Odoo Studio.
Great for: approval flows, simple forms, notifications, small departmental apps. Dangerous for: anything touching the ledger, anything business-critical with no owner, and anything built by someone who then leaves. Shadow low-code is a real governance problem — inventory it, assign owners, and put critical flows under source control.
Level 4 — Side-by-side extension
Your logic runs outside the ERP and talks to it via APIs.
┌─────────────┐ API ┌──────────────────────┐
│ ERP │◀─────────────────▶│ Your extension │
│ (clean) │ events │ (own DB, own repo, │
└─────────────┘ │ own release cycle) │
└──────────────────────┘
Platforms: SAP BTP, Microsoft Power Platform + Azure, Oracle OIC/OCI, NetSuite SuiteCloud, or plain containers on any cloud.
Good candidates: customer/supplier portals, mobile apps, complex custom calculations, machine learning, integration hubs, document generation, anything with a UI the ERP can’t do well.
Bad candidates: anything that must be transactionally consistent with a posting. If it must be in the same database transaction, it belongs inside.
Trade-offs:
- Full freedom of stack, testing, and release cadence
- The ERP stays clean and upgradeable
- But: latency, eventual consistency, API rate limits, licence implications (see Licensing), and a second system to operate
Level 5 — Core modification
Changing vendor code. Sometimes genuinely necessary; always expensive.
The permanent costs:
- Every upgrade requires manual conflict resolution on every modified object
- Vendor support may refuse to help on modified objects
- The modification may become obsolete when the vendor changes the surrounding code
- Regression testing burden forever
If you must modify:
- Get written approval from an architect and the business owner
- Register it in a modification log: object, reason, author, date, business case
- Keep the change minimal and well-commented, with the ticket reference
- Write tests
- Set a review date to check whether standard functionality has caught up
- Track total modification count as a metric — it’s your upgrade-cost predictor
A customer with 12 modifications upgrades in weeks. A customer with 2,000 upgrades in years, or never.
The clean core doctrine
Every vendor now preaches this, in their own words:
✅ Configure
✅ Extend via sanctioned hooks
✅ Build side-by-side
✅ Use released public APIs only
❌ Modify vendor code
❌ Write directly to vendor tables
❌ Use internal/unreleased APIs
❌ Depend on undocumented behaviour
“Use released APIs only” is the one developers break most often. Reading an internal table directly works today and breaks silently at the next upgrade, usually in production, usually at month end.
Managing custom code
Once you have custom development, it needs the same discipline as product code:
Naming convention: Z*/Y* (SAP), a dedicated prefix or namespace elsewhere
Source control: Git, always, even for platform-native code
Code review: mandatory
Static analysis: the vendor's linter (ATC, X++ best practices, ESLint)
Unit tests: for logic; integration tests for postings
Documentation: technical spec per object, linked to the functional spec
Ownership: every custom object has a named owner
Inventory: a register of all custom objects with a business justification
Retirement: review annually; delete what's unused
Usage tracking is the highest-value governance tool. Log every execution of every custom program. After a year you’ll typically find 20–40% of custom objects have never been run. Deleting them shrinks your upgrade test scope directly.
RICEFW estimating
The standard estimating unit. See Who Works on ERP.
| Object | Simple | Medium | Complex |
|---|---|---|---|
| Report | 3–5 d | 8–12 d | 20+ d |
| Interface | 5–10 d | 15–25 d | 40+ d |
| Conversion | 5–8 d | 12–20 d | 30+ d |
| Enhancement | 2–4 d | 6–10 d | 20+ d |
| Form | 3–5 d | 8–12 d | 20+ d |
| Workflow | 5–8 d | 12–20 d | 30+ d |
Indicative only — calibrate against your own team’s history. And add 30–50% for testing, documentation, and defect fixing, which are always underestimated.
The custom code review checklist
- Could this be configuration instead?
- Does standard functionality already do this? (Check the release notes of the last three versions)
- Are we using released APIs only?
- Does it handle: empty results, locks, period-closed, missing master data, missing exchange rate?
- Is it idempotent if it can be retried?
- Is it authorisation-checked?
- Does it respect the tenant/company/org scope?
- Will it perform at production volume? (Tested, not assumed)
- Are amounts
DECIMALwith a currency? - Is it restartable if it’s a batch job?
- Is it documented and owned?
- Does it have tests?