Architecture
This site is a full-stack web application built from scratch — no CMS, no framework boilerplate. Every page is server-rendered by Flask using Jinja2 templates, with a MySQL database driving the resume content. That means the resume page is genuinely data-driven: add a bullet to the database and it shows up on the page immediately, correctly grouped and filtered.
Deploying on a Linux VPS with nginx as a reverse proxy means the static files are served efficiently without hitting Python at all. gunicorn manages the worker processes, keeping the app responsive under load.
Tech Stack
Flask
Python micro-framework for routing, view logic, and Jinja2 template rendering. Lightweight and explicit — nothing hidden.
SQLAlchemy
ORM for database queries. Models defined in Python, queries composed with the fluent API — no raw SQL in views.
MySQL
Relational database storing companies, positions, and resume bullets with JSON columns for tools, skills, and impact data.
Jinja2
Server-side templating with template inheritance. base.html defines layout; child templates fill named blocks.
Vanilla JS
No frameworks. The resume filter, carousel, and skill icon swap are all plain ES6 — fast, dependency-free, easy to audit.
CSS Custom Properties
Design system built on :root variables — color palette, spacing scale, type scale — making visual changes one-line operations.
nginx + gunicorn
Production deployment: nginx handles TLS and static files; gunicorn manages Python worker processes.
python-dotenv
Secrets and environment-specific config live in .env, never in source code. Credentials are injected at runtime.
Design Decisions
- Server-side rendering over a SPA: Flask + Jinja2 means every page is a fast, indexable HTML document. No JavaScript required to see any content.
- Data-driven resume: Resume bullets are rows in a database, not hardcoded HTML. This enables filtering by skill category, future versioning, and easy updates without touching templates.
- JSON columns for flexible metadata: Skills, tools, and impact data are stored as JSON — structured enough to query, flexible enough to evolve without schema migrations.
- No build step: CSS and JS are written directly — no webpack, no transpilation. Keeps the feedback loop fast and the toolchain simple.
- CSS Custom Properties as a design system: All colors, spacing, and typography are defined as variables at
:root. Theming a page means changing one variable, not hunting through hundreds of rules.