
Sergio GuadarramaOr: How we prevented architectural debt before it even happened SonarQube catches bugs & code...
Or: How we prevented architectural debt before it even happened
Let me paint a picture you might recognize.
You have a team of developers. Maybe 10, maybe 100. You've planned a beautiful architecture:
┌─────────────────────────────────────┐
│ Presentation Layer │
│ (Components, Controllers, Pages) │
└──────────────────┬──────────────────┘
│ (through services)
┌──────────────────▼──────────────────┐
│ Service Layer │
│ (Business Logic, Domain Logic) │
└──────────────────┬──────────────────┘
│ (through repositories)
┌──────────────────▼──────────────────┐
│ Data Access Layer │
│ (Repositories, Queries, DB) │
└─────────────────────────────────────┘
Beautiful. Clean. Maintainable.
Then month 2 happens.
A component directly imports from the database layer "just this once."
A service calls another service that hasn't been created yet.
A utility function imports from the presentation layer.
Month 3? You don't have an architecture anymore. You have... spaghetti.
SonarQube catches:
Code Review catches:
The Gap:
Enter architect-linter. A linter specifically for architecture.
Think ESLint (which catches code style), but for your system design.
$ architect-linter-pro --check
❌ src/components/Button.tsx imports from src/api/user.ts
Rule violation: Presentation layer cannot import from API layer
Suggestion: Use src/services/UserService instead
❌ src/services/Auth.ts imports from src/components/LoginForm.tsx
Rule violation: Service layer cannot import from Presentation layer
✅ Rest of architecture is clean
{
"architecture_pattern": "Hexagonal",
"forbidden_imports": [
{
"from": "src/presentation/**",
"to": "src/infrastructure/**",
"reason": "Presentation shouldn't depend on infrastructure details"
},
{
"from": "src/application/**",
"to": "src/infrastructure/**",
"reason": "Application layer is infrastructure-agnostic"
}
]
}
# .github/workflows/architecture.yml
- name: Check Architecture
run: architect --check
❌ PR blocked: Architecture violations detected
- 2 violations found
- Fix before merging
Unlike ESLint (JS only) or Pylint (Python only), architect-linter works across 4 languages:
Perfect for monorepos with mixed stacks:
frontend/ (TypeScript + React)
backend/ (Python + FastAPI)
services/ (PHP + Laravel)
↓
architect-linter enforces SAME rules across all 3
Before architect-linter:
After architect-linter (2 months):
| Need | Tool | Why |
|---|---|---|
| Catch bugs | SonarQube | Finds logic errors, security issues |
| Review logic | Code Review | Humans are best here |
| Enforce architecture | architect-linter | Automatic, systematic, consistent |
| All together | All 3 | SonarQube + architect-linter in CI, code review for design |
cargo install architect-linter-pro
architect-linter-pro --init
# Interactive wizard creates architect.json
architect-linter-pro --check
# GitHub Actions
- run: architect-linter-pro --check
# GitLab CI
script:
- architect-linter-pro --check
# Pre-commit hook
# .pre-commit-config.yaml
- repo: https://github.com/sergiogswv/architect-linter-pro
rev: v5.0.0
hooks:
- id: architect-linter-pro
| Feature | architect-linter | ESLint | SonarQube | Manual Review |
|---|---|---|---|---|
| Multi-language | ✅ Yes | ❌ JS only | ✅ Yes | ✅ Yes |
| Architecture rules | ✅ Yes | ❌ No | ⚠️ Limited | ✅ Yes |
| Fast | ✅ Rust, parallel | ✅ JS | ❌ Slow | ❌ Very slow |
| Free | ✅ Open source | ✅ Open source | ❌ $10k+/year | ✅ Yes |
| Easy setup | ✅ 5 min | ✅ 5 min | ❌ 2 hours | N/A |
| Automation level | ✅ 100% | ✅ 100% | ✅ 90% | ❌ 0% |
cargo install architect-linter-pro
architect-linter-pro --init
architect-linter-pro --check
Takes 5 minutes. Your architecture will thank you.
Let me know what you think!