Ahmed Abdelaziz

PortfolioDesign Docs

Architecture Decisions

Six key choices and why they were made.

Last updated August 2026

Overview

Five decisions that shaped this site. Each has context, choice, why, and trade-off — no jargon wall.


ADR-001: The Filesystem Is the CMS

Status: Accepted

Context: Projects need titles, tags, banners, links, and long-form bodies. A headless CMS (Contentful, Sanity) or a database could store all of it.

Decision: Content lives as markdown/MDX files in the repository — projects/ for entries, content/design-docs/ for deep dives — discovered by glob at build time.

Why: Zero infrastructure, zero cost, version history and rollback come free with git, and authoring is just writing files. For a single-author site, an editorial UI solves a problem that doesn't exist.

Alternatives: Headless CMS (extra service, auth, network dependency), database + admin UI (overkill).

Trade-off: No WYSIWYG editing and non-developers can't contribute. Accepted — the audience for contributions is one person.


ADR-002: Server-Side MDX with One Component Map

Status: Accepted

Context: Project pages and design docs need rich rendering — diagrams, callouts, tables — without shipping a Markdown engine to the browser or compiling MDX per-request.

Decision: next-mdx-remote in RSC mode compiles content on the server; every file renders through a single shared component map (src/lib/mdx.tsx) extended by custom remark/rehype plugins (callouts, asset rewriting, heading slugs).

Why: One place defines what Markdown means on this site. New syntax support (like callouts) is a plugin, not a migration. Output is static HTML.

Alternatives: .mdx pages in the app directory (couples content to routing), client-side rendering (ships the compiler to visitors).

Trade-off: JS expressions inside MDX are permitted (blockJS: false) — fine for self-authored content, but content isn't sandboxed.


ADR-003: Lazy Client-Side Mermaid

Status: Accepted

Context: Architecture docs want real diagrams. Mermaid's bundle is far too heavy for the initial page payload of a mostly-static site.

Decision: Diagrams are written inline in MDX; SSR renders a placeholder; the library is dynamically imported only after hydration; renders serialize through a queue; theme changes re-render without reload; a full-screen viewer adds pan/zoom.

Why: Diagram-heavy docs cost almost nothing in page weight; the interactive viewer turns static images into explorable artifacts.

Alternatives: Pre-rendering diagrams to SVG at build time (loses theming and zoom interactivity), images authored externally (drifts from the text).

Trade-off: Diagrams don't exist in server HTML — invisible to crawlers and to no-JS visitors.


ADR-004: Node Runtime for Project Assets

Status: Accepted

Context: Project screenshots live next to their markdown inside projects/ — not in public/ — so they can be committed per-project and referenced relatively.

Decision: Serve them through a dynamic route (/project-assets/[...path]) that reads the filesystem with a path-traversal guard and immutable cache headers.

Why: Co-location keeps each project self-contained (drop the folder anywhere else and it still works); the guard keeps the route from reading outside projects/.

Alternatives: Copying everything into public/ at build time (loses relative references in raw MDX), a pure static export (can't run the handler).

Trade-off: The deployment needs a Node runtime — fine on Vercel, but rules out output: "export" hosting like GitHub Pages.


ADR-005: Motion as a Design Language, Reduced Motion as a Rule

Status: Accepted

Context: Portfolio sites compete on feel. But animation is exactly what makes some users ill.

Decision: Every animated element goes through Motion primitives (Reveal, Stagger, hover lifts) under a global MotionConfig reducedMotion="user"; the theme toggle uses the View Transitions API where available.

Why: One motion vocabulary across cards, prose, and diagrams; accessibility isn't a per-component afterthought.

Alternatives: CSS-only animations (no orchestration), no motion (the site reads flat).

Trade-off: Client components carry the animation layer; hydration matters more. Contained by keeping data fetching server-side.

ADR-006: External API Documentation as a Frontmatter URL

Status: Accepted

Context: Some projects (notably the ecommerce backend) have a full API surface described outside the portfolio — currently Mintlify at https://codebyahmed.mintlify.site/introduction (previously the openapi.yaml on GitHub). Embedding the spec would mean shipping a Swagger/Redoc bundle or duplicating docs inside MDX.

Decision: Store a single optional field apiDocsUrl (alias apiDocs) on Project frontmatter, validate it as http(s):// at build time (src/lib/projects.ts), and render a conditional external link — API Documentation with FileCode + ExternalLink — on the project header and the design-docs index header. No new route, no bundle cost.

Why: One link, zero duplication — the external site remains the source of truth, the portfolio stays static and never proxies the spec. Validation fails the build loudly on typos, and absence hides the button entirely (no placeholder).

Alternatives: Internal /api-docs route rendering swagger-ui-react/Redoc (heavy bundle, sync burden), extending meta.yaml per-doc (too granular — the spec is project-level), separate Postman/Apidog fields (deferred — single URL covers the need).

Trade-off: Discoverability depends on the author remembering the frontmatter; a broken external URL still builds if syntactically valid — runtime 404 is outside the build's reach.

Common thread

Static first, server-rendered always, client JavaScript only where interactivity earns its payload.