Maintainability Index (MI)
Maintainability index is a composite metric that uses a logarithmic formula to predict how difficult a piece of code will be to maintain over time.
Calculated as MI = 171 - 5.2 * ln(HALVOL) - 0.23 * CYCLO - 10.2 * ln(SLOC), where HALVOL is Halstead Volume, CYCLO is Cyclomatic Complexity and SLOC is Source Lines Of Code.
The tool uses the normalized version of the formula to ensure the result is between 0 and 100: MI' = max(0, (MI * 100) / 171).
Low maintainability index indicates a function/method/constructor that is difficult to understand, change and maintain.
Our definition uses 10.2 as a modifier for SLOC, instead of the original 16.2, because the number of lines of code does not contribute as significantly to the overall complexity of functions/methods in Dart/Flutter codebases.
Why Be Cautious
Low maintainability index can lead to:
- High Maintenance Costs: Low-maintainability code demands more time and effort to modify or troubleshoot.
- Increased Risk of Bugs: Difficult-to-maintain code can lead to more errors due to limited clarity.
How to Address Low Maintainability Index?
To address low maintainability index, try addressing each dependent metric: HALVOL, CYCLO and SLOC.
Config Example
Set halvol-modifier (default is 5.2) to change how Halstead Volume contributes to the overall metric result.
Set cyclo-modifier (default is 0.23) to change how cyclomatic complexity contributes to the overall metric result.
Set sloc-modifier (default is 10.2) to change how the number of source lines of code contributes to the overall metric result.
dcm:
metrics:
maintainability-index:
threshold: 0.5
halvol-modifier: 5.2
cyclo-modifier: 0.23
sloc-modifier: 10.2
To set multiple threshold or other common config options, refer to the Configuring Metrics page.
Example
To view what contributes to the metric value, generate an HTML report.
❌ Bad: Low Maintainability Index
// MI: ~58.7% | CYCLO: 9 | SLOC: 26 | HALVOL: ~891.70
Future<Map<String, Object?>> processCheckout(User u, Cart c, String? promo) async {
logger.info("Starting checkout for user: ${u.id}");
if (u.isActive && !u.isBanned) {
if (c.items.isNotEmpty) {
double total = c.calculateSubtotal();
if (promo != null && promo == 'WINTER25') {
total = total * 0.85;
} else if (u.isPremium) {
total = total * 0.95;
}
try {
final success = await payment.charge(total, u.id);
if (success) {
final orderId = "ORD-${DateTime.now().millisecondsSinceEpoch}";
await repo.saveOrder(orderId, total, c.items.map((i) => i.id).toList());
return {'status': 'success', 'id': orderId, 'total': total};
} else {
return {'status': 'error', 'msg': 'Payment Refused'};
}
} catch (e) {
logger.error("Checkout crash: ${e.toString()}");
return {'status': 'error', 'msg': 'System Failure'};
}
}
return {'status': 'error', 'msg': 'Empty Cart'};
}
throw Exception("Unauthorized Access");
}
✅ Good: High Maintainability Index
// MI: ~68% | CYCLO: 5 | SLOC: 10 | HALVOL: ~345.95
Future<Map<String, Object?>> processCheckout(User u, Cart c, String? promo) async {
if (!u.canCheckout) throw Exception("Unauthorized Access");
if (c.items.isEmpty) return _err("Empty Cart");
try {
final total = c.calculateDiscountedTotal(u, promo);
final success = await payment.charge(total, u.id);
if (!success) return _err("Payment Refused");
return await _finalizeOrder(u.id, total, c.items);
} catch (e) {
return _err("System Failure");
}
}