Full-Chain Dependency Analysis System for Gaode App: Architecture, Implementation, and Use Cases
The Gaode App full‑chain dependency analysis system parses source code across JS, C++, Android and iOS into ASTs, extracts forward and reverse dependencies, stores them for GraphQL queries, and uses the data to guide regression testing, API deprecation, migration trends, and package‑size optimization, improving codebase governance.
Background
Gaode App has grown to millions of lines of code, supporting complex mapping functionalities. As the team and business expanded, code became fragmented with tangled dependencies, leading to maintenance challenges such as:
Reluctance to modify or deprecate public interfaces because of unknown dependencies, causing code bloat and larger package sizes.
Modules that have not changed still require full regression testing when released in a new client version, due to uncertainty about dependent modules.
Difficulty assessing whether the shift of business logic from Native to lower‑level support is reasonable and well‑governed.
These issues necessitate a systematic governance approach, with the key being a clear understanding of inter‑module dependencies.
Gaode App Platform Architecture
The app consists of four layers:
JS layer – handles business logic and UI framework.
C++ layer – provides high‑performance rendering and implements cross‑cutting APIs.
Android and iOS layers – act as adaptation layers, bridging OS interfaces and smoothing platform differences.
The “cutting‑edge” refers to the boundary between the JS layer and the Native/C++ layer, where APIs such as Bluetooth or system information are defined in Native/C++ and exposed to JS.
Fundamental Implementation Principle
The core data is the dependency relationship (e.g., file A depends on a method in file B). Extracting this relationship involves two steps.
Step 1: Compile source code to obtain an AST
All source files are parsed to generate an Abstract Syntax Tree (AST). For the JS scanner, the typeScript module is used as the compiler, supporting JS(X) and TS(X) via ts.createSourceFile . iOS uses Clang, Android uses bytecode analysis, and C++ relies on symbol‑table analysis.
Step 2: Path extraction – dependency tracing
From the AST we locate import/require/export statements (e.g., import , require , export , module.exports ). Traversal is performed recursively using ts.forEachChild and ts.SyntaxKind to identify node types.
Once an expression is found, the actual file is resolved. For example, the JS pattern const { identifierName } = require('@bundleName/fileName') is used to locate the specific identifier in the target module.
Cross‑cutting dependencies require an extra step: the cut‑over API is split into a calling side (JS) and a declaration side (Native/C++). Data from both sides are linked by version numbers to form a complete dependency chain.
All relationships and metadata are persisted for downstream analysis.
Project Architecture
The system is built with Node.js and the internal egg.js framework, exposing a GraphQL query interface. Data processing modules are independent Node.js packages, reusable by other projects (e.g., IDE plugins).
The left side of the diagram shows data consumers (e.g., QA, architecture teams). The right side is the database storing analysis results. The bottom layer consists of scanners and triggers for the four platforms, supporting event‑driven, scheduled, front‑end, and manual triggers.
Application Scenarios and Implementation Details
Impact Range Determination (Reverse Dependency Analysis)
When an interface or component is widely used, understanding which modules depend on it helps assess regression testing scope, release strategy, and compatibility. Reverse dependencies are derived from the forward dependencies collected by the scanners.
Combining reverse‑dependency data across versions enables calculation of “continuous unused version count,” aiding safe deprecation of APIs.
Version‑to‑Version Change Analysis
By comparing dependency chains between two releases, the system highlights changed files and their impact, allowing QA to focus regression testing on affected functionalities.
This is especially useful when a module is unchanged but its dependencies have evolved (e.g., Native/C++ or shared modules).
Trend Analysis
Gaode tracks the migration of business logic from Native to JS/C++. Two metrics are monitored per version: call volume of Native cut‑over APIs and declaration volume of those APIs. A decreasing call curve followed by a decreasing declaration curve indicates successful migration.
Architecture and API governance teams use these trends to decide when to retire a Native API.
Package Size Optimization – Unused/Duplicate File Detection
Dependency data also reveals files that are never referenced or are exact duplicates (identical MD5). This helped identify thousands of redundant images (e.g., @1x/@2x/@3x variants), prompting design standardization and tooling improvements.
Conclusion
The Gaode full‑chain dependency analysis project illustrates a comprehensive approach to codebase governance, involving compilation theory, graph algorithms, and large‑scale data processing. It challenges developers to handle historical legacy, multi‑level version snapshots, and exponential analysis results, demanding strong programming skills, trade‑off decisions, and resilience.
Amap Tech
Official Amap technology account showcasing all of Amap's technical innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.