Case study: Rebuilding my portfolio in 14 days with AI
- Case study
- Flask
- Google Cloud
- AI-assisted
- Claude Code
The short version is the case study.
Summary
In 14 days (25 Oct to 7 Nov 2026) I turned a portfolio I hand-coded in 2020 into a modern, secure and tested site, working with an AI coding agent. The old site at ilikedatascience.com could no longer be deployed: Google App Engine deprecated its python39 runtime on 1 Oct 2026.
- Approach: I had the AI analyse the code first, then write 16 plans, then implement them one at a time. Every change had to pass a written check before it counted as done.
- Results: 20 commits and about 34,600 lines added, plus a runtime upgrade from Flask 1.1 to Flask 3 on Python 3.12. I added passwordless sign-in, 12 wellness games, a much-improved Family Tree, a Posts editor and an admin page.
- Quality: 283 pytest tests, 49 Vitest tests and two end-to-end suites (47 and 28 checks). The headline demo went from timing out to answering in 0.36 s.
Where it started: the 2020 site
I built the original site entirely by hand in 2020 to showcase data mining and BI work. By 2026 it was live but stuck: I couldn't deploy a fix of any kind.
| Area | State in October 2026 |
|---|---|
| Runtime | Flask 1.1.2 on App Engine python39, which was deprecated, so no redeploys |
| Front end | Hand-written CSS, Bootstrap 4.0, jQuery 3.5.1, Chartist |
| Headline demo | The Apriori demo returned a 502 error after 31 s, and its rules were wrong |
| Security | A secret key in the code, a demo that asked visitors to paste cloud credentials, a reflected XSS bug on /name |
| Code health | Duplicate routes and templates (mainlayout - Copy.html), dead folders, an unrelated Django starter project, a 9.2 MB SQLite source folder |
| Family Tree | A React Flow demo with no source in the repo; builds were copied in by hand |
| Storage, sign-in, tests | None |
The journey, step by step
The rebuild followed one loop throughout: analyse, plan, implement, test, then write a report. Each plan item had the same layout: ID, problem, steps, done when, effort, and recruiter impact.
| Date (2026) | Step | What changed |
|---|---|---|
| 25 Oct | 1. Analyse | The AI reviewed the whole legacy project from several angles and wrote the findings to docs/01–09: architecture, security, bugs, dependencies, Family Tree, UX, roadmap, promotion |
| 25 Oct | 2. Plan | The findings became 10 implementation plans, ordered with "unblock deploys" first, plus a progress table and a revision log |
| 25 Oct | 3. Unblock and harden | Moved to Python 3.12 and Flask 3 with a new venv and git (plan 01). Secrets went to Secret Manager, and CSRF protection and security headers were added (plan 02) |
| 25 Oct | 4. Fix the headline demo | Apriori went from 4.7 s to 0.36 s with correct rules and validated input (plan 03). Broken routes and the XSS bug were fixed and dead code removed (plan 04) |
| 25 Oct | 5. Mind Games | Built 12 wellness games and a check-up. A code review then reproduced and fixed 8 defects, including timing races (plan 10) |
| 25 Oct | 6. Family Tree | Added parents and siblings, read-only sharing, arrangement, a list view, partial birth dates, a primary person, ancestor and descendant views, and a demo video (plans 06, 11–13) |
| 31 Oct | 7. Sign-in | Passwordless sign-in through Google, Microsoft and LinkedIn using OpenID Connect (plan 07) |
| 31 Oct | 8. User data and safety | One private folder per user with a sign-in record, an enforced CSP, a privacy page, export, delete, and photo upload (plans 14–15) |
| 31 Oct | 9. Portfolio face-lift | Responsive layout, accessible colours, a landing page, case studies, content kept in YAML, SEO (plan 05) |
| 1 Nov | 10. Posts | An owner-only Markdown editor with preview, uploads, drafts, and links to my LinkedIn, Medium and WordPress articles (plan 16) |
| 7 Nov | 11. Admin and polish | An admin page for site usage, signed-in users and saved data, a festive page theme, and versioned static URLs for cache busting |
Tech stack
The stack stays deliberately small: a Flask monolith on serverless hosting, using Google-managed storage and secrets.
| Layer | 2020 (manual) | 2026 (rebuilt) |
|---|---|---|
| Language and framework | Python 3.8/3.9, Flask 1.1.2 | Python 3.12, Flask 3.1 with app factory and blueprints |
| Forms and protection | Flask-WTF (partial) | Flask-WTF and CSRF, Flask-Limiter rate limits, enforced CSP and security headers |
| Sign-in | None | Authlib OpenID Connect: Google, Microsoft (Entra), LinkedIn |
| Data and ML | pandas, apyori | pandas 2.2, apyori (Apriori demo, cached and validated) |
| Storage | None (local SQLite experiments) | Google Cloud Storage (one folder per user), Firestore (game results) |
| Secrets | Hard-coded | Google Secret Manager |
| Templates and content | Jinja2, hand-edited HTML | Jinja2, content in YAML, Markdown posts via markdown-it-py (raw HTML off) |
| Front end | Bootstrap 4, jQuery, Chartist, hand-written CSS | Responsive custom CSS with accessible colours; React 18 and React Flow for the Family Tree |
| Testing and lint | None | pytest, Vitest, end-to-end browser tests, ruff |
| Hosting | App Engine Standard python39, gunicorn |
App Engine Standard python312, gunicorn, versioned static URLs |
| AI tooling | None | ChatGPT (early), Claude Code |
Architecture
The site is one Flask app built from an app factory, with a blueprint per feature area. It runs on App Engine Standard, so there are no servers to manage.

Every request passes through the same security layer. Signed-in users' data lives in one private folder per user (users/<hash>/) in Cloud Storage. The Family Tree's React source is built separately and served as a static bundle, with versioned URLs so each deploy busts the cache.
Use of AI vs manual coding
Everything before 25 Oct 2026 was coded by hand. The 14-day rebuild was AI-assisted: some early ChatGPT use, then Claude Code for the analysis, the plans, the code, the tests and the reports. I kept the decisions, the reviews and everything that touches real accounts.
| Phase | Who did it | What that meant |
|---|---|---|
| 2020 to Oct 2026: original site | Me, by hand | Flask app, blueprints, templates, CSS, Apriori demo, BI and data mining pages, the first Family Tree |
| Early in the rebuild | Me, with ChatGPT | Initial help before I moved the work into Claude Code |
| Analysis and planning | Claude Code, directed and reviewed by me | Analysis docs from several angles, 16 plans in one format, a progress table and a revision log |
| Implementation | Claude Code, reviewed by me | Code, tests and an implementation report per plan, one plan item at a time |
| Cloud and accounts | Me, by hand | OAuth app registrations (Google, Microsoft, LinkedIn), Secret Manager, IAM roles, bucket settings, gcloud app deploy |
| Product decisions | Me | What to build next, what to drop (for example the Azure demo once that subscription ended), what goes live |
How I worked with the AI
- Analyse before changing anything. The first prompt asked only for findings from several angles, saved to
docs/. No code was touched. - Plan in a fixed format. Each item states the problem, steps, a "done when" check, effort and recruiter impact, so I could review it before any code was written.
- One plan at a time, with a report. Each plan ends with a report and test counts, and the plan index records progress and every revision.
- Reproduce bugs before fixing them. For the games, the AI reproduced 4 timing bugs in headless Edge before changing any code.
- Check against the real site. Sign-in and other fixes were confirmed on the live URL, not just in local tests.
What I learned
- Plans are the leverage. A reviewed plan with a "done when" line made the AI's output easy to accept or reject.
- AI output still needs checking. One early claim of a "10,000× performance bug" was replaced with the measured figure: 4.7 s down to 0.36 s. One reported finding no longer happened on pandas 2.2, so that item was rescoped.
- Keep the human steps explicit. Console steps, secrets and deploys are listed as manual in every plan, so nothing falls between the AI and me.
- Tests make speed safe. With 283 back-end tests and the front-end suites, large changes in a day were low risk.
Results
The site can be deployed again, it is secure by default, and it does several times what it did before.
| Measure | Before (Oct 2026) | After (7 Nov 2026) |
|---|---|---|
| Deployable | No (deprecated runtime) | Yes (python312) |
| Apriori demo | 502 error after 31 s | 0.36 s, correct rules |
| Automated tests | 0 | 283 pytest, 49 Vitest, end-to-end 47 + 28 |
| Known security issues | Hard-coded secret, XSS, credential-paste demo | Fixed; CSP, CSRF, rate limits, Secret Manager |
| Sign-in | None | Google, Microsoft, LinkedIn |
| Features | Static pages, 1 demo, Family Tree | Plus 12 games, Family Tree sharing and views, Posts, Admin, user data export and delete |
| Change volume | — | 20 commits, about 34,600 lines added, 1,700 removed |
What's next
The remaining work is mostly operational, not code. It covers: pushing the repo to GitHub with CI, adding monitoring, Facebook and GitHub sign-in, upgrading Bootstrap, and finishing the remaining console steps before switching on the enforced CSP in production.