More often than not software systems are displayed as layers. The top-most components use the ones below but not vice-versa. Ideally the package/namespace/module structure reflects the layers, eg:
- features built on top of other features
- REST APIs built on top of DB repositories
However it is easy to loose track of the layering in large projects, violate the structure and in the worst case introduce cyclic dependencies.
The sda tools visualized the dependencies within a code base in a graph and validates the cycle-freeness as part of the test pipeline.
You can download the latest release or compile the project on your own.
# download release
open https://github.com/sandstorm/dependency-analysis/releases/
# … or install via homebrew
brew install sandstorm/tap/sandstorm-dependency-analysis
# … or compile sda locally
go build -o ~/bin/sda
# install GraphViz (with Homebrew on OSX)
brew install graphviz
# show available commands
sda --help
# render and open dependency graph
sda visualize src/
# search and print cycles (exit code equals the number of cycles)
sda validate src/
For your CI pipeline there is an image on hub.docker.com.
# use this image in your CI pipeline
docker run --rm sandstormmedia/dependency-analysis sda --help
# note that the image does not (yet) include GraphViz
# but you can generate the DOT graph and pipe it
# to your local GraphViz installation
docker run --rm \
-v $(pwd):/src \
sandstormmedia/dependency-analysis \
sda visualize /src -o stdout \
| dot -T svg -o output.svg
- Golang
- Groovy
- Java
- PHP (when using classes)
- JavaScript (when using modules)
- TypeScript (when using modules)
- Kotlin
- sda - Sandstorm Dependency Analysis
- Table of Content
- User documentation
- Developer documentation
- Glossary
The given source directory is searched for known file types. All source units (classes in Java/PHP, files in golang) are parsed to collect all dependencies. Source units and dependencies are described using the full name including the package. The longest package prefix shared between all source units is then used as root package.
Given the following Java classes:
de.sandstorm.examples.sda.Main
de.sandstorm.examples.sda.http.WebServer
de.sandstorm.examples.sda.model.WebResponse
The root package is de.sandstorm.examples.sda
and the top-level packages are:
Main
http
model
Now the dependencies between this top-level packages are analyzed. You can look deeper into the package structure by
- adjusting the source path
- by setting
--depth
to a value higher1
You can use the validate command in the CI pipeline as a code-quality test. The exit code equals the numer of cylces in the dependency graph (hopefully zero).
Stdout contains all cycles.
$ sda validate src/main/java
found 2 cycles:
┌▶ http
| ▼
| services
└───┘
┌▶ commands
| ▼
| http
| ▼
| services
└───┘
$ sda validate
no cycles found, everything is all right
test-code-dependencies:
image: sandstormmedia/dependency-analysis:v1
stage: test
needs: [] # executes earlier
dependencies: [] # starts faster
script:
- sda validate src/main/java --depth 1
- sda validate src/main/java --depth 2
- sda validate src/main/java --depth 3 --max-cycles 1
To get an overview over a legacy project or to debug and break cyclic dependencies you can render the dependency graph into an image, such as
The color of the nodes indicated the position in the layering: more blue packages are higher in the layering, more green ones lower.
The colors are particulary helpful in tangeld graphs of messy dependencies.
Note that I omit the node labels in the following examples.
Edges on a cycle have warm colors.
In larger graphs with multiple cycles the color of the edges helps to follow single cycles. Since in the presence of cycles layers cease to exist, the graph becomes tangled. The colors of the nodes still indicate on which level the node might be.
If you plan to clean up a project and remove cycles I suggest the following:
- start on thick edges to break multiple cycles at once
- increase the
--depth
to get some insights where the cycles come from
Lucky you, there are no cycles between classes. Re-ordering a few classes and packages should do the trick.
You probably need to split a class or two. Often the cycles appear in classes which perform several tasks.
Number of steps to go further down into the package hierarchy starting at the root package. The root package is detected automatically.
Regular expression to filter files by their full path before analysis. All paths containing a match are analyzed. You can use this to exclude auto-generated clients.
sda visualize --include 'src/main/java'
Maximum number of cycles to attribute with exit-code 0.
The parameter '--max-cycles' is intended as follows:
- remove cycles step-by-step from legacy projects with the goal to set --max-cycles to zero eventually
- rare corner-cases where you consider cycles a valid option
The graph label is located at the bottom center of the resulting image (default "rendered by github.com/sandstorm/dependency-analysis").
Path to the image file to generate, use stdout
to output DOT graph without image rendering.
Automatically open the image after rendering.
Internally open …
is called.
On operating systems other then OSX this flag and feature does not exist.
sda visualize --show-image=false
Render graph with node labels. I use this flag to generate anonymous graphs for this documentation.
Type of the image file.
$ sda visualize listSupportedOutputTypes
bmp BMP Windows Bitmap
cgimage CGImage Apple Core Graphics
canon DOT Graphviz Language
dot DOT Graphviz Language
gv DOT Graphviz Language
xdot DOT Graphviz Language
xdot1.2 DOT Graphviz Language
xdot1.4 DOT Graphviz Language
eps EPS Encapsulated PostScript
exr EXR OpenEXR
fig FIG Xfig
gd GD LibGD
gd2 GD2 LibGD
gif GIF Graphics Interchange Format
gtk GTK Formerly GTK+ / GIMP ToolKit
ico ICO Windows Icon
cmap Image Map: Client-side
ismap Image Map: Server-side
imap Image Map: Server-side and client-side
cmapx Image Map: Server-side and client-side
imap_np Image Map: Server-side and client-side
cmapx_np Image Map: Server-side and client-side
jpg JPEG Joint Photographic Experts Group
jpeg JPEG Joint Photographic Experts Group
jpe JPEG Joint Photographic Experts Group
jp2 JPEG 2000
json JSON JavaScript Object Notation
json0 JSON JavaScript Object Notation
dot_json JSON JavaScript Object Notation
xdot_json JSON JavaScript Object Notation
pdf PDF Portable Document Format
pic PIC Brian Kernighan's Diagram Language
pct PICT Apple PICT
pict PICT Apple PICT
plain Plain Text Simple, line-based language
plain-ext Plain Text Simple, line-based language
png PNG Portable Network Graphics
pov POV-Ray Persistence of Vision Raytracer (prototype)
ps PS Adobe PostScript
ps2 PS/PDF Adobe PostScript for Portable Document Format
psd PSD Photoshop
sgi SGI Silicon Graphics Image
svg SVG Scalable Vector Graphics
svgz SVG Scalable Vector Graphics
tga TGA Truevision TARGA
tif TIFF Tag Image File Format
tiff TIFF Tag Image File Format
tk Tk Tcl/Tk
vm VML Vector Markup Language
vmlz VML Vector Markup Language
vrml VRML Virtual Reality Modeling Language
wbmp WBMP Wireless Bitmap
webp WebP WebP
xlib X11 X11 Window
x11 X11 X11 Window
For the sake on simplicity the code parsers built on regular expressions. They only work on normal formatted code.
The parsers only look at imports/usages within the files. References without imports are ignored.
The parsers ignore comments. Commented imports are treated as normal imports.
Unused imports are treated as normal imports.
- install Golang
brew install graphviz
- install the Sandstorm
dev
script helper
dev run
# or
dev run --help
# adjust version as needed
git tag v1.0.0
dev release
# install COBRA CLI (see links to docs below)
cobra-cli add helloWorld
# and adjust generated files
# see all dev scripts
dev help
# visualize all Java projects in current folder
find . -type d | grep -iE 'src/main/java$' | xargs -Isrc bash -c 'export project=$(echo src | cut -d / -f 2); sda visualize src -o $project.svg -l $project'
- Command Line Interface Library: COBRA
- Printing the Graph: GraphViz Examples
- GraphViz rank=same: placing node on the same level
- colorgrad: Go (Golang) color scales library for data visualization, charts, games, maps, generative art and others
- VS Code: Markdown All in One
- source-unit - smallest source module, eg classes in PHP and Java, prototypes in Fusion
- package - location of a source-unit in a hierarchy, eg packages ind Java, namespaces in PHP, folders in JavaScript
- package segment - one step in the package hierarchy, e.g. de.sandstorm.test consists of the three segments [de sandstorm test]
- root package - largest package prefix shared between all source-units
- node - box in a graph
- edge - arrow in a graph