I'm a 4th-year BSc Computer Science (Data Science & AI) student at the University of Dundee, on track for a First-Class degree. I build production-grade systems across three tracks: event-driven data pipelines (Kafka, Airflow, AWS/GCP, Star Schema), end-to-end ML and LLM systems (XGBoost, MLflow, QLoRA fine-tuning, RAG, LangChain, execution-based LLM evaluation, statistical model promotion), and full-stack cloud applications (React, Flask/FastAPI, AWS/GCP, CI/CD) - backed by 7,281 automated tests across five featured projects (1,452 DevSync + 1,402 LAAD + 734 W3C ETL + 2,237 StockLens + 1,456 SWE-Qwen). I care about engineering rigour: dead-letter routing before data hits a database, leakage prevention before any CV fold runs, execution-based evaluation before a model earns champion status, and deployment pipelines that abort on failure rather than hoping nothing breaks.
I'm currently seeking a post-graduate role starting in 2027, in Data, AI or Software Engineering.
- 🎓 BSc (Hons) Computer Science (Data Science & AI) - expected graduation June 2028
- 🏆 AWS Academy - Machine Learning Foundations
- 🏅 Microsoft Learn - Foundations of Azure AI: Concepts, Capabilities, and Implementation
- 🏆 AWS Academy - Cloud Foundations
- 📍 Based in Dundee, Scotland - open to relocation
I love building robust, efficient code and optimising for speed wherever it counts - profiling before guessing, cutting hot-loop waste, choosing the right data structure over the clever one, and measuring every change so "fast" is a number, not a feeling. I treat reliability and observability as non-negotiable from the start, not retrofitted after the fact. That means dead-letter routing before data reaches a database, leakage prevention baked into sklearn Pipelines before any cross-validation fold runs, and CI/CD gates that abort deployment on any test failure rather than hoping nothing breaks in production. I'm drawn to problems where silent failures are the hardest kind to debug - concurrent write contention, foreign key mismatches, retrieval quality in RAG systems, and I build systems that make those failures impossible to miss. I work from requirements before writing code - functional, non-functional, and acceptance criteria first - and treat API documentation, schema contracts, and test plans as deliverables in their own right, not afterthoughts. When it comes to LLMs, the only score I trust is execution: applying the model's patch to the real repository and running the real tests - and no champion is promoted unless the gain clears a paired-bootstrap confidence interval.
Python 3.11 PyTorch QLoRA PEFT Unsloth bitsandbytes TRL Hugging Face Transformers Qwen3-14B vLLM FastAPI Uvicorn Modal Weights & Biases Langfuse Google Cloud BigQuery Terraform Docker GitHub Actions pytest
Production-grade LLMOps platform - ingests 20,477 real SWE-bench GitHub issues into a 17,456-example training corpus, fine-tunes 3 QLoRA variants of Qwen3-14B on Modal A100-80GB GPUs, and measures each model the only way that matters - executing its generated patches against FAIL_TO_PASS / PASS_TO_PASS tests inside real SWE-bench Docker images. Promotion to champion is a statistical decision (Wilson CIs, McNemar, paired bootstrap), never a merge; the winner is served through an OpenAI-compatible, scale-to-zero inference API with per-request LoRA adapters - all orchestrated by Terraform on Google Cloud, tracked end-to-end in Weights & Biases, and gated by 4 GitHub Actions workflows.
- Data engineering pipeline: 20,477 raw instances → pydantic-validated (7 rejected) → 17,456 cleaned through 6 counted quality gates + exact/semantic dedup (726) → repo-stratified 80/10/10 splits (15,011 / 1,556 / 889, zero cross-repo leakage) → 2,313-instance golden set carved before tokenization → 14,833 training examples. Every stage hash-pinned in a manifest, versioned in W&B and mirrored to GCS.
- QLoRA fine-tuning: 3 variants (r16/α32, r32/α64, lr 5e-5) of Qwen3-14B in 4-bit NF4 via Unsloth + FlashAttention on Modal A100-80GB - image-as-code, no Dockerfile, ~1.76% of parameters trained; adapters + tokenizer + chat template shipped to W&B.
- Execution-based evaluation: materializes
repo@base_shain official SWE-bench Docker images, applies model patches via a 3-strategy fallback (git apply→gnu patch --fuzz→unidiff), and runs real FAIL_TO_PASS + PASS_TO_PASS with flaky retries. Promoted champion: 17.20% F2P vs 2.46% baseline (7.0×, 95% CI 11.1–25.8%), 90.10% P2P. - Statistical promotion: 4-condition gate - F2P ≥ 15% & P2P ≥ 90% floors, paired-bootstrap CI lower bound strictly > 0, no P2P regression > 2pt, silent promotions rejected. Full decision record in the W&B
eval-championregistry; the model can't approve its own regression (PRs read / main writes). - OpenAI-compatible serving: FastAPI + vLLM (AWQ int4,
enable_lora) with per-request LoRA adapters pulled from W&B on demand, SSE streaming, faithful 401/422/404/500 envelopes, scale-to-zero - $0.00 when idle. - Observability & infra: W&B artifacts/runs, Langfuse (10% trace sampling), GCP Logging, dashboards-as-code; 1,456 tests + ruff + mypy strict; Terraform IaC with GCP Workload Identity Federation - zero static cloud keys.
Python FastAPI PostgreSQL Apache Kafka Redis XGBoost Scikit-learn Pandas LangChain ChromaDB Ollama MLflow SageMaker React Vite Tailwind CSS Chart.js Nginx Docker Terraform GitHub Actions Playwright AWS
Industry project for NCR Atleos - ATM log ingestion pipeline with 3-layer anomaly detection (XGBoost + Isolation Forest at 99.8% CV accuracy, Z-score, heuristic rules across 49 features) and a fully air-gapped LangChain + ChromaDB RAG diagnostic assistant with cross-encoder reranking, self-consistency scoring, reflexion, and 4-signal confidence fusion — no external API calls. Ingests 930K+ events from 7 log sources through Kafka KRaft with hybrid Redis/in-memory dedup and dead-letter routing. Deployed via Terraform (10 modules, 118 resources) to ECS Fargate behind 3 OIDC-authenticated CI/CD pipelines. 1,402 tests (923 pytest + 394 vitest + 10 Playwright E2E + 75 Terraform IaC) gate every PR; patched 5 pre-release defects including a JWT privilege escalation vulnerability.
- Kafka event streaming: KRaft mode, 2 topics × 3 partitions, at-least-once delivery with manual offset commits. Hybrid deduplication: Redis SET with 1h TTL + 10K-entry in-memory LRU fallback.
- 3-layer detection engine: ML_ENSEMBLE (XGBoost + Isolation Forest, 99.8% CV accuracy) + ZSCORE (rolling 20-window sigma) + HEURISTIC (7 deterministic multi-source correlators). 600s configurable sliding window, 10-min cross-layer dedup.
- Agentic RAG: Cross-encoder reranking (ms-marco-MiniLM), 3-sample self-consistency with 3-gram Jaccard similarity, Reflexion (self-critique → regenerate), citation grounding with regex entity verification. 4-signal confidence fusion: retrieval (30%) + consistency (25%) + verbalized (25%) + grounding (20%).
- Redis 8 patterns: Rate limiting (sorted set), deduplication (set + TTL), JWT blacklist (string + TTL), distributed locks (SET NX EX), Pub/Sub streaming, response caching, dead-letter queue (streams with exponential backoff), analytics counters (INCR + HLL + ZINCRBY).
- MLOps via MLflow: Experiment tracking, model registry with "champion" aliases. 7 artifacts per training run: xgb_classifier, isolation_forest, scaler, label_encoder, feature names, IF feature indices, calibrated UNKNOWN threshold. XGBoost model deployed to SageMaker
laad-xgb-championendpoint on ml.t2.medium for inference. - IaC & documentation: 10-module Terraform (118 resources, 9 modules with 75 test assertions). checkov compliance baseline with inline skips. 1,386-line W3C-pattern documentation with 10 mermaid diagrams covering architecture, Kafka error flow, DB ERD, detection engine, ML training, RAG pipeline, Redis data flow, frontend hierarchy, AWS VPC topology, and CI/CD pipeline.
- 1,402 automated tests (923 pytest across 10 tiers + 394 vitest + 10 Playwright E2E + 75 Terraform IaC) with checkov compliance scanning. 10-tier backend pytest suites: unit, integration, stress, security, ML, RAG, Redis, Kafka, generators, parsers. Terraform test covers 75 assertions across 9 modules.
React Tailwind CSS Flask Socket.IO PostgreSQL Docker AWS GitHub Actions Pytest Jest Cypress
Multi-service full-stack application - Flask API with Socket.IO real-time sync, React SPA served through nginx, and PostgreSQL on AWS RDS. Designed for team collaboration: task management, GitHub issue/PR linking, and role-based access control with OIDC-authenticated CI/CD.
- Multi-stage Docker: Backend compressed to 330MB (python:3.11-slim runtime, build deps stripped in stage 1). Frontend built on node:20-alpine, served by nginx:1.27-alpine with envsubst template for API_UPSTREAM. Docker resolver (127.0.0.11) for runtime DNS. Two-compose-file pattern cleanly separates PostgreSQL from the app stack. Docker layer caching (
type=gha) cuts rebuilds by 70%+ across CI runs. - Real-time collaboration: Flask-SocketIO with JWT-authenticated WebSocket handshake, project-scoped rooms preventing cross-project data leaks. Socket.IO-client on React side broadcasts task updates, comments, and notifications to all room members - zero polling.
- Full CI/CD with OIDC: GitHub Actions with OIDC federation (no static credentials). 7 path-aware CI jobs:
lint(ruff + ESLint),security(pip-audit + npm audit),backend-tests(518 Pytest — unit + Postgres 15 container),frontend-tests(929 Jest),e2e-tests(5 Cypress),backend-image-build(ECR, layer caching). CD deploys on main merge: ECS Fargate rolling update (200s health check), S3/CloudFront distribution. Any CI failure aborts the pipeline. Dependabot + CodeQL on a weekly cadence. - JWT dual auth: Access + refresh token flow with both cookie and Bearer header transport. 3-tier RBAC (Developer / Team Lead / Admin) enforced at endpoint level via decorators - middleware validates the numerical hierarchy so higher roles inherit all lower permissions.
- Database design: 12 PostgreSQL tables with SQLAlchemy ORM, composite indexes on (project_id, status) and (user_id, notification_type) for common query patterns. Full-text search on task titles. Audit logging with automatic timestamping across all entity mutations.
- 1,452 automated tests (518 Pytest + 929 Jest + 5 Cypress) gate every PR. Backend unit tests use in-memory SQLite (fast, zero-db); integration tests target a real Postgres 15 container spun up in CI. Coverage thresholds at 85% for both backend and frontend. Ruff-configured Python linting (pyproject.toml, E/F/I/N/W/UP/B/SIM) and ESLint for JavaScript enforce consistent code quality.
Databricks DLT dbt Apache Airflow Azure SQL Terraform Power BI Apache Spark PySpark Python Grafana Prometheus Delta Lake Docker GitHub Actions pytest
Serverless medallion architecture ETL processing 93 real W3C IIS log files (2009–2011) through Bronze → Silver in Databricks DLT, with pymssql JDBC export to Azure SQL and dbt-driven transformation (dual-dialect T-SQL/PostgreSQL) into a 16-model star schema. Orchestrated by Apache Airflow with Terraform-managed infrastructure, OIDC-secured CI/CD, and 3 Grafana dashboards.
- Serverless Databricks DLT: Bronze ingests 153,380 rows via Auto Loader with 7
@dlt.expect_or_dropquality checks (0 dropped). Silver enriches with 7 MaxMind GeoIP fields via consolidated struct UDF (3.5× faster than 7 separate UDFs), pairing City DB and ASN DB lookups from pure Python maxminddb — no compiled C extensions needed on serverless executors. 5 computed fields (page_category, traffic_type, is_crawler, size_band, referrer_domain) bring the total to 31 columns across 153,377 rows, spanning 30+ countries. - 45-second JDBC export: pymssql batch executemany (BATCH_SIZE=5000) with 4-attempt exponential backoff — 8–9× faster than the initial 413s implementation. Key optimisations:
tuple(row)overrow.asDict()(eliminates 4.7M dict allocations), Spark-side pre-filter beforecollect(), and removed redundant.count()scan. - Dual-dialect dbt (T-SQL/PostgreSQL): 16 models (10 staging + 6 marts) compile against both Azure SQL (production) and PostgreSQL (dev/CI) via inline
{% if target.type == 'sqlserver' %}branches — no separate_azure.sqlfiles. 18 T-SQL compatibility macros + 2 dispatch overrides. 121 data tests: 48 not_null · 18 unique · 21 accepted_values · 10 relationships · 24 expression_is_true. - Airflow Dataset-driven orchestration: 4 DAGs across 2 pipelines (Azure + Docker dev). Dataset outlet
mssql://...decouples ingestion from transformation — no polling overhead, no hard-coded DAG IDs. 4 operator types: DatabricksRunNow, DatabricksSubmitRun, PythonOperator, SparkSubmitOperator. - Full Terraform IaC + OIDC: 4 modules (networking, datalake, databricks, warehouse) + 24 Databricks resources managed as code. OIDC Workload Identity Federation eliminates all static Azure credentials — the CI/CD runner assumes an Azure AD identity via token exchange at runtime.
- 3 Grafana dashboards + 734 tests: 23 panels across ETL, containers, and pipeline health with 8 Prometheus alert rules. Custom data freshness probe exposes 4 gauges (Bronze/Silver/SQL row counts + composite status). Python test suite (613 pytest: 468 unit + 92 Terraform + 23 DAG integrity + 18 integration + 12 dbt-compile) plus 121 dbt data tests (46 not_null + 24 expression + 21 accepted + 16 unique + 10 relationships + 4 singular).
Python FastAPI TypeScript React Native Expo Rust PyO3 PyTorch LangGraph AWS Bedrock PostgreSQL Redis Apache Airflow MLflow Optuna Evidently AI Terraform Docker GitHub Actions AWS SageMaker Jest Pytest
Production-grade FinTech platform - Scan receipts via a confidence-gated OCR cascade (Tesseract → AWS Bedrock Vision LLM fallback), trade real stocks with the extracted amounts, track portfolios with cash-flow-aware time-weighted returns, get 5-day LSTM directional forecasts, benchmark against SPY (tracking error + information ratio), and query holdings via a 16-tool LangGraph ReAct agent streaming over SSE — all deployed on AWS ECS Fargate ARM64 via Terraform IaC with OIDC CI/CD.
- Multi-service backend: FastAPI (Python 3.13, asyncpg, SQLAlchemy 2.0, Pydantic v2, structlog) serving 59 REST endpoints across 14 routers. Separate LangGraph agent service (2-node
StateGraph, 16 tools, 7 categories,ChatBedrockConversevia Nova Lite) with two-tier Redis (7-day TTL) + PostgreSQL conversation persistence. 1,415 pytest functions across 76 test files, 90% line coverage gate. - Rust acceleration layer: PyO3/Maturin native extension replacing pandas for 17 technical indicators (log returns, SMA, RSI, MACD, rolling vol, Bollinger %B, ATR, OBV, Williams %R, ROC, cross-sectional excess returns vs SPY). 13 source modules, 12 exported functions, O(n) zero-Python-overhead batch computation. Built for ARM64 in CI via
maturin build --release. - Global LSTM forecasting: PyTorch model with 16-dim entity embeddings across 475+ S&P 500 tickers, 2-layer LSTM (hidden=80, dropout=0.535), Focal Loss (γ=1.49), Optuna 50-trial HPO. 51.63% directional accuracy (vs 33% random baseline), simulated Sharpe 0.97. Chronological 70/15/15 splits, 6-year OHLCV lookback.
- NLP cascade OCR pipeline: 4-stage escalation — Tesseract regex (base) → Bedrock Vision LLM (Nova Lite) → Text-only LLM → Degraded fallback. Confidence gating (overall ≥0.7, OCR ≥0.6, merchant verified via rapidfuzz ≥80). Discrepancy detection with float tolerance + string normalization. Redis 24h cache avoids redundant Bedrock calls.
- Portfolio analytics engine: Time-weighted return with explicit cash-flow handling (dedicated
cash_flowstable, not generic ledger). Hybrid cache: PostgreSQL for bulk OHLCV (indexed range queries), Redis for quotes (5-min TTL) and history (60-min TTL). Benchmark comparison yields Tracking Error and Information Ratio vs SPY. yfinance wrapped inThreadPoolExecutor(8 workers) +tenacityretries. - MLOps retraining pipeline: Apache Airflow 2.11 on ECS Fargate ARM64, weekly cron (Mon 06:00 UTC). Feature computation via Rust engine → Optuna HPO → MLflow tracking → Champion/Challenger evaluation (DA improvement >2pp promotes). Evidently AI drift detection (PSI>0.25, KS>0.3, JSD>0.3) on features + predictions. Champion model served via EFS mount (zero-copy) + S3 +
model_registrytable. Optional SageMaker endpoint (ml.m5.xlarge). - Frontend: React Native + Expo 54 + TypeScript 5.9, file-based routing (Expo Router), Zustand state, biometric auth, dark mode, auto-lock on background. 85 test files, 822 Jest assertions (branches≥75%, functions≥80%, lines≥90%).
- Infrastructure: Terraform ≥1.9 (14 modules, 193 resources). VPC (2 AZs, public/private), ALB + WAF (rate-based + OWASP), ECS Fargate ARM64/Graviton, Multi-AZ RDS PostgreSQL 18, ElastiCache Redis 8.8 (TLS + AUTH), EFS for champion model, CloudWatch alarms. 3-tier security groups (Internet→ALB→ECS→RDS/Redis).
- CI/CD: GitHub Actions OIDC (no static AWS creds). 9 parallel CI jobs (Lint/TS/Tests/Security/Rust/Backend/Docker/IaC/Secrets) — path-aware, each runs only on relevant changes. 7-stage CD: Build (Rust wheel ARM64, 4 Docker images) → Trivy critical scan → Terraform plan → Manual approval → Apply → ECS rolling update with health-check gating. CodeQL weekly, Dependabot weekly.
AI Engineering
Data Engineering
Software Engineering
Cloud & DevOps
Testing & Quality



