gRPC: Top 6 Things that Bite Newbies

Why it’s Awesome

Before diving into the tips, here’s what I’ve found super valuable about gRPC and IDLs, interface definition languages, in general:

Top 6 Tips

#0. Philosophy

First some critical concepts for the back of your mind. All the tips are fundamentally related to a few key ideas:

  1. Always Backward Compatible: We’re used to making liberal changes to our code, but once you have customers depending on the APIs being stable, you can’t just update parameters without breaking clients.
  2. WORA (write once, run anywhere): Because it’s polyglot (works in many languages) there may be parts of the proto files (protobufs) that feels a little unnatural for your platform. You need to keep in mind that a client can be anything from python to C++ so the naming and name-spacing rules may not match what you’re used to.
  3. REST vs RPC: Nouns vs Verbs. Lastly, coming from REST you may be thinking in terms of objects and CRUD operations. Going to gRPC is a bit more function oriented (Operation-oriented in gRPC lingua), although it’s still very natural for object-oriented languages. Which brings us to the tips…

#1. OOPs Don’t Worry

Have no fear, protobufs translate easily into your modern object-oriented programming languages, though the terminology is slightly different:

  • class => “service”. A set of methods acting on structured data.
  • Functions (and methods) => “rpc
  • struct => “message
  • Arrays [] => “repeated” (e.g. int_32 a[] == repeated int32 a)
  • include (using) => “import
  • union => “oneof
struct HelloRequest {
string name;
};
struct HelloReply {
string message;
};
class Greeter {
public static HelloReply sayHello(HelloRequest request);
};
syntax = "proto3";
// ...
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}

#2. Take and Return Messages

For something like an echo server you might be tempted to take and return a string:

syntax = "proto3";service EchoService {
rpc Echo (string) returns (string);
}
syntax = "proto3";service EchoService {
rpc Echo (EchoRequest) returns (EchoResult);
}
message EchoRequest {
string msg = 1;
}
message EchoResult {
string msg = 1;
}
message EchoResult {
string msg = 1;
int32 status = 2;
}

#3. Same Shape, Different message Please.

Even though two messages may look very similar, and have the same fields and types, resist the urge to use a single message type if they have separate meaning. For example, in the EchoService above, one might have naturally chosen to have EchoRequest and EchoResult be one message instead since they each just have a string field named msg; however, as shown above, having separate message types allowed us to update the EchoResult without affecting the EchoRequest. Again, backward compatibility for-the-win!

#4. Missing or New fields show up with a Default Value

In the old days (proto v2), there were annotations for required and optional fields, but now they’re all just optional. When a field is missing it arrives with the default value of empty-string “”, 0, or false. This let’s you make smart decisions to maintain backward compatibility. In the above example where we added a new status field to EchoResult, an old client who didn't know about it would send the old format without status, so it would arrive as 0.

message EchoResult {
string msg = 1;
enum StatusCode {
NONE = 0; // default if missing
SUCCESS = 1;
BAD_INPUT = 2;
}
StatusCode status = 2;
}

#5. How to Rename or Remove fields

Good and bad news: although renaming is actually fine for fields because the binary serialization doesn’t include the field name (just those “= 2;” style numbers), you almost never want to do this because you’ll break the generated client and server code stubs. It’s more likely that you’ll want to mark the old one deprecated, and add the new one:

message EchoResult {
string msg = 1;
int32 status = 2 [deprecated = true]; // <- Changed
enum StatusCode {
NONE = 0;
SUCCESS = 1;
BAD_INPUT = 2;
}
StatusCode status_code = 3; // <- New name and field #3
}

#6. Remember Polyglot

Frustration. There are going to be times when the naming in gRPC feels insane. The length and namespacing of variables is going to get super long. The CamelCase or snake_case is going to trip you up a dozen times. You’re going to expect dots ‘.’ where there are underscores “_” and vice versa.

Other Tips

Okay you over-achievers, I didn’t want to let this article get too long, but this is a catch all for additional things that can be added over time, to highlight for newbies to gRPC

  • Beware proto2! Many online examples are still proto2, but the Internet goes stale fast. Be sure to learn from proto3 tutorials.
  • Well Known Types: avoid the urge to define a common data types for your set of protos, if you can use one of the “WKT” (well known types) here that includes things like Duration and Timestamp.
  • API vs Storage messages: It’s fine to use protobufs to define APIs and physical storage, but avoid the urge to use a single message. Instead define two separate messages that can evolve on their own.

Conclusion

Hopefully you feel confident enough to get started and clear these little hurdles. This is just my experience and perspective, but I’d love to hear yours. What tips do you have? Please let me know in the comments.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Charles Thayer

Charles Thayer

Roblox, Facebook, Yahoo YST, Distributed Systems, KV & NoSQL, Monitoring, DevOps, CTO, Principal Engineer, etc.