Skip to content

Commit 4bdbb0b

Browse files
committed
feat(infra): optimize Docker for cold-starts & add design rationale
• Refactored Dockerfile into 3 stages (deps, builder, runner) to maximize layer caching and minimize cold-start latency. • Integrated 'Engineering Decision Audit' modal for HLD/LLD pages. • Added architectural FAQs covering multi-cloud strategy, error logging, and the Workerd portability plan.
1 parent 3cad562 commit 4bdbb0b

5 files changed

Lines changed: 175 additions & 12 deletions

File tree

Dockerfile

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1-
FROM node:20-slim
2-
1+
# Stage 1: Install dependencies
2+
FROM node:20-slim AS deps
33
WORKDIR /app
4-
5-
ENV PORT=8080
6-
ENV NEXT_TELEMETRY_DISABLED=1
7-
84
COPY package.json package-lock.json* ./
9-
RUN npm install
5+
RUN npm ci
106

7+
# Stage 2: Build the app
8+
FROM node:20-slim AS builder
9+
WORKDIR /app
10+
COPY --from=deps /app/node_modules ./node_modules
1111
COPY . .
12-
12+
ENV NEXT_TELEMETRY_DISABLED 1
1313
RUN npm run build
1414

15+
# Stage 3: Runner
16+
FROM node:20-slim AS runner
17+
WORKDIR /app
18+
19+
ENV NODE_ENV production
20+
ENV NEXT_TELEMETRY_DISABLED 1
21+
ENV PORT 8080
22+
23+
# Standalone mode only needs these files
24+
COPY --from=builder /app/public ./public
25+
COPY --from=builder /app/.next/standalone ./
26+
COPY --from=builder /app/.next/static ./.next/static
1527

1628
EXPOSE 8080
1729

18-
CMD ["npm", "run", "start"]
30+
31+
CMD ["node", "server.js"]

next.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4-
/* config options here */
4+
output:'standalone'
55
};
66

77
export default nextConfig;

src/app/versioncv/page.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
import React from 'react';
44
import { motion } from 'framer-motion';
5-
import { Layers, Activity, ShieldCheck, Zap, Database, ArrowRight } from 'lucide-react';
5+
import { Layers, Activity, ShieldCheck, Zap, Database, ArrowRight, Terminal } from 'lucide-react';
66
import HomeButton from '@/components/HomeButton';
7+
import ArchitectureFAQ from '@/components/ArchitectureFAQ';
78

89
/* ======================================================
910
ARCHITECTURE DATA
@@ -104,6 +105,7 @@ const HLD: Layer[] = [
104105
====================================================== */
105106

106107
export default function VersionCVPage() {
108+
const [isFAQOpen, setFAQOpen] = React.useState(false);
107109
return (
108110
<main className="relative min-h-screen bg-[#05080c] text-slate-200 overflow-x-hidden font-sans">
109111

@@ -156,6 +158,18 @@ export default function VersionCVPage() {
156158
View Interactive Pipelines
157159
<ArrowRight size={18} />
158160
</motion.a>
161+
<motion.button
162+
onClick={() =>setFAQOpen(true)}
163+
whileHover={{ scale: 1.02 }}
164+
whileTap={{ scale: 0.98 }}
165+
className="cursor-pointer inline-flex items-center gap-3 px-6 py-3 rounded-xl
166+
bg-blue-500/10 border border-blue-500/20 hover:border-blue-500/50
167+
text-blue-400 hover:text-blue-300 font-mono text-xs font-black
168+
uppercase tracking-widest transition-all backdrop-blur-md group"
169+
>
170+
<Terminal size={16} className="group-hover:rotate-12 transition-transform" />
171+
See Engineering Decisions // FAQ
172+
</motion.button>
159173
<span className="text-slate-500 text-sm font-mono tracking-tighter">
160174
(HLD overview below)
161175
</span>
@@ -237,6 +251,12 @@ export default function VersionCVPage() {
237251
))}
238252
</div>
239253
</div>
254+
<ArchitectureFAQ
255+
isOpen={isFAQOpen}
256+
onClose={() => setFAQOpen(false)}
257+
/>
258+
259+
240260

241261
{/* Footer Branding */}
242262
<footer className="py-20 text-center border-t border-white/5 mt-20">

src/app/versioncv/pipelines/page.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import {
1616
X, Cpu, Database, Globe, Shield, Lock, Zap, Server,
1717
Code, Activity, Search, FileEdit, MessageSquare,
1818
ListChecks,
19-
BarChart3, Home
19+
BarChart3, Home,
20+
Terminal
2021
} from 'lucide-react';
2122
import HomeButton from '@/components/HomeButton';
23+
import EngineeringAuditModal from '@/components/ArchitectureFAQ';
2224

2325
/* ======================================================
2426
1. MASTER DATA DEFINITIONS (NON-TRUNCATED)
@@ -430,6 +432,7 @@ function TestMetricsView() {
430432
const [selectedTest, setSelectedTest] = useState<typeof TEST_PROOFS[0] | null>(
431433
TEST_PROOFS[0]
432434
);
435+
433436
const [zoomImg, setZoomImg] = useState<string | null>(null);
434437
const isMobile =
435438
typeof window !== 'undefined' && window.innerWidth < 768;
@@ -600,6 +603,7 @@ function TestMetricsView() {
600603

601604
export default function PipelineVisualizer() {
602605
const [selectedNode, setSelectedNode] = useState<any>(null);
606+
const [isFAQOpen, setFAQOpen] = React.useState(false);
603607
const [activeTab, setActiveTab] = useState('ats-scan');
604608

605609
const tabs = [
@@ -639,11 +643,25 @@ export default function PipelineVisualizer() {
639643
: 'bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 hover:bg-emerald-500/20'
640644
}`}
641645
>
646+
642647
<BarChart3 size={16} />
643648
View Test Metrics & Latencies
644649
</button>
645650
</div>
651+
<motion.button
652+
onClick={() =>setFAQOpen(true)}
653+
whileHover={{ scale: 1.02 }}
654+
whileTap={{ scale: 0.98 }}
655+
className="cursor-pointer inline-flex items-center gap-1 px-4 py-1.5 rounded-xl
656+
bg-blue-500/10 border border-blue-500/20 hover:border-blue-500/50
657+
text-blue-400 hover:text-blue-300 font-mono text-xs font-black
658+
uppercase tracking-widest transition-all backdrop-blur-md group"
659+
>
660+
<Terminal size={16} className="group-hover:rotate-12 transition-transform" />
661+
See Engineering Decisions // FAQ
662+
</motion.button>
646663
<HomeButton/>
664+
647665
<div className="text-center md:text-right">
648666
<h1 className="text-white text-lg md:text-xl font-black tracking-tighter italic">VERSION-CV ARCHITECTURE</h1>
649667
<p className="text-blue-500 font-mono text-[8px] md:text-[9px] tracking-[0.4em] uppercase font-bold">Serverless Orchestration</p>
@@ -775,6 +793,8 @@ export default function PipelineVisualizer() {
775793
Storage/DB: D1 / KV / R2 bucket
776794
</div>
777795
</footer>
796+
{/* FAQ Modal */}
797+
< EngineeringAuditModal isOpen={isFAQOpen} onClose={() => setFAQOpen(false)} />
778798

779799
</div>
780800
);

src/components/ArchitectureFAQ.tsx

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use client';
2+
import { AnimatePresence, motion } from 'framer-motion';
3+
import { HelpCircle, Terminal, Zap, Cpu, Database, Activity, Server, Layers, X } from 'lucide-react';
4+
5+
/* ======================================================
6+
ARCHITECTURAL AUDIT / FAQ DATA
7+
====================================================== */
8+
const ARCHITECT_FAQS = [
9+
{
10+
q: "Why decompose logic into independent services instead of a single Monolith?",
11+
a: "To control the 'Blast Radius.' By isolating related logic into independent services, a failure in the ATS Parser won't bring down the Auth or Payment gateways. This 'Selective Maintenance' allows me to update the Scoring Engine without any downtime for the rest of the system, keeping everything organized and easy to manage.",
12+
icon: <Cpu size={14} className="text-blue-400" />
13+
},
14+
{
15+
q: "Why Cloudflare Workers over AWS Lambda or GCP Cloud Run for core logic?",
16+
a: "In my initial AWS/GCP prototypes, cold starts were killing the UX. Cloudflare uses V8 Isolates, providing <5ms cold starts. Additionally, the restricted NPM support forced me to write 'Pure TS' with minimal dependencies, resulting in a lean, hardened codebase that isn't bloated by external package vulnerabilities.",
17+
icon: <Zap size={14} className="text-yellow-400" />
18+
},
19+
{
20+
q: "What was the motivation for a Multi-Cloud strategy?",
21+
a: "Operating System constraints. While Cloudflare handles the 'Speed-Layer,' heavy tasks like PDF rendering with Puppeteer require a full OS environment and state persistence. I offloaded these to GCP Cloud Run, creating a hybrid mesh that leverages Edge speed for logic and Containerized power for heavy lifting without compromising quality or security.",
22+
icon: <Activity size={14} className="text-purple-400" />
23+
},
24+
{
25+
q: "Why implement a Producer-Consumer pattern with SSE for long-running tasks?",
26+
a: "UX Decoupling. Resume optimization can take ~30s. If run synchronously, the CPU spikes and the connection times out. I send the request to a queue and open an SSE connection to track progress. This allows for async batch processing, keeping the frontend snappy and the backend efficient under high request volume.",
27+
icon: <Terminal size={14} className="text-orange-400" />
28+
},
29+
{
30+
q: "Why choose a Relational DB combined with KV Caching?",
31+
a: "Relational integrity with Edge performance. We need JOINs for complex user data, but DB latencies can be 10x slower than the Edge. I use Cloudflare KV as a cache with ~9ms latency to store hot metadata, ensuring the UI feels 'instant' while maintaining an organized, normalized data structure in D1/PostgreSQL.",
32+
icon: <Database size={14} className="text-green-400" />
33+
},
34+
{
35+
q: "What is the 'Hard Exit' strategy if Cloudflare ceases to be viable?",
36+
a: "The architecture is strictly built against the 'workerd' open-source runtime—the same V8 Isolate engine that powers Cloudflare under the hood. This ensures 100% environment parity. If we need to migrate, we can deploy the same code into a workerd-based container mesh on any VPS (AWS/GCP/DigitalOcean). The data layer follows: Metadata migrates from D1 to Neon PostgreSQL (or self-hosted MinIO for storage). Zero code rewrites, just a runtime shift.",
37+
icon: <Server size={14} className="text-red-500" />
38+
},
39+
{
40+
q: "How do you handle debugging when something breaks in this distributed setup?",
41+
a: "I built a custom error-catching mechanism into every service. Each time an error occurs, it calls a central 'Error Logger' function that writes directly to a single database table. This table stores the error message, the specific route, the service name, and the exact function where it failed. Instead of wasting hours or days hunting through different cloud logs, I just check this one central DB table. It tells me exactly where to go to fix the problem in minutes.",
42+
icon: <Activity size={14} className="text-red-400" />
43+
},
44+
{
45+
q: "Is Serverless always better than a traditional Monolith or Pod-based setup?",
46+
a: "Not at all. Heavy, CPU-intensive workloads that require constant uptime are often cheaper and more stable on dedicated instances. My approach is to analyze a Monolith top-to-bottom: I evaluate each component's cost, risk, and resource utilization. If a service has spiky load or remains idle >50% of the time, I decouple it to Serverless to save costs. If it’s a mission-critical, high-utilization service, I keep it on dedicated infrastructure. It’s about matching the deployment model to the actual workload requirements.",
47+
icon: <Layers size={14} className="text-slate-400" />
48+
}
49+
];
50+
51+
52+
export default function EngineeringAuditModal({ isOpen, onClose }: { isOpen: boolean, onClose: () => void }) {
53+
return (
54+
<AnimatePresence>
55+
{isOpen && (
56+
<div className="fixed inset-0 z-[100] flex items-center justify-center p-6 md:p-10">
57+
{/* Backdrop */}
58+
<motion.div
59+
initial={{ opacity: 0 }}
60+
animate={{ opacity: 1 }}
61+
exit={{ opacity: 0 }}
62+
onClick={onClose}
63+
className="absolute inset-0 bg-[#05080c]/90 backdrop-blur-md"
64+
/>
65+
66+
{/* Modal Card */}
67+
<motion.div
68+
initial={{ opacity: 0, scale: 0.95, y: 20 }}
69+
animate={{ opacity: 1, scale: 1, y: 0 }}
70+
exit={{ opacity: 0, scale: 0.95, y: 20 }}
71+
className="relative w-full max-w-4xl max-h-[85vh] bg-[#0a0f18] border border-white/10 rounded-[2.5rem] shadow-2xl overflow-hidden flex flex-col"
72+
>
73+
{/* Header */}
74+
<div className="p-8 border-b border-white/5 flex items-center justify-between bg-white/[0.02]">
75+
<div className="flex items-center gap-4">
76+
<div className="p-3 bg-blue-500/10 rounded-2xl border border-blue-500/20">
77+
<Terminal size={20} className="text-blue-400" />
78+
</div>
79+
<div>
80+
<h2 className="text-xl font-black text-white italic uppercase tracking-tighter">Engineering Decision Audit</h2>
81+
<p className="text-[10px] font-mono text-slate-500 uppercase tracking-widest">versionCV</p>
82+
</div>
83+
</div>
84+
<button onClick={onClose} className="cursor-pointer p-3 hover:bg-white/5 rounded-full transition-colors text-slate-500 hover:text-white">
85+
<X size={24} />
86+
</button>
87+
</div>
88+
89+
{/* Content (Scrollable) */}
90+
<div className="flex-1 overflow-y-auto p-8 md:p-12 space-y-12">
91+
{ARCHITECT_FAQS.map((faq, index) => (
92+
<div key={index} className="group relative pl-8 border-l border-slate-800 hover:border-blue-500/30 transition-colors">
93+
<div className="absolute -left-1 top-1 h-2 w-2 rounded-full bg-slate-800 group-hover:bg-blue-500 transition-colors" />
94+
<h3 className="text-lg font-bold text-white mb-3 tracking-tight flex items-center gap-3">
95+
<span className="text-blue-500 font-mono text-sm">Q:</span> {faq.q}
96+
</h3>
97+
<p className="text-slate-400 text-sm leading-relaxed italic font-medium">
98+
<span className="text-slate-600 font-mono text-sm mr-2">A:</span> {faq.a}
99+
</p>
100+
</div>
101+
))}
102+
</div>
103+
104+
105+
</motion.div>
106+
</div>
107+
)}
108+
</AnimatePresence>
109+
);
110+
}

0 commit comments

Comments
 (0)