Module Boundaries
Build systems and package managers formalize the boundaries developers intended. When a developer declares a build target, package, or module, they are encoding a structural decision about what belongs together. This lens reads those declarations directly rather than inferring them from file proximity.
Signals and Indicators
Build manifests to locate:
Package.swift(Swift Package Manager) — each.target()and.testTarget()declaration names an explicit module with a defined source directorybuild.gradle/build.gradle.kts(Gradle) — top-levelapply plugin:blocks and subproject includes insettings.gradlemark module roots;implementation/apivscompileOnlydependency configurations reveal visibility intentpackage.json(Node/npm/yarn) — thenamefield andexportsmap define the public surface; workspaces in a monorepo root list all sub-packages*.csproj/*.sln(MSBuild) — each.csprojis a distinct assembly; solution file project references encode the dependency graphPodfile/podspec(CocoaPods) — each podspec is a distributable module boundaryMODULE.bazel/BUILD/BUILD.bazel(Bazel) —cc_library,swift_library,kt_jvm_libraryrules define fine-grained build unitsCargo.toml(Rust) —[workspace]members and[lib]/[[bin]]sections define crate boundariesgo.mod(Go) — each module root is a deployable unit; subdirectorypackagedeclarations name internal groupings
Directory naming conventions:
- Directories named
core,common,shared, orutilat the root of a module often represent deliberate cross-cutting infrastructure — note but do not automatically split them - Directories named after product features (
auth,payments,checkout) suggest feature-module organization - A
modules/orpackages/top-level directory almost always signals a monorepo with multiple independent scopes
Access control declarations:
- Swift
internalvspublicvspackagekeywords on types and functions - Kotlin
internalvisibility modifier - TypeScript
exportstatements inindex.tsbarrel files - C#
internalvspublicclass visibility - Java package-private (default) vs
publicvisibility
Boundary Detection
Each independently declared build target or package is a primary scope group candidate. Apply these rules:
- One target, one candidate. If the build system declares it separately, treat it as a candidate scope group regardless of size. A 3-file Swift target is still a distinct module.
- Merge trivial wrappers. If a target contains only re-exports of another target with no logic of its own, it may belong with the target it wraps.
- Split large monolithic targets. A single build target containing 200+ files likely conflates multiple concerns. Flag for secondary analysis using
interface-cohesionanddependency-clusterslenses. - Test targets follow their main target.
AuthTestsbelongs withAuth— do not create a separate scope group for test targets unless they test multiple main targets. - Vendor/third-party directories are excluded.
node_modules/,Pods/,vendor/,.build/— these are external dependencies, not scope groups.
Findings Format
MODULE BOUNDARIES FINDINGS
==========================
Build System: <e.g., Swift Package Manager, Gradle, npm workspaces>
Manifest Files Found: <list with paths>
Declared Modules:
- <ModuleName> (<path>) — <file count> files, <dependency count> declared dependencies
- ...
Anomalies:
- <ModuleName>: <reason — e.g., "single target with 300+ files, likely conflates concerns">
- ...
Recommended Scope Group Candidates:
- <ModuleName> — <one-line rationale>
- ...