A practice group's provisioning client sends SCIM 2.0 writes that RFC 7644 does not describe — which divergences do you absorb, and which do you refuse?
answer
- you cannot patch the other side
- one meaning or no absorption
- normalise at the edge, never in storage
- scope it to the tenant that forced it
- refusals must be diagnosable
basics
~20 sAbsorb a divergence only when it has exactly one possible meaning, can be normalised at the request edge, and is scoped to the tenant that forced it. Refuse anything ambiguous or lossy, loudly, with an error a support engineer can act on — because an absorbed divergence is permanent.
solid answer
~50 sYou are the service provider and the client is the customer's identity system: software you cannot patch on your schedule, and asking their administrator to change it has a commercial cost you may not be willing to pay. So this is a product decision, not a correctness one. **Absorb** when the divergence has exactly one possible meaning, when handling it changes nothing a conforming client sees, when it can live as a normalisation at the edge of the request rather than a branch through your domain, and when it can be scoped to the tenant whose credential it arrived on. **Refuse** when the intent is ambiguous, when absorbing it would silently drop or invert data, or when you would be guessing which people get access to clinical images. Refusal must be diagnostic: a SCIM error with `scimType` and a `detail` a support engineer can act on, plus your own log of the tenant and the body.
code
pseudocode · 13 linesnormalise(request, tenant):
for rule in divergenceRules.forTenant(tenant): # data, not scattered branches
if rule.matches(request):
request = rule.rewrite(request) # edge only; domain never sees the rule
audit.record(tenant, rule.name, request.id)
return request
handle(request, tenant):
normalised = normalise(request, tenant)
if not conformsToOurContract(normalised):
log.warn(tenant, rawBody(request)) # refusal must be diagnosable later
return scimError(400, scimType, detailNamingOperationAndAttribute)
return apply(normalised)go deeper
Understand that the provisioning client belongs to the customer, so 'the caller should fix their request' is often not available, and that a refusal has to explain itself.
Explain why an ambiguous write is refused rather than interpreted, and why a workaround belongs in a normalisation step before your domain logic rather than inside it.
Show the tenant-scoped rule set with a test and a record per rule, and a refusal path carrying scimType and a detail that a support engineer can act on without reading your code.
Argue the asymmetry explicitly — you cannot patch the other side, and refusal has a commercial cost — then give the four conditions for absorbing, and account for the carrying cost of every rule you keep.
## The asymmetry that makes this a judgement call Every other decision on this endpoint has a right answer in a specification. This one does not, because of who is on the other end. The SCIM client pushing dentists and hygienists into your radiography viewer belongs to the customer. You cannot ship it a fix on your schedule. You can ask the customer's administrator to reconfigure it, and sometimes that is the only correct answer, but it has a cost measured in renewal conversations rather than in engineering hours. What the customer experiences when you refuse is not "our identity system is non-conforming". It is "the viewer is broken". So the question an interviewer is really asking is whether you can distinguish the divergences that are cheap to absorb from the ones that will quietly damage your data, and whether you have a mechanism rather than a reflex. ## A test for absorbing Absorb only when **all four** of these hold: 1. **Unambiguous.** There is exactly one thing the client can mean. A `remove` whose path is absent fails this immediately: it might mean clear that attribute, or it might mean the client built the document wrong and the intended target is anyone's guess. 2. **Invisible to conforming clients.** Handling it changes nothing another customer's client observes. A normalisation that alters what you return to everybody is a protocol change wearing a workaround's clothes. 3. **Expressible at the edge.** It can be a rewrite applied to the request before your domain sees it — a case fold on an `op` value, an array accepted where a single object was expected — rather than a conditional threaded through your persistence code. A divergence that reaches storage is one you will be unable to remove. 4. **Scopeable.** The credential the write arrived on already resolves one tenant, so the rule can apply to that tenant alone rather than becoming global behaviour that a future conforming client inherits. ## A test for refusing Refuse, and mean it, when any of these hold: - **The intent is ambiguous.** Two readings, one of which removes a person's access to clinical images, is not a coin to flip. - **Absorbing it loses data.** Accepting a membership document whose shape you half-understand, and writing the part you did understand, produces a group that is wrong in a way nobody can detect from either side. - **It would make you guess about access.** Who may open a patient's radiographs is the customer's decision expressed through their identity system. If their write does not say, the answer is not a default. - **It is a client bug that will be fixed.** Absorbing it removes the pressure that would have fixed it, and you now own it forever. And refuse *legibly*. A 400 with `scimType` and a `detail` naming the operation and the attribute is what lets a support engineer, reading a ticket three time zones away, tell an administrator what to change. A refusal nobody can diagnose is functionally the same as an outage. ## Who decides, and who enforces It is worth being precise about the parties, because this is where the reasoning usually slides. **Your endpoint** enforces: it applies the write or returns an error. **Your product** decides which divergences the endpoint will tolerate, and that decision is made once, deliberately, not per incident. **The customer's identity system** decides what to send, and neither of your two roles can change that. A design that blurs the first two acquires a conditional per customer; a design that forgets the third builds a support process around asking people to do things they cannot do. ## The estate over years The tenth divergence does not cost what the first did. Keep them as data rather than as code paths scattered through a handler: 1. A single normalisation layer at the request edge, with one named rule per divergence. 2. Each rule recorded with the client kind it addresses, the date, the tenant that forced it, and the ticket. 3. A test per rule that pins both the divergent input and the conforming input, so nobody "fixes" a rule back into a bug. 4. A review that asks, on a cadence, whether each rule is still needed — clients do get updated, and a rule whose tenant left two years ago is pure carrying cost. Without that, the normalisation layer becomes archaeology: nobody knows which conditional is load-bearing, so nobody removes any of them, and the endpoint's behaviour is no longer describable in the discovery documents you publish. That is the real long-term failure — not a wrong branch, but a contract you can no longer state. ## The answer that shows seniority The weak answer is either extreme: "we conform strictly and it is their bug", which loses customers, or "we accept whatever arrives", which loses the ability to reason about your own data. The strong answer names the asymmetry, gives a test with a short list of conditions, insists the refusals be diagnostic, and treats the accumulated divergences as an asset with a maintenance cost and an owner.
- Give a divergence you would absorb and one you would refuse, and say what separates them.Absorb a client that sends a single object where the schema expects an array of one — there is exactly one possible meaning and the rewrite is one line at the edge. Refuse a `remove` operation arriving with no `path`: it could mean clear this attribute or it could be a document-building bug, and the two readings differ over whether people lose access. Unambiguity is the line.
- Why must a divergence rule be scoped to a tenant rather than applied globally?Because the credential the write arrived on already identifies exactly one customer, so scoping costs nothing, and because a global rule silently changes how you treat a conforming client that arrives next year. Global leniency also compounds: two tenant-specific rules can be individually safe and jointly ambiguous once both apply to every request.
- When is asking the customer to reconfigure their identity system the right answer?When the divergence is a configuration choice rather than a limitation of their software — a wrong attribute mapped to `userName`, a scope that syncs a whole organisation instead of one practice group. Those are cheap for them to change and expensive for you to absorb permanently. The judgement is whether you are asking them to change a setting or to replace their identity system.
- What happens to your published discovery documents once you have absorbed several divergences?Nothing should, and that is the constraint. The configuration and schema documents are the same for every tenant, so a divergence that changes what you advertise has leaked out of the normalisation layer into your contract. If you cannot state your behaviour in those documents any more, the rules have stopped being rewrites and become a second, undocumented protocol.
saying these in an interview costs you the question
- Accepts whatever arrives so the customer stops complaining
- Refuses everything non-conforming and calls it the client's bug
- Guesses the intent of an ambiguous write rather than refusing it
- Adds the workaround inside the persistence code
- Applies a client-specific rule to every tenant
- Returns a bare 400 with nothing a support engineer can act on