Skip to content
5 min read

Why Your D365 Implementation Fails (and How to Fix It)

D365CRMArchitectureBest Practices

Where this comes from#

Over the past four years I've seen Dynamics 365 CE from three very different seats: a manufacturing CRM integrated with F&O over Dual-Write, a greenfield platform for a national logistics operator with ten-plus external systems, and a rescue engagement on a credit-management platform that had been accumulating technical debt for years before I arrived.

Different industries, different budgets, same failure patterns. None of them were technology failures. Every one traces back to a decision made either before the first line of code or long after go-live, when everyone had stopped paying attention.

Integration estimated as a line item#

On the manufacturing project, Dual-Write looked straightforward on paper. Standard entity maps for customers, products, and pricing. Microsoft documentation makes it look like configuration work, and for a vanilla F&O instance it mostly is.

This F&O instance was not vanilla. Years of custom X++ had grown around the entities we needed to sync: validation handlers, pre- and post-event logic, custom fields with their own business rules. Every CRUD operation on a synced entity could trigger F&O-side code that threw an exception, and the Dual-Write pipeline surfaced those as sync errors with almost no diagnostic context. We discovered the customizations one at a time, usually when something failed in production.

Stabilizing the integration took roughly three times the original estimate. Not because anyone was slow, but because the estimate was built on the assumption that the other system was standard.

That's the actual lesson: integration effort scales with the customization level of the other system, not yours. When I estimate an integration now, the first thing I ask for is access to the other platform's dev team and an honest inventory of their customizations. If the answer is "we'll sort that out later," the estimate gets a multiplier.

The same project taught us when to walk away from a pattern. Inventory levels and production status were originally planned for Dual-Write too. After the stabilization experience, we moved them to Virtual Entities instead, because read-only query-on-demand doesn't care how customized the F&O side is. Choosing the integration pattern based on the other system's state, rather than on what the architecture diagram looks like, would have saved us months.

Building custom before learning the platform#

I did this myself early on. I came to D365 as a C# developer, and my instinct was to write a plugin for everything. The platform punishes that instinct.

I've watched teams spend weeks on custom approval logic when the Power Automate approvals connector covered their actual requirement. I've reviewed plugins that did nothing a Business Rule couldn't do, except now they needed a build pipeline, a code review, and a deployment window for every change.

Custom code in D365 isn't just an upfront cost. Every plugin you write is something the next team has to read, debug, and migrate. The platform's native features are maintained by Microsoft; your plugin is maintained by whoever inherits it.

The discipline that works for me: map the requirement to native capabilities first, in writing. Only when there's a concrete gap, something configuration genuinely cannot express, does it become code. On the logistics project we enforced this through the project wiki: every feature spec had to state why custom code was needed before any got written.

The data model decided in week one#

On the manufacturing project, the entire sales process arrived as a Canva prototype. Click-through mockups: lead card, quote builder, pipeline view. My job was to turn that into a Dataverse schema.

The translation took two weeks of iteration, and I'm glad it did. The prototype showed fields with no natural source of truth. It implied state transitions that no screen actually expressed. Every ambiguity we caught at the whiteboard was an ambiguity we didn't have to fix later with a schema migration against live data.

Restructuring a data model under hundreds of users and millions of rows is expensive and slow, so the cheap insurance is boring: take the two or three weeks before building anything. Add alternate keys at the start, because retrofitting them once integrations depend on record matching is miserable. And think about reporting before finalizing relationships, because the questions the business wants answered have a way of dictating entity design.

Nobody owns the platform after go-live#

This is the pattern nobody writes articles about, and it's the one I now get hired to fix.

The credit-management platform I joined as a consultant was a successful implementation by any go-live measure. Years later it was running 35,000+ active cases, 163 Power Automate flows, and a client portal. It was also quietly rotting. Twelve of its 32 JavaScript web resources still called the Xrm.Page API that Microsoft deprecated years ago. Most had no error handling at all. The Dataverse Search index had grown to 51 tables, including Field Service and Copilot Studio tables the firm had never used, consuming 16 GB on the dev environment alone.

No single decision caused any of that. It's drift: every sprint adds, nothing ever removes, and no one is accountable for the platform as a whole once the implementation partner leaves.

The fix isn't heroic. Someone owns platform health, and it has to be a person, not a committee. Deprecation work gets a standing slot in the release calendar instead of waiting for a crisis. And once a year, someone audits what's actually used: flows, web resources, indexed tables, security roles. The audit on that credit-management platform projected the search index could shrink by more than half just by removing tables nobody had ever queried.

What I'd tell a project sponsor#

Ask your architect two questions. First: "what did you assume about the systems we're integrating with, and who verified it?" Second: "who owns this platform in year three?"

If either answer is vague, that's where your implementation will fail. The technology will be fine.