Technology
02/17/2026

How we migrated from git submodules to a centralized proto repository and solved access to private go modules

Moving API contract management from Git submodules to a centralized Proto repository was more than a technical refactor. It was an architectural shift that improved velocity, coherence, and scalability. The migration introduced semantic versioning, reduced operational friction, and forced a robust strategy for handling private Go modules in local, CI, and Docker environments—demonstrating that access infrastructure is as critical as code itself.

How we migrated from git submodules to a centralized proto repository and solved access to private go modules
Share
LinkedIn
X (Twitter)
Facebook

Table of Contents

Introduction

Systems don’t fail because there isn’t enough code; they fail because there isn’t enough coherence. That incoherence usually appears in the contracts that connect everything: the APIs. For a long time, .proto files were treated as just another backend detail, while the frontend consumed them via Git submodules. In theory that worked. In practice it added friction, unnecessary coupling, and constant cross-team dependencies. The issue wasn’t gRPC or Protocol Buffers.

Moving to a centralized proto repository wasn’t just a structural change. It redefined how we version, distribute, and govern system contracts. During that process another critical problem emerged: how to make private Go modules work consistently across all environments.

N2.png

Background

Using Git submodules worked while the system was small. As services and teams grew, clear symptoms appeared:

  • Changes to proto files required simultaneous coordination between frontend and backend.
  • Submodules added unnecessary complexity to the development flow.
  • There was no clear visibility into which contract version was in use.
  • Versioning relied implicitly on commits, not on intentional releases.
  • Synchronization became manual, fragile, and error-prone.

The system and the organization was not designed to scale.

Core idea

An API contract is not a file: it’s a product. Like any product, it needs explicit versioning, controlled distribution, and clear consumption rules. Centralizing proto files in a dedicated repository turns the contract into a single source of truth, decoupled from any particular implementation. This enables scaling of both the system and the organization.

It wasn’t the submodules it was the mental model

Git submodules don’t fail by themselves. They fail when used to solve a governance problem. In our case, we treated a distributed system like a single repository.

  • Invisible coupling: the frontend depended on the backend’s internal state, breaking layer independence.
  • Implicit versioning: referencing commits removed context about the intent of changes (breaking vs non-breaking).
  • Operational friction: switching branches or updating references required specific knowledge that not every developer had.
  • Forced coordination: every change implied synchronizing PRs across repos, increasing iteration cost.

The problem was systemic—design and governance rather than purely technical.

N3.png

Centralizing the contract turns the API into infrastructure

A standalone proto repository does more than organize files: it redefines the contract’s role. The contract stops being an internal artifact and becomes a central piece of architecture.

  • Single source of truth: all services consume the same contract, eliminating divergence.
  • Real semantic versioning: each proto change maps to an intentional version (major, minor, patch).
  • Decoupled distribution: TypeScript and Go clients can be published as independent packages for autonomous consumption.
  • Scale by design: adding languages or services doesn’t require structural changes to the repository.

This changes the API from a dependency into a platform

Automation as a coherence guarantee, not just efficiency

Automating code generation and publishing was not merely an optimization it was essential to keeping the system consistent without relying on manual discipline.

  • CI as contract guardian: GitHub Actions formats, generates, and versions code automatically.
  • Human error elimination: no one needs to remember to generate code or bump versions manually.
  • Commit-on-change: commits are created only when there are real differences, avoiding unnecessary noise.
  • Local reproducibility: with Docker and the Buf CLI, any developer can reproduce CI behavior locally.
  • Automation doesn’t speed the system up; it stabilizes it.

N4.png

Private Go modules are not a detail they’re an access system

The biggest challenge was not generating code; it was making that code accessible consistently and securely in all environments. Here architecture intersects permission management.

  • Local environment (GOPRIVATE): bypassing public validations was key to working with private repos.
  • CI (GitHub Actions): we designed authentication flows based on tokens issued by GitHub Apps.
  • Local Docker: SSH-based access required mounting host credentials into the container.
  • Docker in CI: credentials needed to be injected at build time for authenticated downloads.

N5.png

Recommendations

  • Treat API contracts as versioned products, not internal files.
  • Avoid Git submodules when contracts change frequently across teams.
  • Automate generation and distribution to remove reliance on manual processes.
  • Define a clear authentication strategy for each environment from the start.
  • Evaluate architecture by how it scales in complexity, not just how it works today.

Conclusions

Migrating to a centralized proto repository is more than a technical improvement: it’s an organizational architecture decision. It decouples teams, makes change intent explicit, and builds a more predictable system. The key lesson wasn’t about gRPC or Protocol Buffers; it was understanding that contracts are the system’s most sensitive point. If contracts aren’t well defined, versioned, and distributed, everything else becomes fragile. Scaling a system isn’t about writing more code it’s about designing better boundaries.

Glossary

  • gRPC: A communication framework enabling efficient service-to-service interaction over HTTP/2.
  • Protocol Buffers: A language for defining structured data contracts between systems.
  • GOPRIVATE: Go configuration that allows use of private modules without external validation.
  • Semantic versioning: A versioning scheme that signals the type and impact of changes.
  • CI (Continuous Integration): An automated system that validates, generates, and deploys code changes.

Gain perspective with curated insights

How we migrated from git submodules to a centralized proto repository and solved access to private go modules | Meetlabs