SlideShare a Scribd company logo
Building gRPC services
With Go and Bazel
Backend Stack at TextNow
• Build microservices using the Go programming language
• Go pairs well with gRPC
• Easy to create new networked services that are able to scale
• Use a monorepo for all Go microservices
• Easy to spin up new microservices
• Take advantage of a common set of build & release tools
• Linters, formatters, build rules, and more.
• Use the gRPC framework for inter-service communication
• Write clear, agreed upon contracts between our microservices
• Improved the level of documentation for our internal microservices.
Backend Stack at TextNow
• Began adopting gRPC/Go 2 years ago
• Our monorepo now has:
• roughly 3000 commits
• 34 contributors
• roughly 3 dozen microservice daemons
• roughly 2 dozen CLI tools
• We have learned a lot along the way!
Version 1.0
• textnow-mono’s first commit:
• a tiny gRPC API
• a daemon implementing the service
• a CLI tool
• The actual logic in the daemon wasn't significant
• It provided a playground to try new things
• Project structure?
• Build tooling?
• Proto generation?
Version 1.0 Project Structure
• proto/
• test_service.proto
• services/
• service name (for example, testService)
• pb/
• test_service.pb.go
• api.go
• Other .go files exist that offer the required business logic used by the API type
• cmd/
• testServiced/
• main.go
• testServiceCli/
• main.go
Version 1.0 Proto Structure
• All files in 1 directory
• Each file defined it’s own namespace
• Common types were imported across multiple protos
• The option go_package directive was specified
• Gave us control over mapping protos to services
• Each service lived in the same package name
• We called it ‘pb’
• It allowed us to distinguish auto-generated types from manually written types
• Proto code was moved to the right package manually
Version 1.0 Build System
• Script to auto-generate proto files
• Manually run `go build …` or `go run <package>`
Version 1.0 Lessons Learned
• Putting all the proto files into a single directory isn't a good idea!
• Differentiating between public interfaces and private interfaces was very difficult.
• A directory full of proto files didn't make it clear which were common types
• Generating types in a separate package than the service adds
unnecessary complexity.
• Service definitions from different pb packages led to a lot of issues
• Duplicate naming between manually written and proto-specified types is confusing
• Can’t extend auto-generated types
• Manual steps will be forgotten.
• Edit the proto file
• Build the .pb.go files
• Copy them into the appropriate subdirectory
• Leads to skew between what is committed in the proto file and what is built
Version 2.0
• Major goals of v2.0
• Ease of development
• Support for multiple build targets (binary, Docker image, etc.)
• Support for gRPC build targets
• Better build times (caching, parallelism, etc.)
• Consistent builds across platforms
• OS X and Linux
Version 2.0 - Options
• Makefiles
� Tried and true
� Easy to get started with
� Makefiles get really complicated as the project grows
� Remote caching is offered by the compiler, not the framework
• Bazel
� Fast, parallel builds with remote caching and build targets
� Supports gRPC and build targets across many languages
� Bleeding edge, going through active development
� Documentation is sparse
Version 2.0 – Building with Bazel
• We chose to adopt Bazel as our build framework
• rules_go specifies the build targets for the Go programming language
• Native support for both proto and gRPC build targets
• Use Gazelle to create build files
• Analyzes proto and Go files and generates build targets automatically
• Finds dependencies and updates accordingly
• Handles vendored references natively
Version 2.0 – Adopting Bazel
1. # brew install bazel
2. Create a WORKSPACE and BUILD.bazel file at the root of the project
https://github.com/bazelbuild/rules_go has good defaults
3. # bazel run //:gazelle
4. # bazel build //…
5. Success?
• In a new project? Yes
• In an existing project? Maybe?
• In textnow-mono? Definitely not
Version 2.0 – Adopting Bazel
• What broke?
• Gazelle does not like multiple proto packages in one target
• Gazelle does not generate proto targets out of path
• Needed to manually write the build targets for our protos
• We had checked in a number of auto-generated targets
• Gazelle happily generated build targets for these files
• Conflicted with the auto-generated build targets
Version 2.0 – Project Structure
• Changing our thinking about proto files solved our Bazel issues!
• Internal gRPC service protos are declared in their service package
• No more ‘pb’ packages
• Types live where they are used
• Can be extended if warranted
• Public gRPC service protos are declared in a separate hierarchy
• All services are versioned
• Version exists in the package name
• One or more services may implement the server interface
• One service may easily implement multiple versions of the server interface
Version 2.0 – Project Structure (Internal API)
• services/
• service name (for example, testService)
• api.go
• test_service.proto
• Other .go files exist that offer the required business logic used by the API type
• cmd/
• testServiced/
• main.go
• testServiceCli/
• main.go
Version 2.0 – Project Structure (Public API)
• api/
• test/
• v1/
• test_service.proto
• services/
• service name (for example, testService)
• api.go
• Other .go files exist that offer the required business logic used by the API type
• cmd/
• testServiced/
• main.go
• testServiceCli/
• main.go
Version 2.0 – Project Structure
• Gazelle generates Go build targets for all protos
• Build target has both client and server stubs
• Gazelle auto-links these build targets in our services
• We manually add build targets for our Dockerized services
• Use rules_docker and go_image
• We manually add other build targets for public APIs
• Bazel supports Java, CC, PHP, etc.
• Manually specify other language build targets
• Bazel build then auto-generates client dependencies
Version 2.0 - Example
• https://github.com/rmrobinson-textnow/bazel-grpc-example
• Public gRPC API with internal service
• Service has both a Dockerized daemon and CLI tool
• All built with Bazel!
Version 2.0 - Issues
• All proto/gRPC targets are built out of path
• Bazel has its own filesystem path for build artifacts
• Can’t access these files in IDEs
• No autocomplete 
• Build caches are necessary as projects get larger
• Initial clones can take a long time to build
• Build everything from scratch
• Building for Docker on Mac is awkward
• Must supply the --platform tag to Docker build targets
• All said Version 2.0 is much better than Version 1.0
Version 2.1 – API Field Validation
• Specify the allowed data on a field, not just the type, in the proto
• Easy for both sides of the API to see what should be allowed
• https://github.com/mwitkow/go-proto-validators
• Generates code for Go
• Build type included in Gazelle
• Lacks some dependencies making building without vendor tricky
• https://github.com/envoyproxy/protoc-gen-validate
• Generates code for Go, Java and Python (with C++ in development)
• Has Bazel targets for the validator logic and the Go dependencies
• Gazelle requires some hacky directives in top-level BUILD.bazel
Version 2.1 – API Linting
• We lint our code, we should also lint our APIs
• Identify style, consistency or best-practice violations at commit time
• https://github.com/uber/prototool
• More than just a linter, a full proto build tool
• Has several style guides, with rules for building your own
• Lots of development underway
• https://github.com/googleapis/api-linter
• Enforces the Google style guide
• Pretty simple
Version 2.2 – Auto-generated client libraries
• Given a service description, can we generate a full client SDK?
• Pre-specified hostnames, ports, etc.
• Automatic handling of retries, backoffs, etc.
• Applied on a per-route basis!
• https://github.com/googleapis/gapic-generator
• Still under active development
• No support for newer client languages (Swift, Kotlin)
• One possible end state – APIs as SDKs

More Related Content

What's hot (20)

PDF
Codifying the Build and Release Process with a Jenkins Pipeline Shared Library
Alvin Huang
 
PDF
Ursula Sarracini - When Old Meets New: Codebases
Anton Caceres
 
PDF
JUC Europe 2015: Plugin Development with Gradle and Groovy
CloudBees
 
PDF
August OpenNTF Webinar - Git and GitHub Explained
Howard Greenberg
 
PDF
Import golang; struct microservice - Codemotion Rome 2015
Giorgio Cefaro
 
PPTX
Introduction to GoLang
NVISIA
 
PDF
July OpenNTF Webinar - HCL Presents Keep, a new API for Domino
Howard Greenberg
 
PDF
eZ Publish 5: from zero to automated deployment (and no regressions!) in one ...
Gaetano Giunta
 
PDF
JUC Europe 2015: Hey! What Did We Just Release?
CloudBees
 
PDF
From Virtual Machines to Containers
camunda services GmbH
 
PDF
August Webinar - Water Cooler Talks: A Look into a Developer's Workbench
Howard Greenberg
 
PDF
13 practical tips for writing secure golang applications
Karthik Gaekwad
 
PDF
Building and Releasing a Golang CLI Tool
Bradley Cypert
 
PDF
Puzzle ITC Talk @Docker CH meetup CI CD_with_Openshift_0.2
Amrita Prasad
 
PDF
Introduction to Docker - Learning containerization XP conference 2016
XP Conference India
 
PDF
Real world microservice architecture
Viacheslav Poturaev
 
PDF
Semantic versioning and microservices in GO
Viacheslav Poturaev
 
PPTX
Integrating Git, Gerrit and Jenkins/Hudson with Mylyn
Sascha Scholz
 
PPTX
Gradle.Enemy at the gates
Strannik_2013
 
PDF
はじめての JFrog Artifactory
Tsuyoshi Miyake
 
Codifying the Build and Release Process with a Jenkins Pipeline Shared Library
Alvin Huang
 
Ursula Sarracini - When Old Meets New: Codebases
Anton Caceres
 
JUC Europe 2015: Plugin Development with Gradle and Groovy
CloudBees
 
August OpenNTF Webinar - Git and GitHub Explained
Howard Greenberg
 
Import golang; struct microservice - Codemotion Rome 2015
Giorgio Cefaro
 
Introduction to GoLang
NVISIA
 
July OpenNTF Webinar - HCL Presents Keep, a new API for Domino
Howard Greenberg
 
eZ Publish 5: from zero to automated deployment (and no regressions!) in one ...
Gaetano Giunta
 
JUC Europe 2015: Hey! What Did We Just Release?
CloudBees
 
From Virtual Machines to Containers
camunda services GmbH
 
August Webinar - Water Cooler Talks: A Look into a Developer's Workbench
Howard Greenberg
 
13 practical tips for writing secure golang applications
Karthik Gaekwad
 
Building and Releasing a Golang CLI Tool
Bradley Cypert
 
Puzzle ITC Talk @Docker CH meetup CI CD_with_Openshift_0.2
Amrita Prasad
 
Introduction to Docker - Learning containerization XP conference 2016
XP Conference India
 
Real world microservice architecture
Viacheslav Poturaev
 
Semantic versioning and microservices in GO
Viacheslav Poturaev
 
Integrating Git, Gerrit and Jenkins/Hudson with Mylyn
Sascha Scholz
 
Gradle.Enemy at the gates
Strannik_2013
 
はじめての JFrog Artifactory
Tsuyoshi Miyake
 

Similar to Building gRPC services (20)

PDF
Portable CI wGitLab and Github led by Gavin Pickin.pdf
Ortus Solutions, Corp
 
PPTX
Untangling fall2017 week2_try2
Derek Jacoby
 
PPTX
Untangling fall2017 week2
Derek Jacoby
 
PPTX
Build your android app with gradle
Swain Loda
 
PPTX
Managing changes to eZPublish Database
Gaetano Giunta
 
PDF
Managing Changes to the Database Across the Project Life Cycle (presented by ...
eZ Systems
 
PPTX
Habitat Workshop at Velocity London 2017
Mandi Walls
 
PPTX
DockerCon 15 Keynote - Day 2
Docker, Inc.
 
PPTX
Gocd – Kubernetes/Nomad Continuous Deployment
Leandro Totino Pereira
 
PPTX
CT Software Developers Meetup: Using Docker and Vagrant Within A GitHub Pull ...
E. Camden Fisher
 
PDF
Embedded Systems: Lecture 10: Introduction to Git & GitHub (Part 1)
Ahmed El-Arabawy
 
PDF
Enterprise Griffon
James Williams
 
PPTX
CloudFest 2018 Hackathon Project Results Presentation - CFHack18
Jeffrey J. Hardy
 
PDF
Starting from scratch in 2017
Stefano Bonetta
 
PPTX
habitat at docker bud
Mandi Walls
 
PPTX
Modern Web-site Development Pipeline
GlobalLogic Ukraine
 
PDF
Writing Services with ZF2
Mike Willbanks
 
PDF
Docker based-Pipelines with Codefresh
Codefresh
 
PDF
Using Grunt with Drupal
arithmetric
 
PPTX
Build Tools & Maven
David Simons
 
Portable CI wGitLab and Github led by Gavin Pickin.pdf
Ortus Solutions, Corp
 
Untangling fall2017 week2_try2
Derek Jacoby
 
Untangling fall2017 week2
Derek Jacoby
 
Build your android app with gradle
Swain Loda
 
Managing changes to eZPublish Database
Gaetano Giunta
 
Managing Changes to the Database Across the Project Life Cycle (presented by ...
eZ Systems
 
Habitat Workshop at Velocity London 2017
Mandi Walls
 
DockerCon 15 Keynote - Day 2
Docker, Inc.
 
Gocd – Kubernetes/Nomad Continuous Deployment
Leandro Totino Pereira
 
CT Software Developers Meetup: Using Docker and Vagrant Within A GitHub Pull ...
E. Camden Fisher
 
Embedded Systems: Lecture 10: Introduction to Git & GitHub (Part 1)
Ahmed El-Arabawy
 
Enterprise Griffon
James Williams
 
CloudFest 2018 Hackathon Project Results Presentation - CFHack18
Jeffrey J. Hardy
 
Starting from scratch in 2017
Stefano Bonetta
 
habitat at docker bud
Mandi Walls
 
Modern Web-site Development Pipeline
GlobalLogic Ukraine
 
Writing Services with ZF2
Mike Willbanks
 
Docker based-Pipelines with Codefresh
Codefresh
 
Using Grunt with Drupal
arithmetric
 
Build Tools & Maven
David Simons
 
Ad

Recently uploaded (20)

PDF
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
PPTX
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
PDF
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
PPTX
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
PDF
Lecture A - AI Workflows for Banking.pdf
Dr. LAM Yat-fai (林日辉)
 
PPTX
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
PDF
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
PPTX
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
PDF
Per Axbom: The spectacular lies of maps
Nexer Digital
 
PPTX
AI in Daily Life: How Artificial Intelligence Helps Us Every Day
vanshrpatil7
 
PPTX
The Future of AI & Machine Learning.pptx
pritsen4700
 
PDF
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
PPTX
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
PDF
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
PDF
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
PDF
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
PPTX
AVL ( audio, visuals or led ), technology.
Rajeshwri Panchal
 
PDF
Tea4chat - another LLM Project by Kerem Atam
a0m0rajab1
 
PPTX
Simple and concise overview about Quantum computing..pptx
mughal641
 
PDF
Generative AI vs Predictive AI-The Ultimate Comparison Guide
Lily Clark
 
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
Lecture A - AI Workflows for Banking.pdf
Dr. LAM Yat-fai (林日辉)
 
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
OA presentation.pptx OA presentation.pptx
pateldhruv002338
 
Per Axbom: The spectacular lies of maps
Nexer Digital
 
AI in Daily Life: How Artificial Intelligence Helps Us Every Day
vanshrpatil7
 
The Future of AI & Machine Learning.pptx
pritsen4700
 
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
OFFOFFBOX™ – A New Era for African Film | Startup Presentation
ambaicciwalkerbrian
 
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
AVL ( audio, visuals or led ), technology.
Rajeshwri Panchal
 
Tea4chat - another LLM Project by Kerem Atam
a0m0rajab1
 
Simple and concise overview about Quantum computing..pptx
mughal641
 
Generative AI vs Predictive AI-The Ultimate Comparison Guide
Lily Clark
 
Ad

Building gRPC services

  • 2. Backend Stack at TextNow • Build microservices using the Go programming language • Go pairs well with gRPC • Easy to create new networked services that are able to scale • Use a monorepo for all Go microservices • Easy to spin up new microservices • Take advantage of a common set of build & release tools • Linters, formatters, build rules, and more. • Use the gRPC framework for inter-service communication • Write clear, agreed upon contracts between our microservices • Improved the level of documentation for our internal microservices.
  • 3. Backend Stack at TextNow • Began adopting gRPC/Go 2 years ago • Our monorepo now has: • roughly 3000 commits • 34 contributors • roughly 3 dozen microservice daemons • roughly 2 dozen CLI tools • We have learned a lot along the way!
  • 4. Version 1.0 • textnow-mono’s first commit: • a tiny gRPC API • a daemon implementing the service • a CLI tool • The actual logic in the daemon wasn't significant • It provided a playground to try new things • Project structure? • Build tooling? • Proto generation?
  • 5. Version 1.0 Project Structure • proto/ • test_service.proto • services/ • service name (for example, testService) • pb/ • test_service.pb.go • api.go • Other .go files exist that offer the required business logic used by the API type • cmd/ • testServiced/ • main.go • testServiceCli/ • main.go
  • 6. Version 1.0 Proto Structure • All files in 1 directory • Each file defined it’s own namespace • Common types were imported across multiple protos • The option go_package directive was specified • Gave us control over mapping protos to services • Each service lived in the same package name • We called it ‘pb’ • It allowed us to distinguish auto-generated types from manually written types • Proto code was moved to the right package manually
  • 7. Version 1.0 Build System • Script to auto-generate proto files • Manually run `go build …` or `go run <package>`
  • 8. Version 1.0 Lessons Learned • Putting all the proto files into a single directory isn't a good idea! • Differentiating between public interfaces and private interfaces was very difficult. • A directory full of proto files didn't make it clear which were common types • Generating types in a separate package than the service adds unnecessary complexity. • Service definitions from different pb packages led to a lot of issues • Duplicate naming between manually written and proto-specified types is confusing • Can’t extend auto-generated types • Manual steps will be forgotten. • Edit the proto file • Build the .pb.go files • Copy them into the appropriate subdirectory • Leads to skew between what is committed in the proto file and what is built
  • 9. Version 2.0 • Major goals of v2.0 • Ease of development • Support for multiple build targets (binary, Docker image, etc.) • Support for gRPC build targets • Better build times (caching, parallelism, etc.) • Consistent builds across platforms • OS X and Linux
  • 10. Version 2.0 - Options • Makefiles � Tried and true � Easy to get started with � Makefiles get really complicated as the project grows � Remote caching is offered by the compiler, not the framework • Bazel � Fast, parallel builds with remote caching and build targets � Supports gRPC and build targets across many languages � Bleeding edge, going through active development � Documentation is sparse
  • 11. Version 2.0 – Building with Bazel • We chose to adopt Bazel as our build framework • rules_go specifies the build targets for the Go programming language • Native support for both proto and gRPC build targets • Use Gazelle to create build files • Analyzes proto and Go files and generates build targets automatically • Finds dependencies and updates accordingly • Handles vendored references natively
  • 12. Version 2.0 – Adopting Bazel 1. # brew install bazel 2. Create a WORKSPACE and BUILD.bazel file at the root of the project https://github.com/bazelbuild/rules_go has good defaults 3. # bazel run //:gazelle 4. # bazel build //… 5. Success? • In a new project? Yes • In an existing project? Maybe? • In textnow-mono? Definitely not
  • 13. Version 2.0 – Adopting Bazel • What broke? • Gazelle does not like multiple proto packages in one target • Gazelle does not generate proto targets out of path • Needed to manually write the build targets for our protos • We had checked in a number of auto-generated targets • Gazelle happily generated build targets for these files • Conflicted with the auto-generated build targets
  • 14. Version 2.0 – Project Structure • Changing our thinking about proto files solved our Bazel issues! • Internal gRPC service protos are declared in their service package • No more ‘pb’ packages • Types live where they are used • Can be extended if warranted • Public gRPC service protos are declared in a separate hierarchy • All services are versioned • Version exists in the package name • One or more services may implement the server interface • One service may easily implement multiple versions of the server interface
  • 15. Version 2.0 – Project Structure (Internal API) • services/ • service name (for example, testService) • api.go • test_service.proto • Other .go files exist that offer the required business logic used by the API type • cmd/ • testServiced/ • main.go • testServiceCli/ • main.go
  • 16. Version 2.0 – Project Structure (Public API) • api/ • test/ • v1/ • test_service.proto • services/ • service name (for example, testService) • api.go • Other .go files exist that offer the required business logic used by the API type • cmd/ • testServiced/ • main.go • testServiceCli/ • main.go
  • 17. Version 2.0 – Project Structure • Gazelle generates Go build targets for all protos • Build target has both client and server stubs • Gazelle auto-links these build targets in our services • We manually add build targets for our Dockerized services • Use rules_docker and go_image • We manually add other build targets for public APIs • Bazel supports Java, CC, PHP, etc. • Manually specify other language build targets • Bazel build then auto-generates client dependencies
  • 18. Version 2.0 - Example • https://github.com/rmrobinson-textnow/bazel-grpc-example • Public gRPC API with internal service • Service has both a Dockerized daemon and CLI tool • All built with Bazel!
  • 19. Version 2.0 - Issues • All proto/gRPC targets are built out of path • Bazel has its own filesystem path for build artifacts • Can’t access these files in IDEs • No autocomplete  • Build caches are necessary as projects get larger • Initial clones can take a long time to build • Build everything from scratch • Building for Docker on Mac is awkward • Must supply the --platform tag to Docker build targets • All said Version 2.0 is much better than Version 1.0
  • 20. Version 2.1 – API Field Validation • Specify the allowed data on a field, not just the type, in the proto • Easy for both sides of the API to see what should be allowed • https://github.com/mwitkow/go-proto-validators • Generates code for Go • Build type included in Gazelle • Lacks some dependencies making building without vendor tricky • https://github.com/envoyproxy/protoc-gen-validate • Generates code for Go, Java and Python (with C++ in development) • Has Bazel targets for the validator logic and the Go dependencies • Gazelle requires some hacky directives in top-level BUILD.bazel
  • 21. Version 2.1 – API Linting • We lint our code, we should also lint our APIs • Identify style, consistency or best-practice violations at commit time • https://github.com/uber/prototool • More than just a linter, a full proto build tool • Has several style guides, with rules for building your own • Lots of development underway • https://github.com/googleapis/api-linter • Enforces the Google style guide • Pretty simple
  • 22. Version 2.2 – Auto-generated client libraries • Given a service description, can we generate a full client SDK? • Pre-specified hostnames, ports, etc. • Automatic handling of retries, backoffs, etc. • Applied on a per-route basis! • https://github.com/googleapis/gapic-generator • Still under active development • No support for newer client languages (Swift, Kotlin) • One possible end state – APIs as SDKs