Code dependency graph is always wanted, at least for me.
From Top-Down direction, it's great to know which parts of the system are needed to change when facing a business requirement change.
From Bottom-Up direction, it's necessary to check which parts of the system might be impacted when one change is made.
Knowing code dependency can have better impact analysis and effort estimation.
This project is to build the code dependency graph with the asistance of AI. Due to the dynamic characteristics of JavaScript, the dependency graph might not be completely accurate but we try to make it closed.
This tool includes below major components:
-
API for graph data (Node, Edge) manipulation against Graph Database
- Exported from
index.js
, mainly includesGraphBuilder
,AiProvider
,AstParser
and some facilitated APIs
- Exported from
-
AI adaptors
- Pre-defined
AiProvider
under directoryai-providers
.
- Pre-defined
-
Graph data generation sample script.
- Sample Script
js.repo.graph.builder.js
andjava.repo.graph.builder.js
which build code dependency graph.
- Sample Script
-
Sample project providing restful API for graph component manipulation and visualization UI
- Under directory
sample-project
.
- Under directory
-
Sample VSCode extension that calls RESTful API provided by Sample Project.
- Under directory
ai-buddy
.
- Under directory
Add sample.config.js
like below:
{
graph: {
type: 'ARCADEDB',
connectionOptions: {
host: '127.0.0.1',
port: 2480,
database: '',
username: '',
password: ''
}
},
defaultAiProvider: 'MOONSHOT',
// filesMatchingPatterns: /.*\.java$/
filesMatchingPatterns: [
/.*\.(vue|js)$/,
/^(?!.*\.(test|spec)\.js$)/,
/^(?!.*\.json$).*$/,
/^(?!.*(node_modules|cypress)).*$/
],
aiProviders: {
MOONSHOT: {
apiKey: 'key-1'
},
BIGMODEL: {
apiKey: 'key-2'
}
}
}
Some environment variables should be set before executing below script:
PROJECT_ROOT
: Absolute path of the project to be analyzed.SERVICE_NAME
: Micro-Service Name of the projectAI_ENABLED
: If set tofalse
, it will not try to send the function to AI provider to get description based on function implementation.
For JavaScript project, invokes command node js.repo.graph.builder.js
.
For JAVA project, invokes command node java.repo.graph.builder.js
.
java.repo.graph.builder
utilizes LSP besides AST. You can start one using Eclipse JDT.
These command extracts the code dependency on the project specified in path PROJECT_ROOT
and store to your graph database.
After the code dependency stored in DB, sample web server of RESTful API can be started for dependency retrieval. It's under directory sample-project
and can be started as:
- Run
npm install
- Run
npm build
- Run
node server.js
There is ONLY one Edge type Uses
. It's used to connect:
- From Micro-Service to System Module
- From System Module to Component
- From Component to Component
A Uses
B means that A depends
on B, which is A -> B
. When this relationship exists, if B is updated, A is potentially affected. Business Module, Micro-Service, System Module are mainly used as grouping purpose. The real dependency or impact actually depends on Component -> Component
relationship tracking. Detail explanation of different Vertex categories is in following sections.
Vertex Schema
{
name: '', // Function Name / Field Name / API URL / Queue Name / Table Name / Store Procedure Name
type: '', // `UI`, `Function` / `Field` / `Interface` (API / Queue / Table / Store Procedure), etc.
description: '', // Generated from AI if it's Function
microService: '',
businessModules: [''], // Related Business Module that groups the Micro-Service if required.
public: true/false,
systemModule: '', // Node.js package name, JavaScript file name, Java Full-qualified Name, such as `user/user.controller.js`, `com.xxx.user.UserController.java`
sourceCode: '' // Source Code of Function, mainly
}
Vertex has three basic properties:
name
: Brief name of the Vertex and should be unique in same categorytype
: Type identifier within same category if required.description
: Long description of one Vertex. It must be in detail, especially for Business Module, System Module, Function type Component, etc. It is provided to AI as RAG with your question so that it can reason out which vertices to look up.
Vertex has four categories:
- Business Module
- Micro-Service
- System Module
- Component
- Set to System Module as grouping purpose
- Should be correctly pinpointed by AI given BA’s question
- Used for top-down search all related System Module / Components
- Technical Perspective
- Grouping purpose for better visualization or query
Grouping code files by Roles or by Feature/Module are two popular choices. This Dependency Graph project would prefer the second choice, but it does not affect your usage only if you name the Vertex in the pattern suits your project. Name of the System Module Vertex could simply be the folder name, file name or the combination of it.
System Module Vertex has businessModule
and microService
properties for graph building.
By Roles:
│ ├── app.js
│ ├── controllers
│ │ ├── inquiryController.js
│ │ └── updateController.js
│ ├── dbaccessors
│ │ └── dataAccessor.js
│ ├── models
│ │ └── order.js
│ ├── routes
│ │ └── routes.js
│ └── services
│ └── inquiryService.js
By Feature/Module with optional sub-level roles' separation:
│ ├── app.js
│ ├── organizations
│ │ ├── organization.controller.js
│ │ ├── organization.routes.js
│ │ └── catalog.routes.js
│ └── orders
│ ├── controllers (optional separation)
│ │ ├── orderInquiryController.js
│ │ └── orderUpdateController.js
│ ├── dbaccessors
│ │ └── orderDataAccessor.js
│ ├── models
│ │ └── order.js
│ ├── routes
│ │ └── orderRoutes.js
│ └── services
│ └── orderInquiryService.js
This is the actual Vertex that tracks code dependency. It has extra properties beside name
, type
, description
:
name
: It can be Function Name / Field Name / API URL / Queue Name / Table Name / Store Procedure Nametype
: It can beUI
,Function
/Field
/Interface
(API / Queue / Table / Store Procedure), etc.public
:true
/false
indicating its public interface or not.microService
: Part of the unique constraint of Component.systemModule
: Part of the unique constraint of Component. Easier CRUD and graph search starting point.sourceCode
: Source code of this component. It can be function signature & body, API route validation code, etc.
To build a code dependency graph for a project, it should probably go through below steps:
- Create
Business Module
type Vertices. These vertices should normally be prepared by business analysis and they probabaly cannot be done through codebase scan. - Create
Micro-Service
type Vertices. These vertices are normally 1-1 mapping to the code repositories. - Create
System Module
andComponent
type Vertices. These should be done automatically by code scan.- Execute
js.repo.graph.builder
orjava.repo.graph.builder
.
- Execute
Sample Vertices:
const businessModuleVertices = [
{
name: 'Organization',
category: 'businessModule',
description: 'Organization Management',
type: 'Organization'
},
{
name: 'Metering',
category: 'businessModule',
description: 'Metering Report',
type: 'Reporting'
}
];
const microServiceVertices = [
{
name: 'platform_console',
category: 'microService',
description: 'Platform Console',
type: 'frontend'
},
{
name: 'platform_console_service',
category: 'microService',
description: 'Platform Console Service',
type: 'backend'
}
];
const systemModuleVertices = [
{
category: 'systemModule',
businessModules: ['Organization'],
microService: 'platform_console_service',
name: 'organization.routes.js',
type: 'Class',
description: 'API Routes for Organization API',
dependencies: [
{
category: 'component',
name: 'POST /organizations/:orgId/roles',
type: 'api',
description: 'API Routes for adding memberRoles for Organization',
sourceCode: `...`
}
]
},
{
category: 'systemModule',
businessModules: ['Organization', 'Metering'],
microService: 'platform_console_service',
name: 'organization.services.js',
type: 'Class',
description: 'Service Class for Organization',
dependencies: [
{
category: 'component',
name: 'addMemberRoles',
type: 'function',
description: 'Service API to add memberRoles for Organization',
sourceCode: `...`
}
]
},
{
category: 'systemModule',
businessModules: ['System'],
microService: 'platform_console_service',
name: 'repository.util.js',
type: 'Class',
description: 'Repository Utility',
dependencies: [
{
category: 'component',
name: 'updateAll',
type: 'function',
description: 'Repository API for updating data',
sourceCode: `...`
}
]
}
];