An important thing to recognize is that the status code, as well as the headers, are metadata that has meaning to the generic components that are participating in the message exchange. They aren't really intended for the end consumer.
One of the key differences between 2xx and 4xx is the effect that it has on caches. RFC 7234 explains
A cache MUST invalidate the effective Request URI (Section 5.5 of [RFC7230]) as well as the URI(s) in the Location and Content-Location response header fields (if present) when a non-error status code is received in response to an unsafe request method.
PUT, POST, PATCH are all unsafe.
Caches (and other intermediate components) aren't supposed to go digging into the response body to figure out what is going on -- the origin server is supposed to put all of the necessary hints into the status line and headers.
So for this case, one of the dimensions of "it depends" rests on whether or not you want caches to invalidate the representations of the resource.