- Custom development makes sense when the business has three rules it cannot bend and the packaged tool cannot express them without a workaround.
- If the workaround is a parallel spreadsheet, the spreadsheet is the real system, and custom software just makes that honest.
- Two cases decided by this test: a manufacturer with five bidirectional ERP flows, and a security-services company scheduling guards across sites.
The symptom: the workaround has become the system
You notice the problem before anyone calls it a software problem. Someone on the operations team keeps a spreadsheet open all day, side by side with the packaged tool. They copy order numbers out of the marketplace portal, paste them into the ERP, then copy stock levels back the other way. When you ask why, the answer is a rule: "we only issue an outbound document for pending orders, never for completed ones." The packaged connector cannot express that rule, so a person does it by hand.
That is the symptom. It is not downtime, it is not an error message. It is a manual step that exists only because the software cannot hold a business rule. In the manufacturer case, the marketplace ERP and the factory ERP needed five flows moving in both directions: orders in, stock out, invoices out, shipping back, and product identity reconciled across both. The vendor connector could move some of that. It could not refuse to touch completed orders, so a human stood in the gap.
In the security-services case the symptom looked different. A request arrived with its own rules document for scheduling guards across sites. The deciding question was narrow: can a packaged scheduling tool express these rules without a parallel spreadsheet? If the answer is no, the spreadsheet is not a stopgap. It is the scheduling engine, and it lives outside the tool you are paying for.
How to diagnose it
Do not start with a feature comparison. Start with the rules. Sit with the people who run the process and ask them to write down the three rules the business cannot bend. Not preferences, not nice-to-haves. The rules that, if broken, mean a wrong invoice, a guard at the wrong site, or stock that does not exist.
Then take each rule and try to express it inside the packaged tool. Concretely, look for where the tool lets you write a condition. In most ERP and scheduling tools that means a rule builder, a workflow editor, or a scripting hook. If the tool has an API, check whether the condition can be enforced server-side. A quick way to test this is to read the API reference and look for a field that carries order status or shift state, then check whether you can filter on it before an action fires.
For the integration case, the diagnostic was a field mapping exercise. Pull one order from the marketplace API and one from the factory ERP, put them side by side, and list every field that has to match. Product identity is usually the first failure: the same item has a different name, SKU, or alias in each system. If you cannot map product aliases, every downstream flow is wrong. A short script against the marketplace order endpoint and the ERP product endpoint is enough to see this:
curl -s "$MARKETPLACE/api/orders?status=pending" \
| jq '.items[] | {order_id, status, sku, qty}'
curl -s "$ERP/api/products?sku=$SKU" \
| jq '{sku, alias, stock, unit}'
If the two SKUs do not reconcile, you have found the reason the manual step exists. For the scheduling case, the diagnostic is the same shape: take the rules document, take the packaged tool's rule editor, and try to encode rule one. If it needs a second tool, a script, or a spreadsheet to hold the exception, you have your answer.
What the evidence showed
In the manufacturer case, the vendor connector could move data but could not express the pending-only rule. The business rule was that an outbound document is issued only for pending orders, never for completed ones. The connector had no place to put that condition, so completed orders risked being touched, and someone watched them by hand. The five flows were real and bidirectional: orders, stock, invoices, shipping, and product identity. The fix was a Node.js service that maps product aliases between the two systems and checks order status before issuing anything outbound.
In the security-services case, the request came with its own rules document, and the deciding factor was whether a packaged tool could express those rules without a parallel spreadsheet. The rules themselves are not public, so the article does not describe them. What matters is the test: if the packaged tool needs a spreadsheet to hold a rule, the spreadsheet is the system.
Neither project measured ROI, savings, or hours saved. Those numbers were not collected, so none are reported here. The value was in removing a manual step and making the real rules visible in code instead of in a person's head.
When custom development makes sense
| Signal | Packaged tool | Custom development |
|---|---|---|
| Three rules the business cannot bend | Rule builder cannot express them | Rules live in code, versioned and testable |
| A parallel spreadsheet exists | Spreadsheet is the real system | Spreadsheet logic moves into the service |
| Product or entity identity differs across systems | Alias mapping is manual | Mapping table maintained in one place |
| Conditional actions (pending only, never completed) | Connector fires on all records | Service checks status before acting |
| Bidirectional flows (five in the manufacturer case) | Partial coverage, manual gap | Each flow owned and monitored |
| Rules arrive as a document, not a config screen | Needs a workaround to hold them | Document becomes the spec |
The fix
For the integration, the fix is a small service that owns the mapping and the condition. It does not try to replace either ERP. It sits between them, translates product aliases, and refuses to act on completed orders. The core is a status check and an alias lookup:
async function syncOrder(order) {
if (order.status !== 'pending') {
return { skipped: true, reason: 'not pending' };
}
const alias = await aliasFor(order.sku);
if (!alias) {
throw new Error(`No alias for SKU ${order.sku}`);
}
const payload = {
reference: order.order_id,
sku: alias.erpSku,
qty: order.qty,
};
return erp.createOutboundDocument(payload);
}
The alias table is the part that matters. It is a single source of truth for product identity, and it is the thing the vendor connector could not hold. When a new product appears, you add one row. When a name changes on either side, you update one row. The five flows then run against the same mapping.
For the scheduling case, the fix is to encode the rules document directly. Each rule becomes a function that takes a shift request and returns a decision. The packaged tool can still handle the calendar view and notifications, but the rule evaluation lives in code where it can be tested against the document. If a rule changes, you change one function and rerun the tests. No spreadsheet, no one remembering the exception.
Custom development is not the right answer when the packaged tool already expresses your three rules. It is the right answer when the workaround is doing the real work. That is the whole test. You can read more about how we approach this at custom software development.
How to verify the fix worked
Before the fix, measure two things: how many records require a manual step, and how many rules live outside the tool. After the fix, measure the same two. The target is zero manual steps for the rules you moved into code, and zero rules held in a spreadsheet.
For the integration, the check is a status audit. Query the ERP for outbound documents created in the last period and confirm none reference a completed order. Then query the marketplace for pending orders and confirm each has a matching ERP document or a logged reason it does not. If the service skips a record, it should say why. A skip with no reason is a bug.
SELECT reference, status, created_at
FROM outbound_documents
WHERE created_at > now() - interval '7 days'
AND status = 'completed';
-- expect zero rows
For the scheduling case, the check is a rule conformance test. Take the rules document, write one test per rule, and run them against the scheduler. If a rule cannot be tested, it is still living in someone's head, and the fix is not done. Before and after, the number you care about is rules expressed in code versus rules expressed in a spreadsheet. That number should go from some to zero.
Neither project produced a measured ROI, and none is claimed. The verification is about coverage and correctness, not savings.
Frequently asked questions
How do I know if my business rules fit a packaged tool?
Write the three rules you cannot bend. Try to express each one inside the tool without a script, a second tool, or a spreadsheet. If all three fit, the packaged tool is fine. If one needs a workaround, that workaround is the real system.
What if only one rule does not fit?
One rule that cannot be expressed is enough to create a manual step, and a manual step is where errors and delays live. The manufacturer case turned on a single condition: pending orders only, never completed ones. The connector could not hold that condition, so a person did.
Is custom development always more expensive?
Not necessarily, but cost was not measured in either case described here, so no comparison is offered. The relevant question is not price, it is whether the packaged tool can express your rules. If it cannot, you are already paying for the workaround in manual effort, whether or not it appears on an invoice.
Can I start with the packaged tool and move later?
Yes, and that is often the right sequence. Use the packaged tool for what it does well, and keep a written record of the rules it cannot express. When the workaround becomes load-bearing, you have the spec for the custom piece already written down.
Frequently asked questions
Write the three rules you cannot bend. Try to express each one inside the tool without a script, a second tool, or a spreadsheet. If all three fit, the packaged tool is fine. If one needs a workaround, that workaround is the real system.
One rule that cannot be expressed is enough to create a manual step, and a manual step is where errors and delays live. The manufacturer case turned on a single condition: pending orders only, never completed ones. The connector could not hold that condition, so a person did.
Not necessarily, but cost was not measured in either case described here, so no comparison is offered. The relevant question is not price, it is whether the packaged tool can express your rules. If it cannot, you are already paying for the workaround in manual effort, whether or not it appears on an invoice.
Yes, and that is often the right sequence. Use the packaged tool for what it does well, and keep a written record of the rules it cannot express. When the workaround becomes load-bearing, you have the spec for the custom piece already written down.