Your support team should not have to read source code to answer "why can't this be edited?"
That is exactly what a boolean permission check costs you. It returns False and discards the only three things anyone downstream needed: which actor, which permission, which ownership rule failed.
The user gets a generic 403. Support escalates. An engineer opens the repository to reconstruct a decision the code already made and then threw away.
We build the check to return an explainable denial instead. Same call site, richer answer: the actor, the permission requested, and the specific rule that rejected it. The interface can then say something true and specific, and a support agent can close the ticket without a developer.
The part teams underestimate comes next. Once those rules are declared as data rather than scattered across view conditionals, the same rule set can drive the Django admin: which roles see which models, which fields are read-only, who may act on an object they do not own. One definition, two consumers. No second, informal permission model quietly drifting out of sync with the first.
That drift is the real failure mode we keep meeting in inherited codebases. The API says no, the admin says yes, and nobody can tell you which one is correct.
So it is worth looking at how access questions get answered on your product today. If the denial does not carry enough context to resolve the ticket, it probably lands on an engineer's desk.
https://shipmindlabs.com/c/056a6fe2