What is technical debt?
Technical debt is the cost of additional work caused by shortcuts in the code: quick fixes, untested code, poor naming, unnecessary complexity. Like financial debt, it accumulates interest if not repaid.
rsonar quantifies this debt in estimated
remediation minutes, using the SQALE (Software
Quality Assessment based on Lifecycle Expectations) model used by
SonarQube.
The SQALE Model in rsonar
Debt calculation
The total debt is the sum of costs per category:
With default costs:
| Category | Default unit cost |
|---|---|
Lint error (error) |
30 min |
Lint warning (warning) |
10 min |
| Lint style violation | 2 min |
| Improperly formatted file (styler) | 5 min |
| Goodpractice failure | 20 min |
| Missing coverage point | 5 min/point |
SQALE rating calculation
The rating is computed from the debt-to-base-effort ratio:
| Ratio | Rating | Meaning |
|---|---|---|
| < 5% | A | Excellent quality |
| 5–10% | B | Good quality |
| 10–20% | C | Acceptable quality |
| 20–50% | D | Poor quality |
| > 50% | E | Critical |
quality_score() exposes the same debt ratio as an
easy-to-read percentage for IDE usage:
Example analysis
library(rsonar)
res <- sonar_analyse(".")
debt <- debt_index(res)
print(debt)
# Fast local score shown in IDE
quality_score(res)── rsonar Technical Debt ───────────────────────────────────
ℹ Estimated duration: 2.17h (130 min)
ℹ SQALE rating: C
── Breakdown by category ────────────────────────────────────
category issues minutes
1 Lint (errors) 2 60
2 Lint (warnings) 4 40
3 Best practices 1 20
4 Coverage 2 10
Customizing costs
Default costs can be adjusted to match your organization’s context:
# Stricter costs for a critical project
debt <- debt_index(res,
cost_lint_error = 60, # 1h per error
cost_lint_warning = 15,
cost_style = 10,
cost_gp = 30,
coverage_target = 90, # Target: 90%
cost_coverage_point = 8
)Remediation strategy
Prioritization: return on investment
Sort by debt_minutes / number_of_corrections_needed
ratio:
debt <- debt_index(res)
# Display categories with the most debt
breakdown <- debt$breakdown[order(-debt$breakdown$minutes), ]
print(breakdown)Progressive approach (legacy debt)
For an existing project with a lot of debt, adopt an incremental approach:
# Week 1: fix lint errors (highest impact)
# Week 2: add tests to increase coverage
# Week 3: reformat the code (styler::style_dir)
# Week 4: address warnings and goodpractice
# Track debt evolution over time
debt_week_1 <- debt_index(res_before)$minutes
debt_week_4 <- debt_index(res_after)$minutes
reduction_pct <- round(100 * (1 - debt_week_4 / debt_week_1), 1)
cat("Debt reduction:", reduction_pct, "%\n")Comparing analyses with sonar_diff()
Use sonar_diff() to see exactly what changed between two
analysis runs:
baseline <- sonar_analyse(".")
# ... make improvements ...
current <- sonar_analyse(".")
diff <- sonar_diff(current, baseline)
print(diff)This shows delta metrics, new issues introduced, and issues fixed.
Trend tracking in CI
Using sonar_trend()
sonar_trend() appends each analysis to a JSON history
file, making it easy to monitor quality evolution across builds:
res <- sonar_analyse(".")
sonar_trend(res, file = "rsonar-history.json")In your .gitlab-ci.yml:
GitLab — rating as a metric
In your .gitlab-ci.yml, expose the rating as a
metric:
rsonar-metrics:
script:
- |
Rscript -e "
library(rsonar)
res <- sonar_analyse('.')
debt <- debt_index(res)
cat('SQALE rating:', debt\$rating, '\n')
cat('Debt minutes:', debt\$minutes, '\n')
# Fail if rating is D or E
if (debt\$rating %in% c('D', 'E'))
stop('Insufficient SQALE rating: ', debt\$rating)
"Best practices for maintaining low debt
-
Configure
.lintrfrom the start of the project withuse_rsonar_lintr() -
Format systematically before each commit:
styler::style_dir("R") - Write tests alongside code, not after
-
Block regressions in CI with
quality_gate(fail_on_error = TRUE) - Don’t accumulate commented code — use Git history
-
Name clearly functions and variables —
object_length_linter(30)
The best way to manage technical debt is not to accumulate it.