What we got wrong turning an internal tool into a SaaS
A CRM built for one team's workflow and a CRM built for many teams are not the same software with a pricing page added. The difference shows up everywhere the first version assumed there was only one answer.

The short answer
- SalesCrew began as a CRM for one team's own sales process. It was then generalized into a product other companies configure and run their own instance of.
- The rework was not mainly new features. It was turning assumptions the internal version never questioned, one brand, one set of stages, one set of channels, into settings a second team could set differently.
- The permission model needed the most honest rethink. A single-team tool defaults to 'everyone sees everything' without anyone deciding it. A multi-client product cannot.
- SalesCrew's per-instance deployment config, three-axis scoped access, and module registry are the concrete answers to that rework, not a redesign done for its own sake.
A tool built for one team already made a hundred small decisions
Software built for one team's own use collects decisions nobody writes down, because only one answer was plausible at the time. The pipeline has these stages because that is how this team sells. The inbox connects to these mailboxes because that is what this team uses. The brand on the login page is the company's own because only one company uses it. None of those were choices in the sense of alternatives being weighed. They were what the tool did, because nothing else was ever asked of it.
That is fine, and often faster, until a second team wants the same product with different stages, a different brand on the login screen, or a different set of channels. At that point every quiet default becomes a real decision. Is this a setting now? Or does it stay fixed, and the second team lives with it? Generalizing an internal tool is mostly that decision, made once per assumption, across the whole product.
The permission model is where this shows up hardest
The clearest example is access control. A tool with one team behind it can let every user see every record, without anyone deciding it on purpose. There was never a reason to build a boundary between people who worked for the same company on the same deals. That default is invisible while it is true. It becomes a real problem the moment a second client's data is in the same product and the first client's users must not see it. Or the moment one client wants a rep to see only their own channel's leads.
SalesCrew's answer was a three-axis scope model: channel, Upwork profile, and area. It applies the same way to the UI and to MCP tokens. The restriction a person has in the app is the restriction an agent or an automation token has, if it is granted the same scopes. Building that after the fact is real rework, because the screens and workflows already assumed scoping was not a concern. Every place that fetched a full list has to fetch a scoped one. Every place that assumed a user could act on any record has to check first.
The module and feature registry is the same problem at a coarser grain. An internal tool has no concept of "which parts of this product does this instance have turned on". The answer was always all of it, for the one team using it. A product serving different client instances needs that concept, because some pay for voice and SMS and some do not. It needs it on the MCP side too: which tool families an instance's tokens can even see, not only which screens render.
What generalizing did not require, and what to actually watch for
None of this meant the CRM's sales process was wrong. The stages, the cadence logic, and the approval queue for anything an agent sends all came from real use and stayed. The rework was narrower than a rebuild. It meant finding every place the product had quietly assumed one answer, brand, stage list, channel set, permission scope, and turning that fixed value into a setting with a sensible default. Per-instance deployment configuration is the mechanism SalesCrew uses for the branding and channel side of that.
The general lesson is to look for assumptions that were never decisions. Those are the ones that break quietly rather than loudly. A feature built explicitly for "team A only" gets flagged and revisited on purpose. A feature that works one way because there was only ever one team to build it for does not get flagged. That is the common failure when a tool built for internal use is asked to serve more than the team it was built for.
Questions
- What actually has to change first: the code or the permission model?
- The permission model, because every screen was quietly built on it. A tool with one team behind it can let anyone see anything without a decision being made. A tool with several client instances cannot. Scoping has to be designed before any single screen is worth polishing further.
- Is it faster to rebuild from scratch instead of generalizing an internal tool?
- Usually not. The internal version already encodes a working sales process end to end, and that is the hard part to get right. The rework is real but narrower than a rebuild: fixed assumptions become settings. Nobody has to re-decide what a CRM should do.
- How do you know when a hardcoded assumption is actually a setting?
- Ask whether a second real user of the product would need it to be different. A stage list, a brand name, or a channel set that only ever had one value, because only one team used the product, usually needs a different value the moment a second team arrives.