Building This Website: A Journey of Learning and Doing

I have this habit of wanting to build things myself—sometimes in ways that are far from efficient, and occasionally downright hair-pulling. But I've found there's real satisfaction in discovering how each cog in the machine works, even if it means reinventing the wheel a few times before you find the shape that rolls best. It would've been simpler to pick a turnkey solution like WordPress or Squarespace for this portfolio site, but where's the fun in that? Instead, I threw myself into modern web development, keen to see how AI could speed up my learning curve and (hopefully) preserve my sanity.

"The best way to learn something is to build it yourself—even if you have to rebuild it three times to get it right."

The Tech Stack: Old Meets New

I chose Flask—a Python web framework—partly because I'm comfortable with Python from my data-centric work, and partly because it's lean enough to reveal its inner workings without drowning me in configuration files. Here's what powers this site:

Core Technologies:
- Backend: Python 3.11 + Flask
- Templating: Jinja2
- Content: Markdown with YAML frontmatter
- Styling: Custom CSS with Flexbox/Grid
- Deployment: Docker + GitHub Actions
- Special Sauce: R Markdown converter for academic content

Here's how these pieces fit together:

┌────────────────────┐     ┌───────────────────────┐
│    routes.py       │     │ services/content.py   │
│  (Flask endpoints) │ <--→│  (blog + projects)    │
└────────────────────┘     └───────────────────────┘
            │                      ↑
            │                      │
┌────────────────────┐            │
│   templates/       │ <─ Jinja2 ─┘
│ (HTML+Markdown)    │
└────────────────────┘

This was only the beginning, though. I had big ideas about combining Jinja2 for dynamic templates, Markdown for simpler content creation, and some good old-fashioned vanilla JavaScript for the front-end sprinkles.

At first, I patted myself on the back for setting up a minimal structure: routes in Flask, templates in Jinja2, and content in plain Markdown. It worked swimmingly—for a week—until I decided it was time for more advanced features. That's when each "victory" became a step towards the next problem to solve. I quickly found myself unpicking thread after thread: a snippet of vanilla JavaScript to handle interactive elements here, a Docker container for consistent deployments there, and eventually a pipeline using GitHub Actions to automate testing and deployment. Each addition felt like a minor triumph, but some turned into stumbling blocks as soon as I discovered a more elegant or maintainable way to do the same thing. Nonetheless, with each misstep I learned something new, and in a twisted kind of way, that was the whole point.

VS Code showing the Flask project structure
Project structure in VS Code showing the Flask/Jinja2 setup


Development Workflow: From Local to Live

One of the most satisfying aspects of this setup is its straightforward development workflow. A fresh development environment spins up with just a few commands:

┌── Terminal ─────────────────────────────────────────┐
│ $ git clone git@github.com:kipjordan/website.git    │
│ $ python -m venv venv                               │
│ $ source venv/bin/activate                          │
│ (venv) $ pip install -r requirements.txt            │
│ (venv) $ flask run --debug                          │
└───────────────────────────────────────────────────┘

When changes are ready for production, a simple git push triggers the deployment pipeline:

[Local Push] → [GitHub] → [Actions Pipeline] → [Build] → [Deploy]
     └→ Runs tests
     └→ Builds Docker image
     └→ Updates production server

This automated workflow ensures that every change is tested and deployed consistently, eliminating the "it works on my machine" syndrome.

AI-Augmented Workflows

One of the biggest game-changers in this journey was integrating AI tools into my workflow. I didn't just tell the AI, "Build me a website," and hope for the best. It was more like inviting a particularly clever friend to watch over my shoulder, pointing out potential pitfalls or generating snippets of code that I could customise and learn from. When I struggled with flexbox layouts for a responsive image gallery, the AI suggested a basic structure that I then massaged into shape—and I gained a solid grounding in responsive design principles in the process.

Later, I revisited the gallery code after reading up on better lazy-loading techniques. My first solution worked… until I realised how sluggish it felt on mobile devices. Thanks to the AI's ability to highlight alternative approaches, I refactored the code into something sleeker and more user-friendly. Each success was swiftly replaced by a new realisation that I could do better, and that back-and-forth might have been exhausting if it weren't so utterly fascinating.

“AI isn't replacing developers—it's augmenting our capabilities and accelerating our learning curves.”

Content Pipeline: From R to Web

One of the most intriguing sections of the site is where I transform R Markdown outputs into web-friendly content. Here's how the content flows through the system:

R Markdown (.Rmd)
      ↓
[rmd_to_clean_html.py]
      ↓
Cleaned HTML + Assets
      ↓
[converter.py]
      ↓
Markdown + Frontmatter
      ↓
Flask ContentService
      ↓
Rendered Web Page

This bridging of data science and front-end design tested my patience in the best possible way. I spent nights wrangling base64-encoded images, ensuring tables looked presentable, and preserving the accuracy of statistical outputs. Each success was short-lived; as soon as I got one graph displaying perfectly, I discovered a new technique for compressing or styling it better.

Security and Performance Considerations

Security wasn't an afterthought—it was baked in from the start. I implemented a strict Content Security Policy (CSP) early, which turned out to be something of a puzzle each time I introduced third-party resources. Did I grumble about it? Absolutely. But I knew that each wave of frustration was also a chance to dig deeper into how browsers protect users (and how they might fail if I didn't set things up correctly)).

I also took a progressive enhancement approach to ensure the site remains functional even if JavaScript takes a holiday. JavaScript handles smooth scrolling and animations, but if it's disabled—or if the user's device has limited resources—there's still a perfectly readable page behind those bells and whistles. It turns out that ensuring accessibility and performance is an ongoing challenge, but it forces me to think carefully about why I'm adding certain features, rather than just how.

Reflecting on the Journey and Looking Ahead

What started as a simple portfolio site became an evolving playground, a place where my successes were rarely permanent, but always a stepping stone to newer, better ideas. I intend to keep this momentum going by adding interactive data visualisations, implementing a dark mode for those late-night reading sessions, and diving deeper into performance optimisations.

Despite the frustrations, or maybe because of them, building this site has enriched my understanding of how web technologies fit together—and how AI can help me learn faster. If you're contemplating a similar do-it-yourself project, I encourage you to dive in, make mistakes, and own those victories (even if they're fleeting). Every step will reveal something you didn't know before, and that's what makes it worth doing.

I'd love to hear your thoughts or war stories from your own build-it-yourself experiences. Feel free to poke around the code or reach out with questions and suggestions. After all, in the words of Richard Feynman:

“What I cannot create, I do not understand.” - Richard Feynman

What's Next?

Future improvements I'm exploring:
- [ ] Dark mode implementation
- [ ] Enhanced image optimisation pipeline
- [ ] WebAssembly for complex visualisations
- [ ] Automated accessibility testing
- [ ] Service Worker for offline capability
- [ ] Structured data for better SEO
- [ ] Analytics dashboard for content performance

These aren't just items on a todo list—they're opportunities to dive deeper into modern web development practices and continue learning. Each new feature will likely lead to discoveries that improve the existing codebase.