Keep the core clean (Everyone agrees. Nobody does it.)
There’s a sentence you hear a lot in enterprise architecture conversations. It sounds sensible, even obvious. “We should keep the core clean.” Heads nod. Someone writes it on a whiteboard. The meeting ends.
And then the next request comes in.
The exception that doesn’t count
I’ve watched this play out across very different systems, in very different organisations, with very different people making the calls. SAP landscapes where the official position was “standardise on SAP wherever possible.” Salesforce implementations where flows were built with genuine care to serve genuinely diverse needs. Bespoke platforms that started as clean, well-reasoned codebases and became something nobody fully understands anymore.
The systems are different. The pattern is identical. It starts with a reasonable exception. A business process that genuinely doesn’t fit the standard. A customer requirement that’s urgent and real. A workaround that gets the deal done or keeps the line running. Nobody is being reckless. Everyone is solving a real problem. And the principle, “keep the core clean,” still lives on the whiteboard, technically intact, because this one thing doesn’t count as a violation. Not really. Death by a thousand reasonable exceptions.
The warning that didn’t travel
Here’s what I’ve seen happen with SAP specifically. The organisation adopts a position: standard SAP processes mean lower cost, easier upgrades, better support. True. Genuinely true. But “standard SAP” is not a monolithic thing. SAP is not all equal just because it shares a logo. What’s standard in one module, one version, one industry configuration, is a custom deviation in another. Without clear criteria for what “standard” actually means in your context, and without someone who owns the boundary decision, the principle becomes a slogan. Everyone agrees with it and nobody applies it consistently, because applying it consistently would require saying no to someone, and that requires standing, language, and trust that often doesn’t exist in the room where the decision gets made. So the customisations accumulate. Not recklessly. Incrementally. Each one defensible in isolation. Each one making the next upgrade harder, the next integration more fragile, the next consultant more expensive to onboard.
The Salesforce story has a different texture but the same shape. Flows built to serve a genuinely diverse set of business needs. The people building them weren’t wrong to build them. The needs were real. But flows in Salesforce don’t exist in isolation. They trigger other flows. They have downstream effects that aren’t always obvious when you’re focused on solving the problem in front of you. And when the complexity compounds, it doesn’t announce itself. It just gets harder to change things. Velocity slows. Scalability erodes. And by the time it’s visibly broken, the context for why it was built that way is mostly gone. What strikes me about this one is that some people saw it coming. Not all of it, not the full picture, but enough to feel the risk. They flagged it. The problem wasn’t that nobody noticed. The problem was that the people who noticed didn’t have the language to make the risk clear and tangible enough to the people with the authority to slow down, or the standing to make it stick when velocity pressure was pushing the other way. The warning existed. It just didn’t travel.
But at least with SAP and Salesforce, you can point at the vendor. The platform has opinions. The licensing model creates constraints. There’s an external frame you can reference when you’re trying to hold a boundary. Take that away, and the dynamic gets starker. A few years ago I watched a bespoke platform, I’ll call it Arctus, get pulled apart by something that had nothing to do with the technology. The engineers on that system were skilled. They had a clear picture of what the architecture should look like and why. But business process logic, the kind that should have lived in adjacent systems or been handled at the integration layer, kept getting pushed into the core. Not because anyone decided that was the right approach. Because stakeholders had direct access to the development backlog, strong opinions about how their processes should work, and more organisational weight than the engineers raising concerns. The engineers knew what was happening. They said so. But knowing something is wrong and having the standing to stop it are two very different things. So the logic landed where the pressure pointed. The architecture absorbed it. And over time, the system that had been designed to be fast and adaptable became slow, brittle, and expensive to change. The eventual rewrite cost significantly more than it would have cost to hold the boundary in the first place. It always does.
This is the version of the problem that has no vendor to blame. It’s a people problem, dressed up as a technical one.
The cost of friction
This is where “keep the core clean” breaks down as a principle in practice. It’s not wrong. It’s just incomplete. A principle without a boundary model is a preference. And a preference loses to a deadline, or a stakeholder with organisational weight, almost every time. But even organisations that have done the boundary work, that have defined what belongs inside and what doesn’t, and have patterns and blueprints for how to extend and integrate cleanly, still find themselves back in the same conversation. Someone is arguing for a shortcut. A team is routing around the agreed approach. A stakeholder is pushing logic into the core again. The reason is usually friction. Not malice, not ignorance, just friction. The right path exists but it’s harder than the wrong one. The extension pattern is documented somewhere nobody reads. The integration layer requires cross-team coordination that adds two weeks to a delivery. The tooling to do it properly is heavier than just hacking it in. And when the deadline is real and the pressure is on, people don’t choose the wrong path because they’ve forgotten the principle. They choose it because the right path has too many steps and the wrong one is right there. This is the part that most architecture conversations stop short of. Defining the boundary is necessary. Providing patterns for both sides of it is necessary. But if the barrier to using those patterns correctly is higher than the barrier to ignoring them, you haven’t solved the problem. You’ve just made the violation slightly more deliberate.
What actually works is when the right path is also the easy path. When extending close to the core through the agreed pattern is less effort than arguing for an exception. When the blueprint for building outside and communicating back in is clear enough, documented well enough, and supported well enough that a team under pressure reaches for it naturally rather than routing around it. In SAP that might mean BTP with scaffolding already in place. In Salesforce it might mean pre-built integration patterns with guardrails that make the clean approach faster than the custom one. In a bespoke system it might mean an API contract the core owns, with enough tooling around it that consuming it is straightforward. The specifics depend on context. The principle is the same: lower the cost of doing it right until it becomes the default, not the exception.
Keeping the core clean isn’t a principle you apply once. It’s a boundary you defend continuously, and you can only defend a boundary that has been made explicit enough that the defence is a shared act, not an individual one.
The organisations that handle this well have done the work, usually painful, often iterative, to make the line between core and extension visible enough that when the next request arrives, the conversation is about where it belongs rather than whether to do it at all. And then they’ve gone one step further. They’ve made the patterns for working on the right side of that line easy enough to use that the shortcut stops being attractive. Keep the core clean. Define where the core ends. Build the patterns for what lives outside it. And then make those patterns easy enough to use that the boundary defends itself. That’s when the principle becomes real. And if you think this is hard now, wait until AI agents are writing significant portions of your codebase. They don’t negotiate, they don’t hesitate, and they don’t remember why the boundary was there in the first place. They just fill the space they’re given. But that’s a conversation for another time.
Stay safe & Thanks for reading 👍
/M