{"id":22095,"date":"2024-12-19T06:00:29","date_gmt":"2024-12-19T14:00:29","guid":{"rendered":"https:\/\/engineering.fb.com\/?p=22095"},"modified":"2024-12-19T13:21:22","modified_gmt":"2024-12-19T21:21:22","slug":"glean-open-source-code-indexing","status":"publish","type":"post","link":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/","title":{"rendered":"Indexing code at scale with Glean"},"content":{"rendered":"<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">We\u2019re sharing details about <\/span><a href=\"https:\/\/glean.software\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Glean<\/span><\/a><span style=\"font-weight: 400;\">, Meta\u2019s open source system for collecting, deriving, and working with facts about source code.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">In this blog post we\u2019ll talk about why a system like Glean is important, explain the rationale for Glean\u2019s design, and run through some of the ways we\u2019re using Glean to supercharge our developer tooling at Meta.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">In August 2021 we open-sourced our code indexing system <\/span><a href=\"https:\/\/glean.software\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Glean<\/span><\/a><span style=\"font-weight: 400;\">. Glean collects information about source code and provides it to developer tools through an efficient and flexible query language. We use Glean widely within Meta to power a range of developer tools including code browsing, code search, and documentation generation.<\/span><\/p>\n<h2>Code Indexing<\/h2>\n<p><span style=\"font-weight: 400;\">Many tools that developers use rely on information extracted from the code they\u2019re working on. For example:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Code navigation (\u201cGo to definition\u201d) in an IDE or a code browser;<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Code search;<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Automatically-generated documentation;<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Code analysis tools, such as dead code detection or linting.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The job of collecting information from code is often called <\/span><i><span style=\"font-weight: 400;\">code indexing<\/span><\/i><span style=\"font-weight: 400;\">. A code indexing system\u2019s job is to efficiently answer the questions your tools need to ask, such as, \u201cWhere is the definition of <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">MyClass<\/span><span style=\"font-weight: 400;\">?\u201d or \u201cWhich functions are defined in <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">myfile.cpp<\/span><span style=\"font-weight: 400;\">?\u201d<\/span><\/p>\n<p><span style=\"font-weight: 400;\">An IDE will typically do indexing as needed, when you load a new file or project for example. But the larger your codebase, the more important it becomes to do code indexing ahead of time. For large projects it becomes impractical to have the IDE process all the code of your project at startup and, depending on what language you\u2019re using, that point may come earlier or later: C++ in particular is problematic due to the long compile times.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Moreover, with a larger codebase and many developers working on it, it makes sense to have a shared centralized indexing system so that we don\u2019t repeat the work of indexing on every developer\u2019s machine. And as the data produced by indexing can become large, we want to make it available over the network through a query interface rather than having to download it.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This leads to an architecture like this:<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-22098\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-1.png?w=768\" alt=\"\" width=\"636\" height=\"450\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-1.png 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-1.png?resize=96,68 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-1.png?resize=192,136 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/span><\/p>\n<p><span style=\"font-weight: 400;\">In practice the real architecture is highly distributed:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Indexing can be heavily parallelized and we may have many indexing jobs running concurrently;<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The query service will be widely distributed to support load from many clients that are also distributed;<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The databases will be replicated across the query service machines and also backed up centrally.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">We\u2019ve found that having a centralized indexing infrastructure enables a wide range of powerful developer tools. We\u2019ll talk about some of the ways we\u2019ve deployed Glean shortly, but first we\u2019ll dive into the rationale for Glean\u2019s design.<\/span><\/p>\n<h2><b>How is Glean different?<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Code indexing systems have been around for a while. For example, there\u2019s a well-established format called <\/span><a href=\"https:\/\/microsoft.github.io\/language-server-protocol\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">LSIF<\/span><\/a><span style=\"font-weight: 400;\"> used by IDEs that caches information about code navigation.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When we designed Glean we wanted a system that wasn\u2019t tied either to particular programming languages or to any particular use case. While we had some use cases in mind that we wanted to support\u2014primarily code navigation of course\u2014we didn\u2019t want to design the system around one use case, in the hope that a more general system would support emerging requirements further into the future.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Therefore:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Glean doesn\u2019t decide for you what data you can store<\/b><span style=\"font-weight: 400;\">. Indeed, most languages that Glean indexes have their own data schema and Glean can store arbitrary non-programming-language data too. The data is ultimately stored using <\/span><a href=\"https:\/\/rocksdb.org\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">RocksDB<\/span><\/a><span style=\"font-weight: 400;\">, providing good scalability and efficient retrieval.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Glean\u2019s query language is very general<\/b><span style=\"font-weight: 400;\">. It\u2019s a declarative logic-based query language that we call <\/span><i><span style=\"font-weight: 400;\">Angle<\/span><\/i><span style=\"font-weight: 400;\"> (\u201cAngle\u201d is an anagram of \u201cGlean\u201d, and means \u201cto fish\u201d). Angle supports <\/span><i><span style=\"font-weight: 400;\">deriving<\/span><\/i><span style=\"font-weight: 400;\"> information automatically, either on-the-fly at query time or ahead of time; this is a powerful mechanism that enables Glean to abstract over language-specific data and provide a language-neutral view of the data.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Storing arbitrary language-specific data can be very powerful. For example, in C++ we use the detailed data to detect dead code such as unused <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">#include<\/span><span style=\"font-weight: 400;\"> or <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">using<\/span><span style=\"font-weight: 400;\"> statements. The latter in particular is rather tricky to do correctly and requires the data to include some C++-specific details, such as which <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">using<\/span><span style=\"font-weight: 400;\"> statement is used to resolve each symbol reference.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">On the other hand, clients often don\u2019t want the full language-specific data. They want to work at a higher level of abstraction. Imagine asking questions like, \u201cGive me the names and locations of all the declarations in this file\u201d, which should work for any language, and which you could use to implement a code outline feature in a code browser. Glean can provide this language-neutral view of the data by defining an abstraction layer in the schema itself \u2013 the mechanism is similar to SQL views if you\u2019re familiar with those. This means that we don\u2019t have to compromise between having detailed language-specific data or a lowest-common-denominator language-neutral view; we can have both.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This generality has allowed Glean to extend to a number of use cases beyond what we originally envisaged. We\u2019ll cover some of those later in this post.<\/span><\/p>\n<h2>A taste of Angle<\/h2>\n<p><span style=\"font-weight: 400;\">Glean has a unified language, Angle, for specifying both schemas and queries. As mentioned above, each language that we index has its own schema. To give you a flavor of this, here\u2019s a fragment of the schema for C++ function declarations:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-22099 alignnone\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-2.png?w=380\" alt=\"\" width=\"380\" height=\"206\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-2.png 380w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-2.png?resize=96,52 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-2.png?resize=192,104 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Defining a schema for Glean is just like writing a set of type definitions. The braces surround a record definition, with a set of fields and their types.\u00a0<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">A <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">FunctionDeclaration<\/span><span style=\"font-weight: 400;\"> is a <\/span><i><span style=\"font-weight: 400;\">predicate<\/span><\/i><span style=\"font-weight: 400;\"> (roughly equivalent to a table in SQL).\u00a0<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The instances of a predicate are called <\/span><i><span style=\"font-weight: 400;\">facts<\/span><\/i><span style=\"font-weight: 400;\"> (roughly equivalent to rows in SQL).\u00a0<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">A predicate is a thing that you can query, and a query returns facts.\u00a0<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">To query efficiently you specify a prefix of the fields. So, for example, we can retrieve a particular <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">FunctionDeclaration<\/span><span style=\"font-weight: 400;\"> efficiently if we know its <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">name<\/span><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Let\u2019s write a query to find the function <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">folly::parseJson<\/span><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-22100 alignnone\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-3.png?w=562\" alt=\"\" width=\"562\" height=\"83\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-3.png 562w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-3.png?resize=96,14 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-3.png?resize=192,28 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">Without going into all the details, at a high level this query specifies that we want to find <\/span><span style=\"font-weight: 400; color: #008000; font-family: 'courier new', courier;\">FunctionDeclaration<\/span><span style=\"font-weight: 400;\"> facts that have a particular name and namespace. Glean can return results for this query in about a millisecond.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Angle supports more complex queries too. For example, to find all classes that inherit from a class called <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">exception<\/span><span style=\"font-weight: 400;\"> and have a method called <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">what<\/span><span style=\"font-weight: 400;\"> that overrides a method in a base class:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-22101 alignnone\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-4.png?w=717\" alt=\"\" width=\"717\" height=\"240\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-4.png 717w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-4.png?resize=96,32 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-4.png?resize=192,64 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">This query returns the first results in a few milliseconds, and because there might be a lot of results we can fetch the results incrementally from the query server.<\/span><\/p>\n<h2>Incremental indexing<\/h2>\n<p><span style=\"font-weight: 400;\">An important innovation in Glean is the ability to index <\/span><i><span style=\"font-weight: 400;\">incrementally<\/span><\/i><span style=\"font-weight: 400;\">. As the codebase grows, and the rate of change of the codebase increases (a monorepo suffers from both of these problems) we find that we can\u2019t provide up-to-date information about the latest code because indexing the entire repository can take a long time. The index is perpetually out of date, perhaps by many hours.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The solution to this scaling problem is to process <\/span><i><span style=\"font-weight: 400;\">just the changes<\/span><\/i><span style=\"font-weight: 400;\">. In terms of computer science big-O notation, we want the cost of indexing to be <\/span><i><span style=\"font-weight: 400;\">O(changes)<\/span><\/i><span style=\"font-weight: 400;\"> rather than <\/span><i><span style=\"font-weight: 400;\">O(repository)<\/span><\/i><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">But actually achieving this is not as straightforward as it might sound.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We don\u2019t want to destructively modify the original data, because we would like to be able to provide data at multiple revisions of the repository, and to do that without storing multiple full-sized copies of the data. So we would like to store the changes in such a way that we can view the whole index at both revisions simultaneously.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Even if we figure out a way to represent the changes, in practice it isn\u2019t possible to achieve <\/span><i><span style=\"font-weight: 400;\">O(changes)<\/span><\/i><span style=\"font-weight: 400;\"> for many programming languages. For example, in C++ if a header file is modified, we have to reprocess every source file that depends on it (directly or indirectly). We call this the <\/span><i><span style=\"font-weight: 400;\">fanout<\/span><\/i><span style=\"font-weight: 400;\">. So in practice the best we can do is <\/span><i><span style=\"font-weight: 400;\">O(fanout)<\/span><\/i><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Glean solves the first problem with an ingenious method of <\/span><i><span style=\"font-weight: 400;\">stacking<\/span><\/i><span style=\"font-weight: 400;\"> immutable databases on top of each other. A stack of databases behaves just like a single database from the client\u2019s perspective, but each layer in the stack can non-destructively add information to, or hide information from, the layers below.\u00a0<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-22102\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-5.png?w=569\" alt=\"\" width=\"569\" height=\"458\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-5.png 569w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-5.png?resize=96,77 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-5.png?resize=192,155 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">The full details are beyond the scope of this post, for more on how incrementality works see: <\/span><a href=\"https:\/\/glean.software\/blog\/incremental\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Incremental indexing with Glean<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Finding the fanout of a set of changes is different for each language.\u00a0 Interestingly the fanout can often be obtained using Glean queries: for example for C++, the fanout is calculated by finding all the files that <\/span><span style=\"font-weight: 400; color: #008000; font-family: 'courier new', courier;\">#include<\/span><span style=\"font-weight: 400;\"> one of the changed files, and then repeating that query until there are no more files to find.<\/span><\/p>\n<h2>How we use Glean at Meta<\/h2>\n<h3>Code navigation<\/h3>\n<p><span style=\"font-weight: 400;\">Code navigation at scale, on large monorepos containing millions of lines in diverse programming languages, is a challenging problem. But what makes it different from the code navigation support available in modern IDEs, other than scale? In our experience, code indexing a la Glean offers the following advantages over IDEs:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Instantly available: Just open the code browser web app (our internal tool uses Monaco) and navigate without waiting for the IDE, build system, and LSP server to initialize<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">More widely available: You can integrate code navigation in pretty much any app that shows code! One particularly useful integration is in your code review tool (ours is called Phabricator), but more on that later.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Full repo visibility: Glean allows you to, for example, find all the references to a function, not just the ones visible to the IDE. This is particularly useful for finding dead code, or finding clients of an API that you want to change.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Symbol search for all the languages across the whole repository.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Cross language navigation: A common situation that comes up is a remote procedure call (RPC). When browsing the code you might want to jump to the service definition or, indeed, to the service implementation itself. Another case is languages with a foreign function interface (FFI), where you would like to browse from an FFI call to the corresponding definition in the target language.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Our architecture for code navigation is based on <\/span><a href=\"https:\/\/github.com\/facebookincubator\/Glean\/tree\/main\/glean\/glass\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Glass<\/span><\/a><span style=\"font-weight: 400;\">, a symbol server that abstracts all the complexities of Glean by implementing the usual code navigation logic in a simple but powerful API. The code browser needs only a single Glass API call,<\/span><i><span style=\"font-weight: 400;\"> documentSymbols(repo,path,revision),<\/span><\/i><span style=\"font-weight: 400;\"> to obtain a list of all the definitions and references in a source file, including source and target spans. The list of definitions is used to render an outline of the file, and the list of references to render underlines that can be hovered over or clicked to navigate. Finally, other code browser features like Find References or Call Hierarchy are also driven by API calls to Glass.\u00a0<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-22103\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-6.png?w=844\" alt=\"\" width=\"844\" height=\"186\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-6.png 844w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-6.png?resize=768,169 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-6.png?resize=96,21 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-6.png?resize=192,42 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">The code for Glass is also open-source, you can find it in <\/span><a href=\"https:\/\/github.com\/facebookincubator\/Glean\/tree\/main\/glean\/glass\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">glean\/glass<\/span><\/a><span style=\"font-weight: 400;\"> on GitHub.<\/span><\/p>\n<h3>Speeding up the IDE<\/h3>\n<p><span style=\"font-weight: 400;\">Using an IDE such as VS Code on a large project, or a project with a large set of dependencies, or in a large monorepo tends to lead to a degraded experience as the IDE isn\u2019t able to analyze all the code that you might want to explore. At Meta we\u2019re using Glean to plug this gap for C++ developers: Because Glean has already analyzed the whole repository, C++ developers have access to basic functionality such as go-to-definition, find-references, and doc comment hovercards for the whole repository immediately on startup. As the IDE loads the files the developer is working on, the C++ language service seamlessly blends the Glean-provided data with that provided by the native clangd backend.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Our target was C++ developers initially because that group typically has the worst IDE experience due to the long compile times, but the approach is not specific to C++ and we imagine other languages following the same path in the future.<\/span><\/p>\n<h3>Documentation generation<\/h3>\n<p><span style=\"font-weight: 400;\">The data we store in Glean includes enough information to reconstruct the full details of an API: classes, methods, type signatures, inheritance, and so on. Glean also collects documentation from the source code when it uses the standard convention for the language, e.g., in C++ the convention is <\/span><span style=\"font-weight: 400; color: #008000; font-family: 'courier new', courier;\">\/\/\/ comment<\/span><span style=\"font-weight: 400;\"> or <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">\/** comment *\/<\/span><span style=\"font-weight: 400;\">. With API data and documentation strings in Glean we can produce automatically-generated documentation on demand.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Here\u2019s an example page for the <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">folly::Singleton<\/span><span style=\"font-weight: 400;\"> type:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-22104\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?w=1024\" alt=\"\" width=\"1024\" height=\"664\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png 1672w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?resize=916,594 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?resize=768,498 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?resize=1024,664 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?resize=1536,997 1536w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?resize=96,62 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2024\/12\/Meta-Glean-image-7.png?resize=192,125 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">The data for these pages is produced by Glass and rendered by a client-side UI. The documentation is fully hyperlinked so the user can navigate around all the APIs throughout the repository easily. Meta engineers get consistent code documentation integrations across all the programming languages supported by Glean.<\/span><\/p>\n<h3>Symbol IDs<\/h3>\n<p><span style=\"font-weight: 400;\">Glass assigns every symbol a <\/span><i><span style=\"font-weight: 400;\">symbol ID<\/span><\/i><b><i>, <\/i><\/b><span style=\"font-weight: 400;\">a unique string that identifies the symbol. For example, the symbol ID for <\/span><span style=\"font-weight: 400; color: #008000; font-family: 'courier new', courier;\">folly::Singleton<\/span><span style=\"font-weight: 400;\"> would be something like, <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #008000;\">REPOSITORY\/cpp\/folly\/Singleton<\/span><span style=\"font-weight: 400;\">. The symbol ID can be used to link directly to the documentation page for the symbol, so there\u2019s a URL for every symbol that doesn\u2019t change even if the symbol\u2019s definition moves around.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We can use the symbol ID to request information about a symbol from Glass, for example to find all the references to the symbol throughout the repository. All of this works for every language, although the exact format for a symbol ID varies per language.<\/span><\/p>\n<h3>Analyzing code changes<\/h3>\n<p><span style=\"font-weight: 400;\">Glean indexing runs on diffs (think, \u201cpull requests\u201d) to extract a mechanical summary of the changeset that we call a <\/span><i><span style=\"font-weight: 400;\">diff sketch<\/span><\/i><span style=\"font-weight: 400;\">. For example, a diff might introduce a new class, remove a method, add a field to a type, introduce a new call to a function, and so on. The diff sketch lists all of these changes in a machine-readable form.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Diff sketches are used to drive a simple static analysis that can identify potential issues that might require further review. They can also be used to drive non-trivial lint rules, rich notifications, and semantic search over commits. One example of the latter is connecting a production stack trace to recent commits that modified the affected function(s), to help root-cause performance issues or new failures.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Indexing diffs also powers code navigation in our code review tools, giving code reviewers access to accurate go-to-definition on the code changes being reviewed, along with other code insights such as type-on-hover and documentation. This is a powerful lift to the code review process, making it easier for reviewers to understand the changes and provide valuable review feedback. At Meta this is enabled for a <a href=\"https:\/\/engineering.fb.com\/2022\/07\/27\/developer-tools\/programming-languages-endorsed-for-server-side-use-at-meta\/\" target=\"_blank\" rel=\"noopener\">variety of different languages<\/a>, including C++, Python, PHP, Javascript, <a href=\"https:\/\/engineering.fb.com\/2021\/04\/29\/developer-tools\/rust\/\" target=\"_blank\" rel=\"noopener\">Rust<\/a>, Erlang, Thrift, and even Haskell.<\/span><\/p>\n<h2>More applications for Glean<\/h2>\n<p><span style=\"font-weight: 400;\">Aside from the primary applications described above, Glean is also used to<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Analyse build dependency graphs.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/engineering.fb.com\/2023\/10\/24\/data-infrastructure\/automating-dead-code-cleanup\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Detect and remove dead code<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Track the progress of API migrations.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Measure various metrics that contribute to code complexity.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Track test coverage and select tests to run.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><a href=\"https:\/\/engineering.fb.com\/2023\/10\/31\/data-infrastructure\/automating-data-removal\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Automate data removal<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Retrieval Augmented Generation (RAG) in AI coding assistants<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Furthermore, there are an ever-growing number of ad-hoc queries made by various people and systems to solve a variety of problems. Having a system like Glean means you can ask questions about your code: we don\u2019t know all the questions we might want to ask, nor do we know all the data we might want to store, so Glean deliberately aims to be as general as possible on both of these fronts.<\/span><\/p>\n<h2>Try Glean today<\/h2>\n<p><span style=\"font-weight: 400;\">Visit the <\/span><a href=\"https:\/\/glean.software\/\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Glean site<\/span><\/a><span style=\"font-weight: 400;\"> for more details, technical documentation, and information on how to get started.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We\u2019re sharing details about Glean, Meta\u2019s open source system for collecting, deriving, and working with facts about source code. In this blog post we\u2019ll talk about why a system like Glean is important, explain the rationale for Glean\u2019s design, and run through some of the ways we\u2019re using Glean to supercharge our developer tooling at [&#8230;]<\/p>\n<p><a class=\"btn btn-secondary understrap-read-more-link\" href=\"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":51,"featured_media":19065,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[66,174],"tags":[],"coauthors":[965,2178],"class_list":["post-22095","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developer-tools","category-open-source"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v19.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Indexing code at scale with Glean - Engineering at Meta<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Simon Marlow, Pepe Iborra\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/\"},\"author\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#author\",\"name\":\"\"},\"headline\":\"Indexing code at scale with Glean\",\"datePublished\":\"2024-12-19T14:00:29+00:00\",\"dateModified\":\"2024-12-19T21:21:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/\"},\"wordCount\":2614,\"publisher\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg\",\"articleSection\":[\"DevInfra\",\"Open Source\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/\",\"name\":\"Indexing code at scale with Glean - Engineering at Meta\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg\",\"datePublished\":\"2024-12-19T14:00:29+00:00\",\"dateModified\":\"2024-12-19T21:21:22+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#primaryimage\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg\",\"contentUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg\",\"width\":1920,\"height\":1080},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2024\\\/12\\\/19\\\/developer-tools\\\/glean-open-source-code-indexing\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/engineering.fb.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Indexing code at scale with Glean\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#website\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/\",\"name\":\"Engineering at Meta\",\"description\":\"Engineering at Meta Blog\",\"publisher\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/engineering.fb.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\",\"name\":\"Meta\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/Meta_lockup_positive-primary_RGB.jpg\",\"contentUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/Meta_lockup_positive-primary_RGB.jpg\",\"width\":29011,\"height\":12501,\"caption\":\"Meta\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/Engineering\\\/\",\"https:\\\/\\\/x.com\\\/fb_engineering\"]},[]]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Indexing code at scale with Glean - Engineering at Meta","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/","twitter_misc":{"Written by":"Simon Marlow, Pepe Iborra","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#article","isPartOf":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/"},"author":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#author","name":""},"headline":"Indexing code at scale with Glean","datePublished":"2024-12-19T14:00:29+00:00","dateModified":"2024-12-19T21:21:22+00:00","mainEntityOfPage":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/"},"wordCount":2614,"publisher":{"@id":"https:\/\/engineering.fb.com\/#organization"},"image":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/07\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg","articleSection":["DevInfra","Open Source"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/","url":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/","name":"Indexing code at scale with Glean - Engineering at Meta","isPartOf":{"@id":"https:\/\/engineering.fb.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#primaryimage"},"image":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/07\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg","datePublished":"2024-12-19T14:00:29+00:00","dateModified":"2024-12-19T21:21:22+00:00","breadcrumb":{"@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#primaryimage","url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/07\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg","contentUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/07\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg","width":1920,"height":1080},{"@type":"BreadcrumbList","@id":"https:\/\/engineering.fb.com\/2024\/12\/19\/developer-tools\/glean-open-source-code-indexing\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/engineering.fb.com\/"},{"@type":"ListItem","position":2,"name":"Indexing code at scale with Glean"}]},{"@type":"WebSite","@id":"https:\/\/engineering.fb.com\/#website","url":"https:\/\/engineering.fb.com\/","name":"Engineering at Meta","description":"Engineering at Meta Blog","publisher":{"@id":"https:\/\/engineering.fb.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/engineering.fb.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/engineering.fb.com\/#organization","name":"Meta","url":"https:\/\/engineering.fb.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.fb.com\/#\/schema\/logo\/image\/","url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2023\/08\/Meta_lockup_positive-primary_RGB.jpg","contentUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2023\/08\/Meta_lockup_positive-primary_RGB.jpg","width":29011,"height":12501,"caption":"Meta"},"image":{"@id":"https:\/\/engineering.fb.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Engineering\/","https:\/\/x.com\/fb_engineering"]},[]]}},"jetpack_featured_media_url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2022\/07\/Eng-Blog-Self-Serve-Hero-Images-DEBUGGING-203-Teale-1.jpg","jetpack_shortlink":"https:\/\/wp.me\/pa0Lhq-5Kn","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/22095","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/users\/51"}],"replies":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/comments?post=22095"}],"version-history":[{"count":14,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/22095\/revisions"}],"predecessor-version":[{"id":22122,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/22095\/revisions\/22122"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/media\/19065"}],"wp:attachment":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/media?parent=22095"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/categories?post=22095"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/tags?post=22095"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/coauthors?post=22095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}