| doc-type | issue | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| issue-type | task | ||||||||||
| status | draft | ||||||||||
| priority | p1 | ||||||||||
| github-issue | |||||||||||
| spec-path | docs/issues/drafts/1669-01-establish-baseline-analysis.md | ||||||||||
| branch | |||||||||||
| related-pr | |||||||||||
| last-updated-utc | 2026-05-18 12:00 | ||||||||||
| semantic-links |
|
Produce two committed artifacts that characterize the current workspace:
- Coupling report — for every workspace package, list its workspace-level dependencies and, for each dependency, the specific items (types, constants, traits, functions) actually imported from it. The report reveals weak dependencies (a package that imports only one constant from another) and tight clusters, and informs every subsequent extraction subissue.
- README audit table — a single table rating each package's README on a three-point scale (good / minimal / stub), to identify documentation gaps.
Both artifacts are generated by a reproducible Rust binary (contrib/dev-tools/analysis/workspace-coupling/)
so they can be refreshed after each structural change without manual effort.
This issue is a subissue of EPIC #1669 (Overhaul: Packages).
The workspace contains 27 packages (including the root torrust-tracker crate) that grew organically over multiple refactoring cycles.
Two coupling problems have already been identified manually:
torrust-clockpreviously depended ontorrust-tracker-primitivesonly to importDurationSinceUnixEpoch(resolved by SI-02).torrust-tracker-configurationdepended ontorrust-clockonly to importDEFAULT_TIMEOUT(tracked in SI-03).
These were discovered through code inspection. A systematic analysis would surface similar findings across all 27 packages without relying on luck or familiarity with the codebase.
Knowing that "package A declares a Cargo dependency on package B" is not enough to assess whether the coupling is appropriate. The item-level view answers:
- Thin dependency: A imports only one constant or one type alias from B → move that item, break the dependency edge.
- Cluster dependency: A imports a cohesive subset of B's API → consider extracting that subset into a new package.
- Deep dependency: A uses many items across B's API → coupling is substantial and intentional; extraction would require significant refactoring.
The Rust binary performs two passes using cargo metadata and a text scan:
- Pass 1 (Cargo.toml graph) — runs
cargo metadatato enumerate all workspace members and their declared workspace-level dependencies (normal, dev, and build), grouped by dependency kind. - Pass 2 (source scan) — for each declared dependency edge
A → B, scansA'ssrc/directory foruse B_module::import statements and fully-qualifiedB_module::path references. Extracts distinct top-level import paths.
The output is a markdown report saved to
docs/issues/open/1669-overhaul-packages/workspace-coupling-report.md.
- Create Rust binary
contrib/dev-tools/analysis/workspace-coupling/— the report generator. - Run the binary and commit the resulting report to
docs/issues/open/1669-overhaul-packages/workspace-coupling-report.md. - Write a brief README audit table (manually, based on inspection) in
docs/issues/open/1669-overhaul-packages/readme-audit.md. - Review the coupling report for thin-dependency findings and record them as observations in the coupling report itself or a linked notes section.
- Research whether
packages/configurationshould be split into per-service sub-packages (e.g., tracker-core config, UDP config, HTTP config, REST API config); see T8.
- Fixing any of the coupling issues found (each fix becomes its own subissue).
- Deciding to split or restructure
packages/configuration— that is a separate subissue if the T8 research finds it warranted. - Semantic domain graph, git co-change graph, or bounded-context analysis (deferred; revisit if the coupling report leaves open questions).
- Generating visual graphs (e.g. DOT/SVG) — the markdown table is sufficient for the first cycle; visualizations can be added if a graph helps communicate a specific finding.
Status values: TODO, IN_PROGRESS, BLOCKED, DONE.
| ID | Status | Task | Notes / Expected Output |
|---|---|---|---|
| T1 | TODO | Create Rust binary contrib/dev-tools/analysis/workspace-coupling/ and add it to workspace members |
Binary compiles cleanly (cargo build -p workspace-coupling) |
| T2 | TODO | Run binary; review output for obvious errors (missing packages, wrong module names) | Report covers all 27 workspace packages |
| T3 | TODO | Save report to docs/issues/open/1669-overhaul-packages/workspace-coupling-report.md and commit |
File committed in the analysis branch |
| T4 | TODO | Manually audit each package README; fill in docs/issues/open/1669-overhaul-packages/readme-audit.md table |
Table covers all 27 packages; rating = good / minimal / stub |
| T5 | TODO | Review coupling report; annotate thin-dependency findings (SI-02/SI-03 patterns and any new ones found) | Findings recorded in a "Observations" section at the bottom of the report |
| T6 | TODO | For each new thin-dependency finding: open (or update) a corresponding subissue in EPIC #1669 Active Subissues | New subissues added to EPIC quick list if applicable |
| T7 | TODO | Run linter all |
Exit code 0 |
| T8 | TODO | Research how to scope packages/configuration per service: (a) split into sub-packages, or (b) gate with Cargo features. Audit which config structs each service needs; prototype the two scenarios below for each approach; record findings and open a new subissue if a change is warranted |
Findings section added to coupling report; new subissue opened if viable |
The goal is to understand how hard it is today to build a smaller tracker binary by assembling only the packages a given deployment really needs. Build one prototype per scenario on the current codebase (no refactoring; just wiring what exists):
| # | Scenario | Required packages (expected) | Key question |
|---|---|---|---|
| P1 | Public UDP-only tracker (no API) | tracker-core, udp-tracker-core, udp-tracker-server, configuration (UDP + core subset) |
Can the binary compile without HTTP/REST-API packages? |
| P2 | Private HTTP tracker + REST management API (no UDP) | tracker-core, http-tracker-core, axum-http-tracker-server, axum-rest-tracker-api-server, configuration (HTTP + REST-API + core subset) |
Can the binary compile without UDP packages? |
For each prototype record:
- Whether it compiled with zero changes to existing packages.
- Which
packages/configurationstructs were actually used and which were dead weight. - Any circular dependency or versioning problem that would block splitting.
- An estimate of binary size reduction vs. the full tracker binary.
| Trade-off | Notes |
|---|---|
| Smaller / safer binaries (reduced attack surface) | Benefit for users who need only one protocol |
| Custom container builds required | Users must build their own images; no official slim images today |
| Incomplete config files | A UDP-only binary would not parse HTTP config sections; partial configs need clear schema boundaries |
| Versioning complexity | Per-service versions are too complex; a single version-per-config-file concept does not map well either. Coordinated versioning (all config sub-packages share a version, bumped together on any breaking change) sounds reasonable but is hard to maintain. Core goal: avoid forcing consumers to import the whole tracker config when they only need, e.g., the UDP config. |
packages/configuration as re-export facade |
Splitting does not require removing packages/configuration; it can re-export from the specialized sub-packages so that the main full-tracker binary and all existing code continue to work without refactoring. |
| Cargo features as alternative to splitting | Instead of separate packages, add Cargo features to packages/configuration (e.g., udp, http, rest-api). Consumers enable only the features they need; the main binary enables all. No package-splitting overhead, no versioning coordination problem. Trade-off: one package is still pulled in as a dependency even if only a small feature is used; all feature combinations must be tested. |
| "Symphony vs Laravel" | Symphony: compose from packages; Laravel: enable/disable in one binary. Current tracker is closer to Laravel. |
Conclusion from T8 feeds into a new subissue (if splitting is warranted) or an explicit "will not split" decision recorded in the coupling report observations.
- Spec drafted in
docs/issues/drafts/ - Spec reviewed and approved by user/maintainer
- GitHub issue created and issue number added to this spec
- Spec moved to
docs/issues/open/with issue number prefix - Script written and reviewed
- Coupling report generated and committed
- README audit table committed
- Observations section written
- EPIC #1669 Active Subissues table updated to
DONE - Issue closed and spec moved to
docs/issues/closed/
- 2026-05-18 00:00 UTC - GitHub Copilot - Spec drafted as subissue SI-01 of EPIC #1669. Scope refined during discussion: item-level import scan is central (not optional) because without it thin-dependency patterns like SI-02/SI-03 cannot be found systematically.
- 2026-05-18 12:00 UTC - josecelano - Added T8: research whether
packages/configurationshould be split into per-service sub-packages. Includes two prototype scenarios (UDP-only and HTTP+REST-API) and a trade-off table. Outcome either opens a new subissue or records a "will not split" decision.
-
contrib/dev-tools/analysis/workspace-coupling/exists, compiles cleanly (cargo build -p workspace-coupling), and produces valid markdown output. -
docs/issues/open/1669-overhaul-packages/workspace-coupling-report.mdis committed and covers all 27 workspace packages. - Every workspace package that has workspace-level dependencies appears in the report with at least one import path listed per dependency (or a documented reason why none was found).
-
docs/issues/open/1669-overhaul-packages/readme-audit.mdis committed with a rating for each of the 27 packages. - Any thin-dependency findings not already covered by existing subissues are recorded as observations in the coupling report.
- T8 research findings (configuration splitting) are recorded in the coupling report or a linked observations file; either a new subissue is opened or a "will not split" decision is documented.
-
linter allexits with code0.
linter all(markdownlint, taplo, cspell, rustfmt, clippy)cargo build -p workspace-coupling
| ID | Scenario | Expected Result |
|---|---|---|
| MV1 | Open docs/issues/open/1669-overhaul-packages/workspace-coupling-report.md and count package sections |
27 packages total: 5 leaf packages listed in the "no workspace dependencies" section; 22 packages in the coupling detail sections |
| MV2 | Find torrust-tracker-configuration in the report; check the torrust-clock dep section |
Should list torrust_clock::DEFAULT_TIMEOUT (confirms SI-03 detection) |
| MV3 | Find torrust-clock in the report; check historical observations for the old primitives dependency edge |
Should mention DurationSinceUnixEpoch move as the SI-02 resolution context |
| MV4 | Run cargo run -p workspace-coupling -- /tmp/test-report.md on a clean checkout |
Binary exits 0; output file matches committed report structurally |
- EPIC:
docs/issues/open/1669-overhaul-packages/EPIC.md - Coupling report (generated):
docs/issues/open/1669-overhaul-packages/workspace-coupling-report.md - README audit (generated):
docs/issues/open/1669-overhaul-packages/readme-audit.md - Report generator:
contrib/dev-tools/analysis/workspace-coupling/ - Existing thin-dependency subissues: SI-02, SI-03