AI Code Maintainability Scoring & Refactoring Engine

Revision Notes — Light-touch interview prep (2–3 sentences + one technical detail per topic)
Scope reminder: this project was deliberately scoped as light-touch, not a deep-dive like Urdu Sentiment or Medical Image. Use the "One-Line Summary" and "Quick Answers" sections as your primary prep. The deeper sections exist so you're not caught flat-footed on a follow-up, not so you memorize all of it.

What Is This Project? (Elevator Pitch)

"I built a two-phase AI system that scores the structural quality of Python code and then autonomously refactors risky code to improve it. Phase 1 uses AST parsing + a Random Forest classifier to predict a maintainability risk score. Phase 2 uses a CodeT5 deep learning model to generate and iteratively select better versions of risky code."

The Problem It Solves

Traditional linters (PyLint, Flake8) use rigid, hand-written rules. This engine instead learns what "risky" structure looks like from data — deep nesting, high complexity, large functions — and then goes a step further than any linter by actually generating improved code, not just flagging problems.

Full Pipeline Flow

Input Code │ ▼ PHASE 1 — Evaluator AST Parsing → Feature Extraction → ML Scoring → Explanation │ ▼ Risk Score (0–100) + Top 3 Reasons │ ▼ PHASE 2 — Refactorer (only if risky) Generate Candidates (CodeT5) → Validate → Re-score via Phase 1 → Select Best │ (loop until target score or max iterations) ▼ Improved Code

Phase 1 — The Evaluator

1. AST Analyzer — 11 Structural Features

Parses code into an Abstract Syntax Tree (not line-by-line text reading — actual structural traversal via ast.walk()) and extracts 11 signals:
FeatureWhat it measures
max_nesting_depthStrongest predictor — deepest if/for/while/try nesting
cyclomatic_complexity1 + every decision point (if/for/while/except/with/assert/bool-op)
avg_function_lengthMean lines per function
num_functions, num_loops, num_if, num_try_except, num_returnRaw structural counts
line_countTotal file size
recursion_flag1 if any function calls itself, else 0
global_variable_countCount of global declarations — hidden state / tight coupling
One technical detail worth knowing cold: nesting depth is computed via a recursive traversal that increments depth only when it enters a defined set of "nesting nodes" (If, For, While, With, Try, FunctionDef, AsyncFunctionDef, ClassDef) — everything else keeps the current depth.

2. Dataset Generation

Synthetic dataset generated from code templates, not scraped real-world code. 220 total samples, perfectly balanced: 110 labeled Clean (0), 110 labeled Risky (1). Reproducible via random.seed(42) — same dataset every run.

3. Feature Pipeline

Converts the feature dictionary into a fixed-order numeric vector (FEATURE_SCHEMA defines the order — this order must stay identical between training and inference or predictions break silently). Scales with StandardScaler (mean=0, std=1) so no single feature like line_count dominates just because its raw numbers are bigger. Scaler is pickled and reused at inference time — never re-fit on new data.

4. Model Training

Two models trained side by side on an 80/20 stratified split (176 train / 44 test):
ModelKey hyperparameters
Random Forest primary / production200 trees, max_depth=10, min_samples_split=4, class_weight="balanced"
XGBoost comparison only200 estimators, max_depth=6, learning_rate=0.1, subsample=0.8
Random Forest is the one actually used in the live Scoring API — XGBoost is trained and evaluated for comparison but not deployed.
No saved accuracy/F1 number exists in the repo — the evaluation function prints it live but doesn't persist it to a file. Run python model_trainer.py before your interview and note the actual number rather than guessing one.

5. Explanation Engine

Answers "why is this risky" without recomputing anything new. Logic: for each of the 11 features, check if the code's actual value exceeds a fixed threshold (e.g. max_nesting_depth > 3, cyclomatic_complexity > 5, global_variable_count > 1). Of the features that exceed threshold, rank by the model's feature importance and return the top 3 as plain-English sentences (e.g. "Deep nesting detected (depth: 7)").

6. Scoring API — Final Output

Single entry point: evaluate(code). Runs the model's predicted probability of the "risky" class × 100 as the risk score.
Risk ScoreLevel
0–30Low
31–60Medium
61–100High
{
  "risk_score": 82,
  "risk_level": "High",
  "confidence": 0.82,
  "top_risk_factors": ["Deep nesting detected (depth: 7)", ...]
}
Properties worth naming if asked: deterministic, stateless, fast (no training happens at inference time).

Phase 2 — The Refactorer

Only runs on code Phase 1 flagged as risky. Uses CodeT5 (a deep learning code-generation model) to produce multiple refactored candidates, then uses Phase 1's own evaluate() as a reward/ranking function to pick the best one.

1. Candidate Generation — 3 Strategies

Generates one candidate per strategy, each with a different prompt and temperature:
StrategyTemperature
Improve readability and clarity0.5 (conservative)
Reduce nesting and simplify logic0.7
Refactor for strict maintainability best practices0.85 (more creative)
One technical detail: temperature controls how much the model deviates from the "safe" rewrite — low temperature stays close to minimal edits, high temperature takes bigger structural risks.

2. Validation → Selection → Iteration

Each candidate is checked to actually compile (no syntax errors) before it's even considered. Valid candidates are re-scored by feeding them back through the Phase 1 evaluate() API, and the lowest risk score wins that round.

Iterative loop defaults: target_score=20, max_iterations=3. Stops early if the target is hit, or after 3 rounds regardless. Starts by initializing the best-known score to float('inf') — a simple trick meaning "anything found is automatically an improvement over nothing."

Tech Stack

ComponentTechnology
Structural AnalysisPython ast module
Risk PredictionRandom Forest (primary), XGBoost (comparison) — scikit-learn
RefactoringCodeT5 (Transformers / PyTorch)
APIUnified main.py entry point + api_server.py
DeploymentLive at ai-code-maintainability.hmuhammadusman.com

Numbers to Remember

Structural features extracted11
Total dataset samples220 (110 Clean / 110 Risky — balanced)
Train / test split176 / 44 (80/20, stratified)
Random Forest trees200 (max_depth=10)
Risk score range0–100
Top risk factors surfaced3
Refactor candidate strategies3 (temps 0.5 / 0.7 / 0.85)
Default optimizer target / max iterations20 / 3
Reproducibility seed42

Quick Answers — Likely Questions

"Why Random Forest over a simpler rule-based linter?"
Rule-based linters need every threshold hand-tuned per rule. A trained model learns which combinations of features actually correlate with risk from data, and can weigh 11 signals together instead of checking them independently.
"Why two models (RF + XGBoost) if only one is deployed?"
Comparison during development — training both and comparing accuracy tells you whether the extra complexity of boosting is worth it on this dataset size before committing to one in production.
"Isn't 220 samples very small for ML?"
Yes — be upfront about this if asked. It's a synthetic, template-generated dataset, not real-world code, which is both the honest limitation and a natural "what I'd improve next" answer (train on real open-source repos labeled by actual maintainability metrics).
"How does Phase 2 know a candidate is actually better, not just different?"
It doesn't trust the language model's own judgment — every candidate gets re-scored through the exact same Phase 1 evaluate() pipeline used on the original code, so "better" is measured by the same objective risk score, not by how the refactor looks.
"What would you improve?"
Real-world training data instead of synthetic templates; persisting evaluation metrics instead of only printing them; and multi-file / cross-function analysis, since right now every file is scored in isolation.

One-Line Summary to Open With

"I built a two-phase AI system — Phase 1 uses AST parsing and a Random Forest model to score Python code's maintainability risk out of 100 and explain why, and Phase 2 uses a CodeT5 deep learning model to iteratively generate and select better refactored versions of risky code, using the Phase 1 score itself as the judge of improvement."