packs

WIP Rust implementation of packs and packwerk for ruby
About
- It's entirely built in Rust, so it's pretty fast! In Gusto's monolith, it's about 10x faster (Benchmarks) than the ruby implementation. Your mileage may vary! Other performance optimizations could potentially get to 20x faster.
- The goal is for this to be able to be a drop-in replacement for
packwerk. - Currently,
packsimplementscheck, withupdatecoming soon.
Usage
Once installed and added to your $PATH, just call packs to see the CLI help message.
Verification
As packs is still a work-in-progress, it's possible it will not produce the same results as the ruby implementation (see below). If not, please file an issue!
To verify:
- Run
rm -rf tmp/cache/packwerkto delete the existing cache. - Run
packs generate_cache(see directions below to get binary) - Run
bin/packwerk updateto see how violations change usingpacks.
Separately, you can run:
packs check and compare the output to bin/packwerk check
Downloading the Binary
Deployment ergonomics are still a WIP.
If you want to try it out:
- Go to https://github.com/alexevanczuk/packs/releases
- Download the
packsasset and runchmod +x path/to/packs - Open the containing directory, right click on the binary, click open, and then accept the warning message that says its from an unknown developer (it's me!)
- Execute
path/to/packsto see the CLI help message.
You can add path/to/packs to your PATH so it's available in every terminal session.
Deployment Improvements
In the future, I hope to:
- Somehow sign the binary so it does not get a warning message
- Make it executable before download
- Add directions to download via some other tool, or ship as a native ruby gem extension.
New to Rust?
Me too! This is my first Rust project, so I'd love to have feedback, advice, and contributions! If you're new to Rust, don't be intimidated! https://www.rust-lang.org has tons of great learning resources.
Not yet supported:
- privacy checker or other checkers
- custom associations
- custom inflections
- custom load paths
- zeitwerk default namespaces
- extensible plugin system
Benchmarks
Cold Cache, without Spring
packs check:rm -rf tmp/cache/packwerk && DISABLE_SPRING=1 time ../pks/target/release/packs checkpackwerk check:rm -rf tmp/cache/packwerk && DISABLE_SPRING=1 time bin/packwerk check
| Run | packs check |
packwerk check |
|---|---|---|
| 1 | 8.9s | 107.83s |
| 2 | 7.31s | 85.24s |
| 3 | 7.55s | 126.52s |
| 4 | 6.85s | 80.47s |
| 5 | 8.45s | 99.90s |
| Average | 7.812s | 99.99s |
Hot Cache, without Spring
packs check:DISABLE_SPRING=1 time ../pks/target/release/packs checkpackwerk check:DISABLE_SPRING=1 time bin/packwerk check
| Run | packs check |
packwerk check |
|---|---|---|
| 1 | 3.86s | 39.33s |
| 2 | 3.69s | 34.02s |
| 3 | 3.6s | 41.68s |
| 4 | 3.52s | 35.26s |
| 5 | 3.32s | 37.14s |
| Average | 3.598 | 37.29 |
Profiling
I've been using https://github.com/flamegraph-rs/flamegraph to generate flamegraphs to improve performance.
Specifically, this command which merges similar code paths to see where most of the time is spent:
sudo cargo flamegraph --profile=release --reverse --min-width=0.5 -- --project-root=../your_app check
For more, see: https://nnethercote.github.io/perf-book/profiling.html
Local Development
Running the CLI in release mode against a target app
RUST_LOG=debug time cargo run --profile=release -- --project-root=../your_app check