Plus, considerations in updating one of GitHub’s oldest and most heavily used features.
Overview
This article details how GitHub rebuilt its Issues search system to support nested queries with boolean AND/OR operators and parentheses. The engineering team replaced the flat query parser with an Abstract Syntax Tree (AST)-based parser using PEG grammar, mapped it to Elasticsearch bool queries, and carefully rolled out the feature using dark-shipping, performance comparison with the Scientist library, and incremental deployment to handle nearly 2,000 queries per second without breaking backward compatibility.
What You'll Learn
How to parse complex nested search queries using PEG grammar and Abstract Syntax Trees
How to map boolean search operators (AND/OR/NOT) to Elasticsearch bool query clauses (must/should/must_not)
How to safely refactor a high-traffic search system using dark-shipping and the Scientist library for production validation
Why backward compatibility testing through dual-execution and result comparison is critical for search system rewrites
How to incrementally roll out a major feature change to minimize risk for millions of daily users
Prerequisites & Requirements
- Understanding of search query syntax and how search systems work at a high level
- Familiarity with Elasticsearch and its query DSL, particularly bool queries
- Basic understanding of parsing concepts including Abstract Syntax Trees and grammars
- Experience with backend systems handling high query volumes(optional)
Key Questions Answered
How did GitHub implement boolean AND/OR operators in Issues search?
Why did GitHub switch from flat list parsing to an AST for search queries?
How did GitHub ensure backward compatibility when rebuilding Issues search?
What is dark-shipping and how does GitHub use it to validate search changes?
How does GitHub handle performance testing when refactoring critical search paths?
What limits does GitHub impose on nested search query complexity?
How does the parslet PEG grammar handle operator precedence in boolean search queries?
What was GitHub's rollout strategy for the new Issues search system?
Key Statistics & Figures
Technologies & Tools
Some links below are affiliate links. We may earn a commission if you make a purchase.
Key Actionable Insights
1When rewriting a high-traffic search system, use dark-shipping to validate correctness by running a small percentage of production queries against both old and new systems simultaneously. Compare result counts as a first approximation of correctness before deeper validation.GitHub ran 1% of issue searches through both systems and compared the number of results returned. Differences in result count within a short time window indicated bugs that needed fixing before user-facing rollout.
2Use PEG (Parsing Expression Grammar) and AST-based parsing when your query language needs to support recursive or nested structures like boolean operators with parentheses. Flat list parsing is insufficient for anything beyond simple AND-joined filters.GitHub used the parslet Ruby library to define a PEG grammar that supports both legacy flat queries and new nested boolean queries, ensuring backward compatibility while enabling the new syntax.
3Map boolean search operators directly to Elasticsearch's bool query clauses: AND to 'must', OR to 'should', and NOT to 'must_not'. Recursively traverse your AST to build the nested query document, reusing existing filter-to-query building blocks.This mapping provides a natural translation from user-facing search syntax to Elasticsearch's native query DSL, allowing complex nested queries without requiring a custom query execution engine.
4Use a library like GitHub's open-source Scientist to safely refactor critical code paths by running old and new implementations side-by-side in production and comparing results and performance without affecting users.Scientist enabled GitHub to compare query performance between the old and new search systems on live traffic, establishing baselines for new nested queries while catching regressions in existing simple queries.
5When rolling out risky changes to high-traffic features, limit blast radius by deploying to a subset of interfaces first (e.g., only the GraphQL API and one UI surface) before expanding to all consumers like REST APIs and dashboards.GitHub first shipped the new search only in the GraphQL API and repository Issues tab, collecting feedback and fixing issues before rolling out to the Issues dashboard and REST API, protecting millions of daily users.
6Impose practical limits on query complexity based on user research rather than arbitrary technical constraints. Customer interviews can reveal the right balance between power and usability for features like nested search.GitHub limited nesting depth to five levels after conducting customer interviews, finding this to be the sweet spot where users had sufficient flexibility without creating overly complex, hard-to-understand queries.