Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
22 Jul 2025 · 5 min read ·Article 44 / 110
Go

44 Reusing Messages with `import`

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

When building distributed systems or microservices, exchanging data between services becomes a necessity. Protocols like gRPC, which are based on Protocol Buffers (protobuf), are extremely popular thanks to their fast serialization and explicit schema representation. However, the larger the data schema you manage, the more critical it becomes to organize and reuse that schema effectively. One of protobuf’s standout features that often gets underrated is the ability to import and reuse messages across protobuf files.

This article explores the concept of reusing messages with import in Protobuf, how to use it, and the best practices for organizing your data schema. I’ll also include code examples, a simulation of how it works in a real-world project, a comparison table of implementations, and even a Mermaid flow diagram to clarify how reusability with import works.


1. A Common Problem: Schema Duplication That Doesn’t Scale

Imagine you have two different teams: the Payments team and the User Management team. Both need a definition of the User message, for example:

proto
1// payments.proto
2message User {
3  string id = 1;
4  string email = 2;
5}
proto
1// users.proto
2message User {
3  string id = 1;
4  string email = 2;
5}

The approach above looks simple, but as the number of services and message definitions grows, this duplication creates problems:

  • Inconsistent schema: A field gets changed in one file, but the other services don’t pick up the update.
  • Code maintenance nightmare: Every update to a duplicated definition has to be repeated across many files.
  • Riskier deployments: The chance of errors increases whenever schemas fall out of sync.

So what’s the solution? This is exactly where protobuf’s import feature comes into play.


2. The Core Concept: Reuse with import

Protocol Buffers supports imports much like modern programming languages do. You simply define a message once and then reuse it across various other .proto files.

Directory structure:

bash
1proto/
2  user.proto
3  payment.proto
4  order.proto

user.proto

proto
1syntax = "proto3";
2
3package myapp.user;
4
5message User {
6  string id = 1;
7  string email = 2;
8}

payment.proto

proto
 1syntax = "proto3";
 2
 3package myapp.payment;
 4
 5import "user.proto";
 6
 7message Payment {
 8  string payment_id = 1;
 9  myapp.user.User user = 2; // reuse the message from user.proto
10  double amount = 3;
11}

With this setup, the User message is defined once and can be used anywhere simply by importing it.


3. Project Scenario Simulation: A Multi-Service Setup

Suppose your company has the following services:

Service NameProto FileReused Message
User Serviceuser.protoUser
Payment Servicepayment.protoUser (from user.proto)
Order Serviceorder.protoUser (from user.proto)

Note: All services must agree on a consistent package and protocol path so that the protobuf compiler (protoc) can resolve every dependency.

Compile Process Simulation:

Each developer simply runs:

bash
1protoc --proto_path=proto/ --go_out=. payment.proto

protoc will automatically locate and compile user.proto if it’s found in the same directory path or has been included via the --proto_path option.


4. Flow Diagram: How Does import Work?

Let’s visualize how message reuse flows when protoc runs.

MERMAID
graph LR
    A[service.proto] -->|"import "user.proto""| B[user.proto]
    B --> C[message User didefinisikan]
    A --> D[message Payment didefinisikan]
    D -->|menggunakan User| C
    A & B --> E[protoc compiler]
    E --> F[Generated code dalam masing-masing bahasa]

Explanation:

  • The payment.proto file imports user.proto.
  • The User message is defined only once in user.proto.
  • The Payment message uses User (fully qualified as myapp.user.User when it’s in a different package).
  • protoc resolves the dependencies across all files.

5. Case Study: Changing the User Schema Centrally

Let’s say a full name field needs to be added to User:

proto
1// user.proto
2message User {
3  string id = 1;
4  string email = 2;
5  string full_name = 3;
6}

Without touching any other file, every service that imports user.proto now automatically supports this new field after recompiling!


6. Table: Comparison Without vs. With import

AspectWithout importWith import
Schema ConsistencyMaintain each file manuallyEverything inherits from one source
Schema UpdatesRisk of falling out of syncJust change a single proto file
MaintenanceHarderEasy and scalable
IDE SupportDifficult to lint cross-fileBetter auto-completion
Dependency BuildManual/linkingDependencies resolved automatically

7. Best Practices for Organizing Protobuf

Here are some best practices to get the most out of import:

  • Separate shared type schemas into their own files.
    • For example: keep each User, Product, and Address message in its own proto file.
  • Use clear, normalized namespace/package naming.
  • Document the schemas shared between services in a central place.
  • Version your proto files (e.g., user_v1.proto / user_v2.proto) when introducing breaking changes.
  • Keep dependencies between protos free of cycles (avoid circular imports).

8. Advanced: Reusing Enums, Services, and Extensions with import

import isn’t limited to messages. Enums, services, and even extension fields can be reused as well:

proto
 1// status.proto
 2enum Status {
 3  ACTIVE = 0;
 4  INACTIVE = 1;
 5}
 6
 7// user.proto
 8import "status.proto";
 9message User {
10  ...
11  Status status = 4;
12}

9. Conclusion

The import feature in Protobuf is a key tool for reusing schemas in a way that’s scalable, maintainable, and reduces code duplication. By designing a modular proto structure, placing reusable messages in a single location, and staying disciplined about updates and documentation, even large projects remain robust and easy to manage.

If your project is starting to “smell” because of message duplication, now is the time to refactor and harness the power of import in Protobuf!


Have you started reusing messages with import yet? Share your experience in the comments! 🚀

Related Articles

💬 Comments