Go’s HTTP package distinguishes protocol and metadata (e.g. header) negotiation from request and response body transfers. A handler isn’t a function that returns, it’s kind of a callback that gets invoked, and can hand off the request and it’s response writer arbitrarily. This is certainly different than treating a request as a function call! But that approach would, I think, be unnecessarily restrictive. Reasonable people may disagree, of course.
In a language like Clojure, it makes sense for your return to be a map with arbitrary data, including headers and a body. It flows well.
But in a language like Go, that idiom of building up a response and returning it can only nicely be achieved with these objects. In fact that’s the pattern, you update the response as you go. Requiring return oriented code would mean you need the same object, but to spend more time managing it and remembering to return it at the end.
After all you don’t know what Middleware made changes you need to include, even on a 500 response.
This problem here seems minor to me. This kind of thing just isn’t where you usually get nailed, and I don’t think you’d spend that much time debugging even if you did. It’s possible, but unlikely.
The proposed solution from the echo framework feels uglier to me than the original problem… if I’m understanding, we always return an error, even when it’s a “success”, and the echo framework sorts out the semantic malfeasance for you, because you’ve created the “error” using echo.Context.JSON.