When a signup gate cannot get a clear answer, the safe default is to refuse the account rather than allow it, because a wrongly created account is far harder to undo than a wrongly refused signup.
When I built the signup gate on my own site, the interesting question was not what it does when the answer is yes or no. It was what it does when the check cannot answer at all — a missing configuration row, a database blip, anything that makes the gate itself throw rather than return a clear verdict. The tempting implementation is the one where an undetermined answer falls through to whatever the surrounding code happens to do next, and in a signup path what the surrounding code happens to do next is create the account. That is the one interpretation of an unknown answer that should never be available, and it is available by default in almost every language, because an unhandled exception has no opinion about what it was gating.
Most checks in a system are fine failing open, in the sense that the cost of a wrong yes during a rare outage is small and reversible. A feature flag that defaults to on when its lookup fails shows a user a feature a moment early; nobody is harmed and nothing is created that has to be unwound. Account creation is a different category of decision, because the moment it happens it produces a durable object — an email on file, a row in a table, an identity a support process might now have to deal with — precisely at the point in the flow where I have the least visibility, an outage or a missing configuration, into whether creating that object was ever the right call.
The asymmetry between the two mistakes is not close once you actually weigh them. Refusing a signup that should have been allowed, because the gate could not answer during a brief outage, costs the person one retry a few minutes later once the system recovers — a bad but small experience, gone the moment things come back. Allowing a signup that should have been refused, for the same reason, leaves a real account behind, created under conditions nobody chose deliberately, that now has to be found and dealt with after the fact rather than prevented before it existed. One mistake self-heals. The other one sits in the database until a person notices it. None of this makes the safer default free. A refused signup during a genuine outage is still a real person hitting a wall with no way to know whether it is deliberate caution or the system being broken, and I accept that cost on purpose because it resolves itself the moment the outage ends, unlike the other one.
So the rule I hold myself to now is specific rather than general: any check that gates something durable and expensive to undo defaults to refusing when it cannot get a clear answer, not to allowing. That is a narrower rule than everything should fail closed, which is not actually true of most checks in a system and would make plenty of ordinary features needlessly brittle during minor outages. It is a rule about what the check is protecting: if the thing on the other side of a yes is reversible and cheap, let it fail open and move on with your day. If the thing on the other side is an account, a payment, a deletion, a message sent to someone else, fail closed, because an unknown answer is never the same fact as a yes, and only one of those two should ever be allowed to stand in for the other by default.
The difference between the two behaviours is one branch — an unhandled exception in the gate check becoming an explicit refusal rather than falling through to whatever the code would otherwise do. The size of the fix is not the point. The point is that it was one line I had to go looking for on purpose, because the failure it corrected produces no error, no crash, no obvious symptom. It just quietly creates the exact thing the gate existed to prevent, under precisely the conditions where nobody is watching closely enough to notice.