SlideShare a Scribd company logo
1 of 57
Cameron Waeland
Software Engineer
Rethinking CodeGen:
IDL, Thrift, gRPC, Ohh My…
InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
https://www.infoq.com/presentations/
compass-rest-grpc
Presented at QCon New York
www.qconnewyork.com
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
12 Months Ago -
Adding a new API Endpoint
Lots of steps
and coordination
across the stack
Difficult to
enforce standards
Proliferation
of similar APIs
Old API
Framework Issues
• Business logic spread
across Python API and
Java backend services
• Maintenance difficult for
developers
• Python API coupled to
backend
• Numerous ad hoc
endpoints
• Adding an HTTP API falls
to service developer
INTRODUCTION
Fast Forward 12 Months
Add a method 

to the relevant
Thrift IDL.
I'm a Compass developer and I want to add an endpoint:
1.
Implement the
method in a
backend
service.
2.
Run a
command.
3.
Get a beer.
4.
a. Generate APIs
b. Generate SDKs
c. Generate Documentation
Cameron Waeland
14°32´55˝ N, 20°59´35˝ E
Software Engineer APIs
• Leads API development at Compass
• Developer frameworks and tooling
• Service development/architecture
COMPASS COMPANY OVERVIEW |
2017
Our
Nationwide
Network
1600+
Agents in
30
offices across
10
major regions
East
West
Boston
New York City
The Hamptons
Washington DC
Miami
San Francisco
Santa Barbara &
Montecito
Los Angeles
Orange County
Aspen
COMPASS COMPANY OVERVIEW |
2017
7
Our Technology
The Compass technology suite
empowers agents to become trusted
advisors to their clients, build their
network and grow their business.
We provide value through technology
and data to our agents so they
can help their clients make smart real
estate decisions.
Advanced Search
Agent Mobile App
Listing Strategy
Markets App
Network
Performance Metrics Valuation
Insights
Deal Closer
Documents
Listing Presentation
Showsheets
Toursheets
Efficiency
Collections
Open House App
Client Portal
Relationship Building
COMPASS COMPANY OVERVIEW |
2017
Drive Toward
Market Leadership
$180M
Compass nearly tripled its
2016 revenue year-over-year to
more than
98%
Our technology and agent support has
led Compass to achieve industry-
leading retention of
#1 #1 TOP
5
Compass is ranked as #1
overall brokerage in Washington
D.C.
Compass is ranked as the #1
brokerage in single-family home
sales in San Francisco
Compass is a top 5 brokerage
by market share in New York,
San Francisco, Los Angeles,
Miami, D.C. and Boston
CodeGen
is a valuable tool
and less difficult
than you think
AGENDA
Birth of a CodeGen Framework
Code Generating an API
Going Further
Thrift to gRPC with CodeGen
What’s a Remote
Procedure Call?
• Interservice
communication over a
network
• Call remote method as
though it were local
• Low overhead, binary
format
• E.g. Thrift, GRPC, Avro
HTTP typically used for
external requests
ADDING GO SUPPORT FOR THRIFT SERVICES
Thrift @Compass
Developed by Facebook,
open sourced via Apache
Interfaces defined using
Interface Description Language
• Define services, methods,
types, etc.
• Inputs to a code
generation tool
• Clients and servers
generated for each
service in a target
language.
• Thrift IDL files are similar
to Protobuf
struct Blog {
1: optional i32 id; // The unique identifier of the blog post
2: optional i32 ownerId; // The userId of the post creator
3: optional string title; // The title of the blog post
4: optional string content; // The content of the blog post
}
struct ViewPostRequest {
1: optional i32 blogId;
}
struct ViewPostResponse {
1: optional base.ResponseStatus status;
2: optional Blog post;
}
struct CreatePostRequest {
1: optional i32 userId; // The user creating the new post
2: optional Blog blog;
}
struct CreatePostResponse {
1: optional base.ResponseStatus status;
2: optional Blog blog;
}
service BlogService {
ViewPostResponse viewPost(1: ViewPostRequest request);
CreatePostResponse createPost(1: CreatePostRequest request);
}
Thrift IDL
• Similar to Protobuf
Blog Service
• Exposes a read
method and a
write method
• Single model
object
• Basic request/
response structs
ADDING GO SUPPORT FOR THRIFT SERVICES
Go Code Generator…
…or a gopher appears
—
Parallel initiative to add Go for backend services.
We were excited by Go’s:
• Concurrency primitives & performance
• Static typing & unambiguous syntax.
But:
• Support in the official Thrift project was poor.
• We needed to build our own generator for Thrift -> Go
Common Code Gen 

Misconceptions
—
• High level of complexity
• Produces messy unreadable code
• Poorly performing code
• Not as good as handwritten code
Thrift to Go Generator
—
• Used go-thrift - Thrift parser
• Library generates Abstract Syntax Trees
• ASTs provide Thrift file contents in a structured way -
Enums, Structs, Services, Methods, etc.
• Think compilers but not as scary!
Code Generation Pipeline
—
• Built Go template files compatible with the AST
generated.
• With template files and ASTs we were able to render
out idiomatic Go code.
• Generated files are compiled into a compass-thrift
binary for use by services.
AST Model
• Using a Parsing
Expression Grammar
(PEG) the file is broken
down into key sections
• Set of rules for string
recognition (think
regexes)
• Choice operator selects
the first match
• The file is represented as a
tree using Go data
structures.
• Field & method names,
type information become
easily accessible.
BIRTH OF CODEGEN FRAMEWORK
Looking at a Struct
AST
BIRTH OF CODEGEN FRAMEWORK
"Blog": {
"Name": "Blog",
"Fields": [
{
"ID": 1,
"Name": "id",
"Type": {"Name": "i32"}
},
{
"ID": 2,
"Name": "ownerId",
"Type": {"Name": "i32"}
},
{
"ID": 3,
"Name": "title",
"Type": {"Name": "string"}
},
{
"ID": 4,
"Name": "content",
"Type": {"Name": "string"}
}
]
}
struct Blog {
1: optional i32 id; // The unique identifier of the blog post
2: optional i32 ownerId; // The userId of the post creator
3: optional string title; // The title of the blog post
4: optional string content; // The content of the blog post
}
Go Templates for
Generation
BIRTH OF CODEGEN FRAMEWORK
type Blog struct {
ID *int32 `thrift:"1" json:"id,omitempty" param:"id" `
OwnerID *int32 `thrift:"2" json:"ownerId,omitempty" param:"ownerId" `
Title *string `thrift:"3" json:"title,omitempty" param:"title" `
Content *string `thrift:"4" json:"content,omitempty" param:"content" `
}
type {{$name|Export}} struct {
type {{$name|Export}} struct {
{{range . -}}
{{.Name|Export}} {{if .|ShouldDereference}}*{{end}}{{.|FieldType}} ` +
"`thrift:"{{.ID}}{{if .Type|IsSet}},set{{end}}"" +
" json:"{{.Name}},omitempty"" +
" param:"{{.Name}}"" +
" {{if Injectable .}}inject:"{{(Injectable .)}}"{{end -}}`" + `
{{end -}}
}
}
Dispelling the

Misconceptions
—
Thrift's clear syntax:
• Straightforward templates
• Create clean and performant code
• Add the features we want
AGENDA
Birth of a CodeGen Framework
Code Generating an API
Going Further
Thrift to gRPC with CodeGen
Motivation for a New
API Framework
—
• Monolithic Python app
• New endpoints built ad hoc by frontend devs
• Business logic spread across services and Python
app
• Inconsistent & difficult to maintain
Where did
we start?
• Consistency - validation,
request/response formats,
URL patterns
• Discoverability -
complete, accurate and
generated API
documentation
• Productivity - generated
API clients, insights
(performance, errors,
availability)
CODE GENERATING AN API
Requirements Gathering!
Group discussions/
interviews with client/service
developers.
Key requirements:
Thrift RPC 

Everywhere
Python App
REST Proxy for
Backend Services
Go Code
Generator
Available Compass Tech
During Design Phase
A Common Refrain
—
How does this endpoint work?
Check the Thrift...
What does this parameter do?
Look at the Thrift...
Is there any documentation?
Read the Thrift…
A pattern emerges… Thrift is the source of truth
General Approach
—
• Thrift emphasis - should be source of truth for any API
• Now have access to a Thrift CodeGen framework
• Let's try an API framework generated directly from Thrift
• HTTP to Thrift reverse proxy
• Go Application - simpler to extend already generated code
The Prototypes
—
• 2 reverse proxy prototypes generated from Thrift
• First would be a more traditional JSON REST API
• Second would be a more experimental GraphQL API
What We Chose
—
Decision: JSON REST API
As much as we tried to justify using GraphQL…
• Risk mitigation - easier migration of clients
• Engineer familiarity with REST
• Relative immaturity of GraphQL
Three Key Aspects of an 

API CodeGen Framework
Source of Truth
In our case Thrift, but could be
database model, object model, etc.
1.
Request
Context
2.
Deployment,
Operations &
Developer
Tooling
3.
HTTP, session info, etc. Supporting development
Performance monitoring, high
availability, etc.
1. Source of Truth
—
• Already established that Thrift is our source of truth
• For an HTTP API we were just short of all the info we
would need
• The "Aha!" moment - Thrift annotations
Annotations
• Language feature of Thrift
• Applied to services,
methods, structs and fields
• Similar to Java - control
parser/compiler behavior
• Specify arbitrary key/value
metadata
• Small modification to
parser to support
CODE GENERATING AN API
service BlogService extends base.BaseService {
ViewPostResponse viewPost(1: ViewPostRequest request)
(api.url="/blog/:blogId", api.method="GET");
CreatePostResponse createPost(1: CreatePostRequest request)
(api.url="/blog", api.method="POST", api.roles="Editor");
} (api.url="/blog")
Example
• Top level URL for the service
• Per-method URLs including
resource IDs
• HTTP request methods
• Endpoint permission
requirements
2. Request Context
—
• URL route + HTTP method to service method binding
• Endpoint authorization
• Python API style convenience features
• Current user
• File uploads
• User permissions
• HTTP headers
• etc.
Injectors
• Introduced special Thrift
annotation for struct fields
• API will inject relevant
information into annotated
fields.
• (api.path)
• The value of ":blogId" as
defined in the path will be
injected into this field.
• (api.inject="userId")
• ID of requesting user will
injected into this field.
CODE GENERATING AN API
struct ViewPostRequest {
1: optional i32 blogId (api.path);
}
struct ViewPostResponse {
1: optional base.ResponseStatus status;
2: optional Blog post;
}
struct CreatePostRequest {
1: optional i32 userId (api.inject="userId");
2: optional Blog blog;
}
struct CreatePostResponse {
1: optional base.ResponseStatus status;
2: optional i64 result;
}
Key Benefits
—
1. Reduces logic and custom code in Python mono app.
2. Thrift files are “source of truth” for all API interactions.
3. Consistent & predictable API.
4. Improved developer productivity

AGENDA
Birth of a CodeGen Framework
Code Generating an API
Going Further
Thrift to gRPC with CodeGen
Code Generating
Clients
• Improve consistency for
frontend developers
• Clients define and maintain
their own templates
• Use structure or
frameworks that they want
• API team doesn't need to
maintain them
GOING FURTHER
Code Generating Documentation
—
• We added a documentation template
• The output is RESTful API Modeling Language
(RAML)
• Supports custom extensions and complex objects in
GET.
• Updated our parser to surface Thrift comments
• Hosted and easily accessible to developers
Key Benefits
—
1. Reduces logic and custom code in Python mono app.
2. Thrift files are “source of truth” for all API interactions.
3. Consistent & predictable API.
4. Automatically generated client code - clients instantly get
backend changes
5. Improved developer productivity

AGENDA
Birth of a CodeGen Framework
Code Generating an API
Going Further
Thrift to gRPC with CodeGen
Introducing gRPC @Compass
—
• Increased commitment to microservices
• Started to re-evaluate our usage of Thrift
• Experienced several frustrations:
• Low quality Python Thrift Server
• Reliability of connections
• General lack of Thrift development
• Investigated gRPC by Google after it was open sourced.
gRPC Advantages
—
• gRPC fills same role as Thrift
• RPC framework for polyglot architectures via CodeGen
• However it also provides:
• Bi-directional streaming using http/2
• Customizability - pluggable auth, tracing, load balancing
and health checking
• Built-in connection retrying
• Efficient, idiomatic clients and servers.
Code Generated Migration
—
• A problem - gRPC recommends protobuf as its IDL
• Compass has a substantial number of Thrift files
• gRPC pluggability + CodeGen framework
• Migrate and keep our Thrift files
• Wrote our own Thrift codec which we plugged into gRPC.
gRPC Stub Example
—
Code generated client/server extensions in target languages
public class BlogServiceGRPC extends com.urbancompass.common.base.BaseServiceGRPC {
private static final Empty __EMPTY = new Empty();
private static final MethodDescriptor<CreatePostRequest, CreatePostResponse> __METHOD_DESCRIPTOR_CREATE_POST =
MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.UNARY,
generateFullMethodName("BlogService", "createPost"),
new ThriftMarshaller<>(CreatePostRequest.class),
new ThriftMarshaller<>(CreatePostResponse.class)
);
private static final MethodDescriptor<ViewPostRequest, ViewPostResponse> __METHOD_DESCRIPTOR_VIEW_POST =
MethodDescriptor.create(
io.grpc.MethodDescriptor.MethodType.UNARY,
generateFullMethodName("BlogService", "viewPost"),
new ThriftMarshaller<>(ViewPostRequest.class),
new ThriftMarshaller<>(ViewPostResponse.class)
);
Where are we today?
Go API + gRPC active
in production -
mobile/web clients
Go API is
deployed as a
single service
Backend service
changes require
regen and
deployment
New Architecture
—
• All requests reverse proxied through Go API
• App services are annotated and available to clients
• Core/Internal services only accessed via App services
Where should we go next?
Future goal - deploy
an instance of API v3
per service
Service change only
affects its API v3
instance to regen
Each Go API instance
registers with load
balancer/reverse proxy
Future Infrastructure Diagram
Other Future Opportunities
—
Robust JavaScript (or TypeScript) SDK
• Leverage Thrift type information in our JS
• Give our developers convenient static analysis
Moving to Protobuf
• Protobuf is the standard IDL format for gRPC
• Standardize our stack - leverage more of the ecosystem


Evaluating GRPC-WEB
• New protocol - web clients speak with gRPC using JSON over HTTP
• Eventually - browsers can use native gRPC over HTTP/2 via
upcoming whatwg fetch/streams API
Wrapping Up
• CodeGen is a valuable tool
• Cut 2 major time consuming steps for our devs
• We migrated to a new RPC framework
• Generated an API from a single source of truth
• It is easier than you think
• Straightforward to build
• Easy to extend to new applications
CodeGen at Compass has allowed us to rapidly transform our
stack over the last year.
Rapidly Growing and Hiring for multiple positions
www.compass.com/careers/jobs/
Thanks!
Watch the video with slide
synchronization on InfoQ.com!
https://www.infoq.com/presentations/
compass-rest-grpc

More Related Content

More from C4Media

More from C4Media (20)

Shifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CDShifting Left with Cloud Native CI/CD
Shifting Left with Cloud Native CI/CD
 
CI/CD for Machine Learning
CI/CD for Machine LearningCI/CD for Machine Learning
CI/CD for Machine Learning
 
Fault Tolerance at Speed
Fault Tolerance at SpeedFault Tolerance at Speed
Fault Tolerance at Speed
 
Architectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep SystemsArchitectures That Scale Deep - Regaining Control in Deep Systems
Architectures That Scale Deep - Regaining Control in Deep Systems
 
ML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.jsML in the Browser: Interactive Experiences with Tensorflow.js
ML in the Browser: Interactive Experiences with Tensorflow.js
 
Build Your Own WebAssembly Compiler
Build Your Own WebAssembly CompilerBuild Your Own WebAssembly Compiler
Build Your Own WebAssembly Compiler
 
User & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix ScaleUser & Device Identity for Microservices @ Netflix Scale
User & Device Identity for Microservices @ Netflix Scale
 
Scaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's EdgeScaling Patterns for Netflix's Edge
Scaling Patterns for Netflix's Edge
 
Make Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home EverywhereMake Your Electron App Feel at Home Everywhere
Make Your Electron App Feel at Home Everywhere
 
The Talk You've Been Await-ing For
The Talk You've Been Await-ing ForThe Talk You've Been Await-ing For
The Talk You've Been Await-ing For
 
Future of Data Engineering
Future of Data EngineeringFuture of Data Engineering
Future of Data Engineering
 
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and MoreAutomated Testing for Terraform, Docker, Packer, Kubernetes, and More
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
 
Navigating Complexity: High-performance Delivery and Discovery Teams
Navigating Complexity: High-performance Delivery and Discovery TeamsNavigating Complexity: High-performance Delivery and Discovery Teams
Navigating Complexity: High-performance Delivery and Discovery Teams
 
High Performance Cooperative Distributed Systems in Adtech
High Performance Cooperative Distributed Systems in AdtechHigh Performance Cooperative Distributed Systems in Adtech
High Performance Cooperative Distributed Systems in Adtech
 
Rust's Journey to Async/await
Rust's Journey to Async/awaitRust's Journey to Async/await
Rust's Journey to Async/await
 
Opportunities and Pitfalls of Event-Driven Utopia
Opportunities and Pitfalls of Event-Driven UtopiaOpportunities and Pitfalls of Event-Driven Utopia
Opportunities and Pitfalls of Event-Driven Utopia
 
Datadog: a Real-Time Metrics Database for One Quadrillion Points/Day
Datadog: a Real-Time Metrics Database for One Quadrillion Points/DayDatadog: a Real-Time Metrics Database for One Quadrillion Points/Day
Datadog: a Real-Time Metrics Database for One Quadrillion Points/Day
 
Are We Really Cloud-Native?
Are We Really Cloud-Native?Are We Really Cloud-Native?
Are We Really Cloud-Native?
 
CockroachDB: Architecture of a Geo-Distributed SQL Database
CockroachDB: Architecture of a Geo-Distributed SQL DatabaseCockroachDB: Architecture of a Geo-Distributed SQL Database
CockroachDB: Architecture of a Geo-Distributed SQL Database
 
A Dive into Streams @LinkedIn with Brooklin
A Dive into Streams @LinkedIn with BrooklinA Dive into Streams @LinkedIn with Brooklin
A Dive into Streams @LinkedIn with Brooklin
 

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Recently uploaded (20)

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 

Rethinking CodeGen: IDL, Thrift, gRPC, Ohh My

  • 1. Cameron Waeland Software Engineer Rethinking CodeGen: IDL, Thrift, gRPC, Ohh My…
  • 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! https://www.infoq.com/presentations/ compass-rest-grpc
  • 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  • 4. 12 Months Ago - Adding a new API Endpoint Lots of steps and coordination across the stack Difficult to enforce standards Proliferation of similar APIs
  • 5. Old API Framework Issues • Business logic spread across Python API and Java backend services • Maintenance difficult for developers • Python API coupled to backend • Numerous ad hoc endpoints • Adding an HTTP API falls to service developer INTRODUCTION
  • 6. Fast Forward 12 Months Add a method 
 to the relevant Thrift IDL. I'm a Compass developer and I want to add an endpoint: 1. Implement the method in a backend service. 2. Run a command. 3. Get a beer. 4. a. Generate APIs b. Generate SDKs c. Generate Documentation
  • 7. Cameron Waeland 14°32´55˝ N, 20°59´35˝ E Software Engineer APIs • Leads API development at Compass • Developer frameworks and tooling • Service development/architecture
  • 8. COMPASS COMPANY OVERVIEW | 2017 Our Nationwide Network 1600+ Agents in 30 offices across 10 major regions East West Boston New York City The Hamptons Washington DC Miami San Francisco Santa Barbara & Montecito Los Angeles Orange County Aspen
  • 9. COMPASS COMPANY OVERVIEW | 2017 7 Our Technology The Compass technology suite empowers agents to become trusted advisors to their clients, build their network and grow their business. We provide value through technology and data to our agents so they can help their clients make smart real estate decisions. Advanced Search Agent Mobile App Listing Strategy Markets App Network Performance Metrics Valuation Insights Deal Closer Documents Listing Presentation Showsheets Toursheets Efficiency Collections Open House App Client Portal Relationship Building
  • 10. COMPASS COMPANY OVERVIEW | 2017 Drive Toward Market Leadership $180M Compass nearly tripled its 2016 revenue year-over-year to more than 98% Our technology and agent support has led Compass to achieve industry- leading retention of #1 #1 TOP 5 Compass is ranked as #1 overall brokerage in Washington D.C. Compass is ranked as the #1 brokerage in single-family home sales in San Francisco Compass is a top 5 brokerage by market share in New York, San Francisco, Los Angeles, Miami, D.C. and Boston
  • 11. CodeGen is a valuable tool and less difficult than you think
  • 12. AGENDA Birth of a CodeGen Framework Code Generating an API Going Further Thrift to gRPC with CodeGen
  • 13. What’s a Remote Procedure Call? • Interservice communication over a network • Call remote method as though it were local • Low overhead, binary format • E.g. Thrift, GRPC, Avro HTTP typically used for external requests ADDING GO SUPPORT FOR THRIFT SERVICES
  • 14. Thrift @Compass Developed by Facebook, open sourced via Apache Interfaces defined using Interface Description Language • Define services, methods, types, etc. • Inputs to a code generation tool • Clients and servers generated for each service in a target language. • Thrift IDL files are similar to Protobuf struct Blog { 1: optional i32 id; // The unique identifier of the blog post 2: optional i32 ownerId; // The userId of the post creator 3: optional string title; // The title of the blog post 4: optional string content; // The content of the blog post } struct ViewPostRequest { 1: optional i32 blogId; } struct ViewPostResponse { 1: optional base.ResponseStatus status; 2: optional Blog post; } struct CreatePostRequest { 1: optional i32 userId; // The user creating the new post 2: optional Blog blog; } struct CreatePostResponse { 1: optional base.ResponseStatus status; 2: optional Blog blog; } service BlogService { ViewPostResponse viewPost(1: ViewPostRequest request); CreatePostResponse createPost(1: CreatePostRequest request); } Thrift IDL • Similar to Protobuf Blog Service • Exposes a read method and a write method • Single model object • Basic request/ response structs ADDING GO SUPPORT FOR THRIFT SERVICES
  • 15. Go Code Generator… …or a gopher appears — Parallel initiative to add Go for backend services. We were excited by Go’s: • Concurrency primitives & performance • Static typing & unambiguous syntax. But: • Support in the official Thrift project was poor. • We needed to build our own generator for Thrift -> Go
  • 16. Common Code Gen 
 Misconceptions — • High level of complexity • Produces messy unreadable code • Poorly performing code • Not as good as handwritten code
  • 17. Thrift to Go Generator — • Used go-thrift - Thrift parser • Library generates Abstract Syntax Trees • ASTs provide Thrift file contents in a structured way - Enums, Structs, Services, Methods, etc. • Think compilers but not as scary!
  • 18. Code Generation Pipeline — • Built Go template files compatible with the AST generated. • With template files and ASTs we were able to render out idiomatic Go code. • Generated files are compiled into a compass-thrift binary for use by services.
  • 19. AST Model • Using a Parsing Expression Grammar (PEG) the file is broken down into key sections • Set of rules for string recognition (think regexes) • Choice operator selects the first match • The file is represented as a tree using Go data structures. • Field & method names, type information become easily accessible. BIRTH OF CODEGEN FRAMEWORK
  • 20. Looking at a Struct AST BIRTH OF CODEGEN FRAMEWORK "Blog": { "Name": "Blog", "Fields": [ { "ID": 1, "Name": "id", "Type": {"Name": "i32"} }, { "ID": 2, "Name": "ownerId", "Type": {"Name": "i32"} }, { "ID": 3, "Name": "title", "Type": {"Name": "string"} }, { "ID": 4, "Name": "content", "Type": {"Name": "string"} } ] } struct Blog { 1: optional i32 id; // The unique identifier of the blog post 2: optional i32 ownerId; // The userId of the post creator 3: optional string title; // The title of the blog post 4: optional string content; // The content of the blog post }
  • 21. Go Templates for Generation BIRTH OF CODEGEN FRAMEWORK type Blog struct { ID *int32 `thrift:"1" json:"id,omitempty" param:"id" ` OwnerID *int32 `thrift:"2" json:"ownerId,omitempty" param:"ownerId" ` Title *string `thrift:"3" json:"title,omitempty" param:"title" ` Content *string `thrift:"4" json:"content,omitempty" param:"content" ` } type {{$name|Export}} struct { type {{$name|Export}} struct { {{range . -}} {{.Name|Export}} {{if .|ShouldDereference}}*{{end}}{{.|FieldType}} ` + "`thrift:"{{.ID}}{{if .Type|IsSet}},set{{end}}"" + " json:"{{.Name}},omitempty"" + " param:"{{.Name}}"" + " {{if Injectable .}}inject:"{{(Injectable .)}}"{{end -}}`" + ` {{end -}} } }
  • 22.
  • 23. Dispelling the
 Misconceptions — Thrift's clear syntax: • Straightforward templates • Create clean and performant code • Add the features we want
  • 24. AGENDA Birth of a CodeGen Framework Code Generating an API Going Further Thrift to gRPC with CodeGen
  • 25. Motivation for a New API Framework — • Monolithic Python app • New endpoints built ad hoc by frontend devs • Business logic spread across services and Python app • Inconsistent & difficult to maintain
  • 26. Where did we start? • Consistency - validation, request/response formats, URL patterns • Discoverability - complete, accurate and generated API documentation • Productivity - generated API clients, insights (performance, errors, availability) CODE GENERATING AN API Requirements Gathering! Group discussions/ interviews with client/service developers. Key requirements: Thrift RPC 
 Everywhere Python App REST Proxy for Backend Services Go Code Generator Available Compass Tech During Design Phase
  • 27. A Common Refrain — How does this endpoint work? Check the Thrift... What does this parameter do? Look at the Thrift... Is there any documentation? Read the Thrift… A pattern emerges… Thrift is the source of truth
  • 28. General Approach — • Thrift emphasis - should be source of truth for any API • Now have access to a Thrift CodeGen framework • Let's try an API framework generated directly from Thrift • HTTP to Thrift reverse proxy • Go Application - simpler to extend already generated code
  • 29. The Prototypes — • 2 reverse proxy prototypes generated from Thrift • First would be a more traditional JSON REST API • Second would be a more experimental GraphQL API
  • 30. What We Chose — Decision: JSON REST API As much as we tried to justify using GraphQL… • Risk mitigation - easier migration of clients • Engineer familiarity with REST • Relative immaturity of GraphQL
  • 31. Three Key Aspects of an 
 API CodeGen Framework Source of Truth In our case Thrift, but could be database model, object model, etc. 1. Request Context 2. Deployment, Operations & Developer Tooling 3. HTTP, session info, etc. Supporting development Performance monitoring, high availability, etc.
  • 32. 1. Source of Truth — • Already established that Thrift is our source of truth • For an HTTP API we were just short of all the info we would need • The "Aha!" moment - Thrift annotations
  • 33. Annotations • Language feature of Thrift • Applied to services, methods, structs and fields • Similar to Java - control parser/compiler behavior • Specify arbitrary key/value metadata • Small modification to parser to support CODE GENERATING AN API service BlogService extends base.BaseService { ViewPostResponse viewPost(1: ViewPostRequest request) (api.url="/blog/:blogId", api.method="GET"); CreatePostResponse createPost(1: CreatePostRequest request) (api.url="/blog", api.method="POST", api.roles="Editor"); } (api.url="/blog") Example • Top level URL for the service • Per-method URLs including resource IDs • HTTP request methods • Endpoint permission requirements
  • 34. 2. Request Context — • URL route + HTTP method to service method binding • Endpoint authorization • Python API style convenience features • Current user • File uploads • User permissions • HTTP headers • etc.
  • 35. Injectors • Introduced special Thrift annotation for struct fields • API will inject relevant information into annotated fields. • (api.path) • The value of ":blogId" as defined in the path will be injected into this field. • (api.inject="userId") • ID of requesting user will injected into this field. CODE GENERATING AN API struct ViewPostRequest { 1: optional i32 blogId (api.path); } struct ViewPostResponse { 1: optional base.ResponseStatus status; 2: optional Blog post; } struct CreatePostRequest { 1: optional i32 userId (api.inject="userId"); 2: optional Blog blog; } struct CreatePostResponse { 1: optional base.ResponseStatus status; 2: optional i64 result; }
  • 36.
  • 37. Key Benefits — 1. Reduces logic and custom code in Python mono app. 2. Thrift files are “source of truth” for all API interactions. 3. Consistent & predictable API. 4. Improved developer productivity

  • 38. AGENDA Birth of a CodeGen Framework Code Generating an API Going Further Thrift to gRPC with CodeGen
  • 39. Code Generating Clients • Improve consistency for frontend developers • Clients define and maintain their own templates • Use structure or frameworks that they want • API team doesn't need to maintain them GOING FURTHER
  • 40.
  • 41. Code Generating Documentation — • We added a documentation template • The output is RESTful API Modeling Language (RAML) • Supports custom extensions and complex objects in GET. • Updated our parser to surface Thrift comments • Hosted and easily accessible to developers
  • 42.
  • 43. Key Benefits — 1. Reduces logic and custom code in Python mono app. 2. Thrift files are “source of truth” for all API interactions. 3. Consistent & predictable API. 4. Automatically generated client code - clients instantly get backend changes 5. Improved developer productivity

  • 44. AGENDA Birth of a CodeGen Framework Code Generating an API Going Further Thrift to gRPC with CodeGen
  • 45. Introducing gRPC @Compass — • Increased commitment to microservices • Started to re-evaluate our usage of Thrift • Experienced several frustrations: • Low quality Python Thrift Server • Reliability of connections • General lack of Thrift development • Investigated gRPC by Google after it was open sourced.
  • 46. gRPC Advantages — • gRPC fills same role as Thrift • RPC framework for polyglot architectures via CodeGen • However it also provides: • Bi-directional streaming using http/2 • Customizability - pluggable auth, tracing, load balancing and health checking • Built-in connection retrying • Efficient, idiomatic clients and servers.
  • 47. Code Generated Migration — • A problem - gRPC recommends protobuf as its IDL • Compass has a substantial number of Thrift files • gRPC pluggability + CodeGen framework • Migrate and keep our Thrift files • Wrote our own Thrift codec which we plugged into gRPC.
  • 48. gRPC Stub Example — Code generated client/server extensions in target languages public class BlogServiceGRPC extends com.urbancompass.common.base.BaseServiceGRPC { private static final Empty __EMPTY = new Empty(); private static final MethodDescriptor<CreatePostRequest, CreatePostResponse> __METHOD_DESCRIPTOR_CREATE_POST = MethodDescriptor.create( io.grpc.MethodDescriptor.MethodType.UNARY, generateFullMethodName("BlogService", "createPost"), new ThriftMarshaller<>(CreatePostRequest.class), new ThriftMarshaller<>(CreatePostResponse.class) ); private static final MethodDescriptor<ViewPostRequest, ViewPostResponse> __METHOD_DESCRIPTOR_VIEW_POST = MethodDescriptor.create( io.grpc.MethodDescriptor.MethodType.UNARY, generateFullMethodName("BlogService", "viewPost"), new ThriftMarshaller<>(ViewPostRequest.class), new ThriftMarshaller<>(ViewPostResponse.class) );
  • 49. Where are we today? Go API + gRPC active in production - mobile/web clients Go API is deployed as a single service Backend service changes require regen and deployment
  • 50. New Architecture — • All requests reverse proxied through Go API • App services are annotated and available to clients • Core/Internal services only accessed via App services
  • 51. Where should we go next? Future goal - deploy an instance of API v3 per service Service change only affects its API v3 instance to regen Each Go API instance registers with load balancer/reverse proxy
  • 53. Other Future Opportunities — Robust JavaScript (or TypeScript) SDK • Leverage Thrift type information in our JS • Give our developers convenient static analysis Moving to Protobuf • Protobuf is the standard IDL format for gRPC • Standardize our stack - leverage more of the ecosystem 
 Evaluating GRPC-WEB • New protocol - web clients speak with gRPC using JSON over HTTP • Eventually - browsers can use native gRPC over HTTP/2 via upcoming whatwg fetch/streams API
  • 54. Wrapping Up • CodeGen is a valuable tool • Cut 2 major time consuming steps for our devs • We migrated to a new RPC framework • Generated an API from a single source of truth • It is easier than you think • Straightforward to build • Easy to extend to new applications CodeGen at Compass has allowed us to rapidly transform our stack over the last year.
  • 55. Rapidly Growing and Hiring for multiple positions www.compass.com/careers/jobs/ Thanks!
  • 56.
  • 57. Watch the video with slide synchronization on InfoQ.com! https://www.infoq.com/presentations/ compass-rest-grpc