Some mistakes are only visible in hindsight. When I first built the portfolio website for interior designer Noor Al-Huwaidi, I was focused on getting it done — and I got it done. The site was live, it looked beautiful, and the client, my wife, was happy. But buried inside the HTML was a decision I had not yet learned to question: every single project, every image, every modal, every line of descriptive text — all of it was written directly into the markup. Hardcoded. Static. Immovable.
It worked. Until it needed to change.
A few weeks after launch, Noor reached out about website maintenance. She wanted new projects added regularly, a journaling section for her thoughts, and the ability to keep the showroom fresh as her portfolio grew. I said I would think about it. I already knew what thinking about it would reveal.
Every new project meant opening the HTML, writing modal markup by hand, updating gallery arrays in JavaScript, and hoping nothing broke in the process.
Ten projects were already in the file. Each one carrying its own block of HTML — a modal, a gallery, a description with sections and subheadings and CAD drawings. The showroom page had grown into something that was technically functional but practically unmaintainable. Adding an eleventh project meant doing everything again, manually, identically, carefully. It was not a workflow. It was a ritual of repetition.
I sat with the codebase for a while before deciding on a direction. The core issue was separation of concerns — or rather, the complete absence of it. Content and structure were fused together. The data lived inside the presentation layer, which meant changing one required touching the other. This is the kind of architecture that works for a single developer building something once, and quietly falls apart the moment the project needs to breathe and grow.
The solution was clear: extract all project data into a structured projects.json file, and let JavaScript fetch it dynamically at runtime. The HTML would become a clean shell. The data would live separately, readable, editable, and independent of the markup around it.
Restructuring a live portfolio without breaking it required working in a dedicated branch — cms-compatibility-update — so that the production site remained untouched throughout the entire process. This was the first good decision of many.
With the data layer separated from the presentation layer, the natural next question was whether to introduce a Content Management System — a visual interface that would allow non-technical editing of the JSON without touching any code. After research, Decap CMS emerged as the most fitting candidate: open source, Git-based, requiring no server, and designed precisely for static sites hosted on GitHub Pages.
What followed was one of the more instructive detours of the project. The authentication layer between Decap CMS, GitHub OAuth, and Netlify's identity proxy proved to be a labyrinth of browser security restrictions, popup-blocking policies, mismatched site identifiers, and configuration subtleties that compounded on one another. Each solution uncovered the next layer of the problem.
The OAuth popup was blocked. The implicit flow redirected to the wrong URL. The Netlify site ID did not match. The config was cached. The JSON was a bare array when it needed to be a wrapped object.
Eventually, after migrating the hosting to Netlify, enabling Git Gateway, configuring Netlify Identity, and resolving a trailing comma on line 509 of the JSON file — the CMS worked. Fully. Projects loaded. Images uploaded. Commits appeared in the repository automatically.
And then, with everything functioning, the decision was made to walk away from it.
The CMS was powerful, but it was also fragile in a specific way — it introduced Netlify's build credit system, an external authentication dependency, and a hosting split that added complexity without adding enough value for a portfolio of this scale. The JSON-driven architecture was already achieving the primary goal: clean separation of data and structure, with a workflow that required editing only a single file to add or update any project.
The site was migrated back to GitHub Pages. The custom domain was reconnected through Namecheap's DNS records. The CMS files were removed. The branch was merged. The site went live, clean and fully dynamic, with no external dependencies beyond a JSON file and a few dozen lines of JavaScript.
The final architecture is quiet and honest. An index.html that always shows the latest four projects. A showroom.html that renders every project from a single source of truth. A projects.json that any developer can open, edit, and understand in under a minute. GitHub Pages serving it all, reliably, for free.
The original mistake was not building the site the wrong way. Every developer builds things the wrong way before they learn the right way. The original mistake would have been leaving it that way once the right way became visible. This project was the moment the right way became visible.
There is a trailing comma on line 509 of a JSON file that I will not forget for a long time. It was the last thing standing between a broken site and a working one, after weeks of architecture decisions, authentication battles, and infrastructure choices. A single comma. It is a good reminder that in web development, the largest problems and the smallest problems deserve exactly the same quality of attention.