Comment by bunderbunder
2 days ago
I keep pining for a stripped-down gRPC. I like the *.proto file format, and at least in principle I like the idea of using code-generation that follows a well-defined spec to build the client library. And I like making the API responsible for defining its own error codes instead of trying to reuse and overload the transport protocol's error codes and semantics. And I like eliminating the guesswork and analysis paralysis around whether parameters belong in the URL, in query parameters, or in some sort of blob payload. And I like having a well-defined spec for querying an API for its endpoints and message formats. And I like the well-defined forward and backward compatibility rules. And I like the explicit support for reusing common, standardized message formats across different specs.
But I don't like the micromanagement of field encoding formats, and I don't like the HTTP3 streaming stuff that makes it impossible to directly consume gRPC APIs from JavaScript running in the browser, and I don't like the code generators that produce unidiomatic client libraries that follow Google's awkward and idiosyncratic coding standards. It's not that I don't see their value, per se*. It's more that these kinds of features create major barriers to entry for both users and implementers. And they are there to solve problems that, as the continuing predominance of ad-hoc JSON slinging demonstrates, the vast majority of people just don't have.
I can write frickin' bash scripts that handle JSON APIs with curl, jq, here quotes and all that.
A lot of people just do whatever comes to mind first and don't think about it so they don't get stuck with analysis paralysis.
Handling failure might be the real hardest programming problem ahead of naming and caches and such. It boggles my mind the hate people have for Exceptions which at least make you "try" quite literally if you don't want the system to barrel past failures, some seem nostalgic for errno and others will fight mightily with Either<A,B> or Optional<X> or other monads and wind up just barreling past failures in the end anyway. A 500 is a 500.
I worked at a place that had a really great coding standard for working with exceptions:
1. Catch exceptions third-party code and talking to the outside world right away.
2. Never catch exceptions that we throw ourselves.
3. Only (and always) throw exceptions when you're in a state where you can't guarantee graceful recovery. Exceptions are for those exceptional circumstances where the best thing to do is fail fast and fail hard.