Interview Prep · Caylent Sr. CSA · Private / Personal Use

Portfolio, Translated to AWS-Native Architecture

Every solution below was actually built and shipped — several on Azure or GCP because that's what the client or the experiment called for. For each one: what I built it with, the AWS-native equivalent I'd design instead, and the specific reason AWS wins that argument — not a generic "AWS is good" answer, but the actual service-level trade-off.
As Built (production)
AWS-Native Equivalent
The Real Story: Multi-Cloud by Design, AWS by Preference
The framing to lead with if asked "why isn't more of this on AWS already?" — before walking through any individual project.

Why Multi-Cloud in the First Place

Going multi-cloud wasn't an accident of client assignment alone — it was a deliberate test of the baseline my certifications actually gave me. I hold certs across all three major clouds (AWS DevOps Professional, AWS Solutions Architect Associate, AWS Cloud Practitioner, GCP Associate Cloud Engineer, Microsoft Azure Fundamentals). Building the same class of system — governance platforms, RAG pipelines, agentic app rationalization — on each cloud was how I proved those certifications translated into working production judgment, not just exam knowledge. That's exactly the portable architectural reasoning a multi-cloud consultancy's CSA needs, even one that's AWS-only today.

Why So Much Landed on Azure Specifically

Rackspace's Public Cloud Advisory practice — my day job for the last several years — has been heavily weighted toward Azure engagements. Leaning into Azure specificity for the EARE platform, the governance platform, Park Agent Chat, and the Content Pipeline was the pragmatic choice to match where the client deal flow and existing customer environments actually were. That's a client-driven reality, not a personal architecture preference — worth naming plainly rather than leaving it as an unexplained gap in an AWS-only interview.

Where My Actual Preference Shows Through

Two things in the portfolio are genuinely mine to choose, not dictated by a client environment — and both point the same direction:

3
AWS Certifications
DevOps Professional · SA Associate · Cloud Practitioner
1
GCP Certification
Associate Cloud Engineer
1
Azure Certification
Microsoft Azure Fundamentals — entry level

The certification journey: AWS is the only cloud where I pushed to the Professional tier — DevOps Professional is a genuinely hard exam, not a checkbox. The Azure side stopped at Fundamentals. That asymmetry reflects where I chose to invest, not just what a client needed certified.

The model layer: even on systems where the compute host is Azure, the AI/model layer very often still defaults to AWS Bedrock — Park Agent Chat and the Content Pipeline both call Bedrock (Claude Sonnet/Haiku) from Azure compute rather than Azure OpenAI, and the from-scratch builds (rarf-v4, EARE AWS, the Advisory AI Governance Platform) are Bedrock-native end to end. To be fair, it's not universal — Knowledge RAG and the Azure side of Digital Twin do use Azure OpenAI — but the pattern of reaching for Bedrock even when the surrounding infrastructure is Azure is real and repeats often enough to be a genuine signal, not a cherry-picked data point.

What This Means for Caylent, Concretely

If hired, I'm not walking in needing to learn Bedrock or figure out agentic AI patterns from scratch — I already have a working backlog of Bedrock-native, production-grade systems built specifically around the two things Caylent's GenAI and application-modernization practices sell every day:

That's the pitch: not "I'll ramp up on AWS," but "here are three production-grade, Bedrock-native accelerators I can adapt for a Caylent customer in week one."

Current Work — A Working Analog to Caylent Accelerate™
Not a shipped case study — an application-rationalization/rebuild platform I'm actively designing right now, the same month Caylent Accelerate™ launched. Included because it's the freshest evidence I have that I'm already building in exactly the direction Caylent's newest product line is heading, independently and in parallel.
What it is
An AWS-native platform pairing a portfolio-level 6R/7R rationalization engine with a LangGraph-orchestrated per-application rebuild pipeline (discovery → rationalize → spec → coding → testing → security → human review → deploy → audit). Bedrock-based model routing, AWS GovCloud as the sovereign deployment target, Terraform across VPC/EKS/RDS/S3/ECR/CloudFront/ALB.
  1. The graph design specifically: a LangGraph StateGraph with 9 nodes and two conditional edges — a bounded quality-gate retry loop (testing → coding if more than 5 tests fail, otherwise → security) and a human-in-the-loop gate (human_review → deploy if approved, otherwise the pipeline ends) — backed by a checkpointer for state persistence, with a documented production upgrade path from in-memory to PostgresSaver on Aurora Serverless on AWS. Every node emits structured events (node_start, edge_decision, state_update) that drive a live graph visualizer and double as an audit trail.
  2. Where the data elements line up with Caylent Accelerate: guardrailed autonomous action with a human approval/audit gate (my human_review node = Caylent's "guardrailed agents do the work; AWS architects approve and audit"); a bounded remediation/retry loop gated by a threshold rather than infinite retries (my route_after_testing conditional edge = Caylent's SLO-driven remediation framework); persistent state so the system doesn't repeat the same failure blind (my checkpointer + the per-project knowledge graph slated to feed a persistent store = Caylent's "agents retain memory of every completed task, eliminating repeat incidents"); and a dedicated audit stage recording exactly why each routing decision was made.
The honest framing: this is application-rebuild orchestration, not CloudOps incident remediation — a different domain (SDLC pipeline stages vs. infrastructure tickets) — and it's pre-production; nothing has run against live customer data yet. I wouldn't overstate the overlap as "I've built Caylent Accelerate already."
What this actually demonstrates: the underlying pattern — bounded guardrailed retries, a human approval checkpoint, persistent state instead of a stateless one-shot agent, and a structured audit trail — is the same shape Caylent formalized into a product, and I arrived at it independently while designing something in a different domain. That's a stronger signal than reciting Caylent's own architecture back to them: I can speak to Accelerate's design not as a marketing pitch I read, but as a pattern I'd have built the same way on my own.
Current Work — AR History App: Live Azure Demo, AWS-Native Target Already Spec'd
Historicalnator — a location-aware history app using generative AI narration and an AR overlay, handling near-real-time geospatial queries at walking pace. The prototype is live today on reused Azure infrastructure; the production architecture is already fully designed for AWS and is waiting only on a partner's AWS account being provisioned before the port starts.
As Built — Azure Prototype (live today)
  • Existing Static Web App (web UI) + existing Azure Functions app (Node.js proxy layer) — reused, not new
  • New Python Flex Consumption Azure Function for chat/narrative generation
  • Cosmos DB — new database in an existing account, holding prompt configs
  • Azure Database for PostgreSQL — new database on an existing server
  • Blob Storage — new containers on an existing account (photos, ingest staging)
  • Azure Container Apps Jobs — on-demand data ingestion, single geography, manually triggered
  • Model layer: AWS Bedrock (Claude Sonnet 4.5, us-east-1) — a live cross-cloud call, today
AWS-Native Target (already designed, not yet built)
  • React Native (mobile) / React PWA (web) — client-side, unchanged by the port
  • AWS Lambda + API Gateway — serverless API, scales to field-trip-sized event bursts
  • RDS PostgreSQL + PostGIS — geospatial engine for radius queries (ST_DWithin)
  • DynamoDB, geo-partitioned — high-read/low-write event warehouse for the real-time AR pin layer
  • S3 — historical photos, audio narration, AR assets
  • Cognito — auth (family profiles, classroom sessions)
  • Pinpoint — usage analytics
  • Bedrock — same model layer, now same-cloud as the compute calling it
Why this is the most concrete live example in the whole page
  1. Near-real-time, large-data handling, already reasoned out: the AR pin layer polls every 10 seconds or every 50 meters of movement (whichever comes first), with an adaptive radius — 200m standing still, 400m at an active walk — and a 9-tile pre-fetch grid around the user's current cell. That access pattern is exactly what DynamoDB's geo-partitioned design targets: sub-second, high-read lookups for "what's near me right now." The heavier spatial work — ST_DWithin radius scoring, dedup of overlapping events at the same coordinates, significance ranking — stays on RDS PostGIS. Two data stores, each matched to a genuinely different access pattern, not one database stretched to do both jobs.
  2. The Bedrock pattern, a third time: even the Azure-hosted prototype calls AWS Bedrock directly for every narrative generation — the same cross-cloud pattern already documented for Park Agent Chat and the Content Pipeline. The model layer choice has consistently been mine, independent of whichever cloud happened to host the surrounding infrastructure that quarter.
  3. An honest gap, not glossed over: the AR roadmap's Phase 2 (shared, persistent multi-user anchors for classroom/field-trip mode) calls for Azure Spatial Anchors — AWS has no direct managed equivalent service. The honest redesign path is Google's ARCore Cloud Anchors (cross-platform, backend-agnostic) or a custom anchor-persistence layer on DynamoDB/S3 — not a like-for-like AWS service swap. Worth naming plainly rather than implying AWS has a drop-in replacement it doesn't.
Why Azure for the prototype specifically: the same reason from "The Real Story" above — reusing already-provisioned AI Pathfinder infrastructure to avoid new recurring cost during the prompt-engineering and stakeholder-demo phase, not a cloud preference. The production architecture has been AWS-native in the design doc from day one; the only blocker to starting the actual port is a target AWS account being set up for the partner business this ships under.
Current Work — MemoryOS: Bedrock Guardrails Already Gate Production Data Today
MemoryOS — an enterprise knowledge platform that ingests Slack, Teams, Jira, Confluence, SharePoint, and email, and turns them into a searchable, cited operational knowledge base and auto-generated handover briefs. The one that lines up with Caylent most directly: it already uses Amazon Bedrock Guardrails — one of the four named pillars of Caylent Accelerate™ — as a production design decision, not a proof-of-concept.
What it is
A medallion-architecture pipeline (Bronze → Silver → Gold) that ingests enterprise chat/wiki/ticket data, deterministically prunes ~70%+ of volume with zero AI inference, then runs the survivors through two AWS Bedrock stages before anything is indexed or searchable.
  1. Stage 2 — guardrails gate what proceeds: every Silver-tier document is passed through a named Bedrock Guardrail (memoryos-noise-filter-v1) with four denied-topic categories — casual social coordination, sports/entertainment, HR/benefits admin, personal updates. If the guardrail intervenes, the document is archived and zero Claude tokens are spent on it — the guardrail is the cost control, not an afterthought bolted onto a working pipeline.
  2. Stage 3 — deterministic, tool-forced classification: documents that pass the guardrail go to Bedrock's Converse API (Claude 3.5 Haiku, upgraded to Sonnet for longer documents) with toolChoice forcing a structured output — actionability, a 1–5 confidence score, operational domain, and a 1–5 "operational continuity risk score" — at temperature: 0.0 so the same input always produces the same classification. This is the same guardrailed-automation-with-explainable-confidence pattern as Caylent Accelerate's SLO-driven remediation, applied to knowledge curation instead of infrastructure incidents.
  3. Serving layer: the chat endpoint grounds every answer in retrieved citations only, with an explicit "not found in the indexed knowledge base" fallback rather than letting the model guess — and the handover-brief endpoint generates a full Markdown operational handover document, citing every claim back to a source item ID.
The honest infrastructure picture: everything except the two Bedrock calls is Azure today — Container Apps, ADLS Gen2 (the Bronze/Silver/Gold tiers themselves), three separate Key Vaults, Azure OpenAI embeddings, Azure AI Language for PII detection, and Azure AI Search for the hybrid BM25+vector index. The design doc is explicit that PII masking must complete before any payload crosses to Bedrock, IAM is restricted to InvokeModel/InvokeModelWithResponseStream only, and client data is never stored in AWS — only inference-time text payloads cross the boundary. That's a deliberate, documented cross-cloud data-minimization design, not an oversight.
The AWS-native full-port, and why it's a real simplification, not just a swap: Container Apps → ECS Fargate, ADLS Gen2 → S3 (same tiered prefix structure), three Key Vaults → Secrets Manager, Azure AI Search → OpenSearch Serverless, Azure OpenAI embeddings → Bedrock Titan/Cohere embeddings, Azure AI Language → Amazon Comprehend PII detection, Table Storage → DynamoDB. Consolidating onto one cloud doesn't just move services — it deletes the entire cross-cloud data-minimization boundary (the Key-Vault-to-Bedrock credential injection, the "text-only, never storage" argument) because everything sits inside one account. One honest exception: the Teams/SharePoint connectors will always need Microsoft Graph API credentials regardless of hosting cloud — that's dictated by the source system being Microsoft 365, not a hosting decision, so it doesn't disappear in an AWS port.
Already Built AWS-Native — No Translation Needed
Enterprise COTS App Rationalization — The Engineering Discipline Behind the $24/4.9hr Number
The headline number is easy to quote; the discipline behind it is the better interview answer if asked to go one level deeper.
The 7-agent architecture, by name
Sequential per application, then one portfolio-level pass — with deterministic (non-LLM) steps bracketing the whole run.
  1. Telemetry → Dependency → Procurement → Provisioning → Synthesizer → Confidence Advisor → Portfolio Narrative. Telemetry classifies right-sizing from RVTools utilization; Dependency flags PHI/hardware/latency risk as a tri-state (true/false/UNKNOWN — never a silent guess); Procurement checks SaaS availability and BAA status; Provisioning and Synthesizer produce the 6R recommendation with a verification-gated confidence score; Confidence Advisor builds the prioritized discovery backlog; Portfolio Narrative writes the CXO-facing summary.
  2. Bracketed by non-LLM steps: a pre-pipeline Enrich Telemetry stage joins the 408-VM app-assignment sheet against a 3,846-VM RVTools export before any agent runs, and a post-pipeline Scenario Bridge plus HTML/executive-summary generators compute current-vs-target state with zero additional Bedrock calls.
Two deliberate "Python, not LLM" boundaries
  1. Provisioning engine: the LLM classifies workload type only — every dollar amount is computed by a deterministic Python financial engine. This single design decision is what eliminates ROI inflation, not a prompt instruction telling the model to "be accurate."
  2. Validator (22 rules, V1–V22): catches bare numerics without source citations, fabricated booleans, silent defaults, and — notably — a "ceiling-backlog incoherence" check and a ladder-audit rule (V22) enforcing that baseline + sum(optimization-ladder increments) == headline number on every single run. The confidence-and-savings math is audited by code, not trusted from the model's own arithmetic.
Why this matters for a CSA conversation: "the AI recommends and a deterministic engine verifies" is exactly the provenance argument a regulated customer's finance or compliance reviewer will ask for before trusting an AI-generated migration business case.
Proven reusable, not just reused — the v3 → v4 story
When the same pipeline was re-pointed at a new client engagement (MUSC Health, Imaging & Radiology — 36 apps, 408 VMs), every file was explicitly classified before a line was touched.
  1. COPY_VERBATIM (zero edits permitted): the agent orchestration, the 22-rule validator, the provisioning engine, the scenario bridge, and the PII masking module (AWS Comprehend) — the parts where correctness matters most were never touched at all.
  2. COPY_MODIFY (targeted string substitutions only): config, data loader, pipeline entry point, and the two report generators — version labels and file paths, nothing structural.
  3. Exactly one new file: the telemetry enrichment script joining the new client's 408-VM assignment sheet against a 3,846-VM RVTools export. Stated design philosophy: "minimize diff surface — every line that differs from the prior version is a potential regression."
This is the "reusable solution patterns across verticals" claim, proven, not asserted: a documented, auditable change-management discipline for reusing an AI pipeline across client engagements — the same rigor I'd want to see (and would enforce) applying an accelerator across multiple Caylent customer engagements.
Production hardening, in the model's own words
From the project's maintained known-issues log — treated as expected behavior, not surprises during a live demo.
  1. JSON parse failures on first attempt are common and recovered automatically via a retry that injects an explicit <JSON>...</JSON> correction instruction (up to 2 retries) — logged as a recovery, not an error.
  2. The validator accepts both blast_radius and blast_radius_estimate field names to absorb minor model output drift rather than failing the run.
  3. RVTools VM-name matching is case-insensitive and whitespace-stripped; unmatched VMs are kept in the output with rvtools_matched: false and null telemetry fields rather than silently dropped.
Cost reality at scale: ~$8–10 per 10 applications (~1M tokens) on Claude Sonnet 4.6 via Bedrock, scaling to the $24 total for the full 36-app / 408-VM engagement — with a live, publicly viewable results dashboard and executive summary published to S3 static website hosting.
Azure / GCP-Built Solutions — AWS-Native Redesign
The core exercise: for each project below, I can explain exactly what I'd swap, and why, in concrete service-to-service terms.
Enterprise Application Rationalization Platform (Azure Twin)
I built both sides of this one deliberately — the cleanest direct comparison in the portfolio.
As Built — Azure
  • 10 Bicep modules, 4-subnet HIPAA VNet
  • Azure OpenAI (GPT-4o) multi-agent swarm
  • Durable Functions ETL pipeline
  • Cosmos DB multi-API + Azure AI Search vector index
  • Container Apps hosting 2 MCP servers
AWS-Native Equivalent (already built — eare-aws.html)
  • 9 CloudFormation modules, VPC with HIPAA-eligible subnets
  • Amazon Bedrock (Claude) 5-agent swarm
  • Step Functions ETL with PHI masking
  • OpenSearch (or Bedrock Knowledge Bases) vector index — single DynamoDB table set instead of Cosmos multi-API
  • ECS Fargate hosting 2 MCP servers
Why AWS wins: Bedrock Knowledge Bases give you managed RAG (chunking, embedding, retrieval) as one service instead of wiring Cosmos DB + Azure AI Search + a custom retrieval layer by hand — fewer moving parts to secure and audit in a HIPAA environment. Step Functions' native audit history (every state transition logged) is a cleaner compliance artifact than Durable Functions' orchestration history for a customer's compliance team to review.
Enterprise AI Governance Platform (Azure Original)
Second side-by-side I built deliberately — same business logic, zero forked code, both clouds.
As Built — Azure
  • 4 Bicep modules, Azure Functions compute
  • Cosmos DB — 7 containers
  • Managed Identity for secrets
  • 18 numbered setup scripts → 1 unified seed runner
AWS-Native Equivalent (already built — advisory-aws.html)
  • 5 CloudFormation stacks, Lambda compute
  • DynamoDB — 7 tables (Cosmos containers → GSIs)
  • Secrets Manager + IAM roles
  • 17 API Gateway routes
Why AWS wins: DynamoDB's default composite-key modeling idiom (e.g. entityType#id) and its lack of a hard per-partition storage ceiling make the specific hot-partition failure mode I hit on Cosmos DB (detailed in the Data Pipeline Evolution card below) less likely to happen and easier to recover from if it does. IAM + Secrets Manager also gives finer-grained, audit-log-native access control than Managed Identity for a multi-tenant governance product.
Pre-Sales Intelligence Research Agent
As Built — Azure
  • Azure AI Foundry hosting
  • Claude 3.5 Sonnet, custom tool-use loop
  • Tools: LinkedIn API, 10-K filings, news APIs
  • BM25 search + PostgreSQL
AWS-Native Equivalent
  • Amazon Bedrock Agents (Claude 3.5 Sonnet)
  • Lambda action groups — one per tool (LinkedIn, 10-K, news)
  • Bedrock Guardrails on outbound company data
  • OpenSearch Serverless (hybrid BM25 + vector) or Aurora Serverless v2 + pgvector
Why AWS wins: Bedrock Agents natively manage the reasoning/tool-orchestration loop and action-group schema (OpenAPI, same pattern I already prototyped in the Bedrock Agent Weather exploration) instead of a hand-rolled loop — less custom code to maintain. Bedrock Guardrails gives a policy-as-config way to prevent the agent from surfacing sensitive competitive intel verbatim, which matters more as this tool scales past internal use.
Natural Language Operations Assistant (Park Agent Chat)
Already calls Bedrock for the model — compute and two of three databases are the cross-cloud part.
As Built — Azure + Bedrock (hybrid)
  • Azure Functions compute
  • Claude Sonnet 4.5 via AWS Bedrock (cross-cloud call)
  • Bedrock Prompt Cache
  • PostgreSQL HNSW + Cosmos DB (two stores)
  • Azure Static Web Apps (frontend)
AWS-Native Equivalent
  • Lambda compute, same region as Bedrock
  • Claude Sonnet 4.5 via Bedrock (same account, no network hop)
  • Bedrock Prompt Cache (unchanged)
  • Aurora PostgreSQL Serverless v2 + pgvector (single store, replaces Postgres + Cosmos)
  • CloudFront + S3 (frontend)
Why AWS wins: This is the most concrete case in the whole portfolio — today the Azure Function makes a real cross-cloud network call to Bedrock for every request. Moving compute to Lambda in the same AWS account/region removes that hop entirely (lower latency, one fewer network boundary to secure), and consolidating Postgres + Cosmos DB into one Aurora pgvector instance removes a second database to operate and reconcile.
Operational Knowledge Retrieval System (Knowledge RAG)
As Built — Azure
  • Azure OpenAI text-embedding-3-small
  • Self-hosted PostgreSQL + pgvector, HNSW index
  • Phi-3 Mini / Phi-4-mini SLM classifier
  • Cosmos DB (retired in v3)
AWS-Native Equivalent
  • Amazon Titan Embeddings or Cohere Embed via Bedrock
  • Aurora PostgreSQL Serverless v2 + pgvector (managed, auto-scaling)
  • SageMaker Serverless Inference hosting the Phi-3/Phi-4-mini classifier
  • Single database from day one — no Cosmos DB detour
Why AWS wins: Aurora Serverless v2 auto-scales the pgvector workload without me managing patching or provisioning — same database engine, fully managed. SageMaker Serverless Inference is the right fit for the small SLM classifier specifically because it's invoked per-query rather than constantly, so I'm not paying for an always-on GPU/CPU endpoint the way a container-hosted Phi-3 deployment would require.
Weather Intelligence API & ML Nowcasting Pipeline
As Built — GCP
  • BigQuery + BigQuery ML for scoring
  • Cloud Run Jobs (5), triggered by Cloud Scheduler
  • GCS public bucket serving the JSON API
  • Terraform IaC
AWS-Native Equivalent
  • Redshift Serverless (+ Redshift ML) for fusion/scoring
  • Lambda functions on EventBridge Scheduler (15-min cadence fits well within Lambda limits)
  • S3 + CloudFront serving the JSON API
  • CloudFormation or CDK IaC
Why AWS wins: EventBridge Scheduler + Lambda bills per-invocation with no minimum container spin-up cost, versus Cloud Run Jobs which bill for the job's full execution window — at a 15-minute cadence over a month that's a real, quantifiable cost difference I can calculate live if asked. Redshift Serverless separates compute/storage exactly like BigQuery but plugs directly into Glue Data Catalog and Lake Formation, which matters for Caylent's data-modernization/lakehouse practice specifically.
Satellite Lightning → Park Ride Risk Intelligence
As Built — GCP
  • GOES-18 GLM NetCDF ingestion every 20 seconds
  • BigQuery storage + spatial (Haversine) filtering
  • GCP Cloud Run compute
AWS-Native Equivalent
  • Kinesis Data Streams ingesting the 20-second cadence feed
  • Lambda consumers doing the geospatial filter per record
  • Amazon Timestream (or Redshift Serverless) for the storm state machine's time-series data
Why AWS wins: Kinesis is purpose-built for a continuous 20-second-cadence stream — it's a more natural fit than batch-oriented BigQuery streaming inserts for this ingestion pattern, and Lambda-per-record keeps the geospatial filter stateless and cheap. Timestream's time-series-native storage (automatic tiering, time-bucketed queries) fits the 5-state storm machine's query pattern better than a general-purpose warehouse table.
Database Architecture Decision: A Cost Inflection Story
This is the strongest "why AWS wins" story in the portfolio — a real, quantified failure, not a hypothetical.
As Built — Azure (3 eras)
  • Era 1: Cosmos DB, partition key /doctypewaitSnapshot alone took 62.5% of 5.9M documents (written for 200+ attractions every 10 min under one low-cardinality key value)
  • Era 2: Hierarchical partition keys fixed the hot partition, but added a $394/mo, 5-container Cosmos floor — including a $116/mo round-trip where PostgreSQL rollups were written to Cosmos and read straight back
  • Era 3: PostgreSQL only — 6 covering indexes, ThreadPoolExecutor batch writes, $0 Cosmos cost
AWS-Native Equivalent
  • DynamoDB with a composite partition key (e.g. doctype#date) designed in from day one
  • — or — Aurora PostgreSQL Serverless v2 from day one (the destination I actually landed on)
  • See the honest breakdown below — this one deserves more than a one-liner
Would DynamoDB Actually Have Been a Better Fit Than Cosmos DB Here?
The honest, root-cause-level answer — not "DynamoDB is just better," but where it specifically would and wouldn't have changed the outcome.
  1. No hard, un-splittable per-partition-key storage ceiling. Cosmos DB enforces a fixed 20 GB limit per logical partition with no way to raise it — waitSnapshot was on track to hit that wall in ~11 months, and the only fix was standing up a new container, migrating 5.9M documents, updating 34 consumer call sites, and running 48 hours of parallel validation. DynamoDB has no equivalent fixed ceiling per partition key — AWS manages physical partition splits for storage growth transparently, so the same key design wouldn't force a "new table + full migration" project just to keep growing.
  2. No "index everything" default. Cosmos DB's default indexing policy indexes every field automatically, which added roughly 50% unnecessary write RUs on top of the hot-partition problem. DynamoDB only charges for the primary key plus whatever Global/Local Secondary Indexes you explicitly create — this specific self-inflicted cost simply has no DynamoDB equivalent, because there's no "index everything by default" behavior to opt out of.
  3. Composite partition keys are the default DynamoDB idiom, not an advanced escape hatch. The permanent fix in Cosmos — hierarchical partition keys like [doctype, date] — was a comparatively new Cosmos feature that still required a brand-new container to adopt. In DynamoDB, a composite/overloaded partition key (doctype#date) is the standard way you're taught to model a table from day one. The mistake that happened here is less likely to happen at all under normal DynamoDB modeling practice, not just easier to fix after the fact.
The honest caveat: DynamoDB isn't immune to bad key design. If the exact same low-cardinality key (doctype alone, ~5 distinct values) had been used verbatim, that one hot key value would still bottleneck at a single partition's throughput ceiling (roughly 3,000 RCU / 1,000 WCU before AWS adds physical partitions) and would still throttle under this write pattern — DynamoDB doesn't eliminate the need for partition key design discipline, it just removes two of the three ways this specific failure compounded (the hard storage wall and the automatic over-indexing cost).
The bigger lesson, and the one I'd actually lead with: the case study's own conclusion holds regardless of NoSQL vendor — park_timeseries (high-frequency, naturally-partitioned-by-entity time series) was a genuinely good fit for a distributed document/wide-column store; park_aggregates (relational GROUP BY rollups written out and read straight back) never needed a NoSQL round-trip at all. DynamoDB would have been a better-behaved version of the same tool for the first half of that data, but the real fix — the one I actually shipped — was recognizing that most of this workload was relational and didn't need a globally-distributed store's differentiators in the first place. That's a data-modeling lesson, not a vendor-preference one, and it's the more credible answer in an interview.
Live Theme Park Data Pipeline (Park Data Ingest)
As Built — Azure
  • Azure Container Apps Jobs, scheduled every 10 minutes
  • PostgreSQL Flexible Server, 4 tables
  • psycopg2, 35-day geo-redundant backup
AWS-Native Equivalent
  • Lambda on EventBridge Scheduler (10-min job fits comfortably within Lambda's 15-min max)
  • Aurora PostgreSQL Serverless v2 or RDS for PostgreSQL, Multi-AZ
  • Automated backups + cross-region snapshot copy for the same 35-day retention
Why AWS wins: A 10-minute-cadence ETL job is well inside Lambda's execution window, so I'd pay only for the seconds the job runs — Container Apps Jobs bills for the full container lifecycle per run, which is a real cost delta at this frequency over a month. RDS/Aurora's automated backup + cross-region copy replaces the "35-day geo-redundant backup" requirement with a managed feature instead of custom scripting.
Policy Governance Library → Multi-Tenant SaaS
As Built
  • TypeScript (strict) + Fastify, containerized
  • PostgreSQL (tenant isolation) + Redis Stack (policy cache, hot-reload)
  • Git-webhook-triggered blue-green index swap, zero downtime
AWS-Native Equivalent
  • AWS App Runner or ECS Fargate running the Fastify service
  • Aurora PostgreSQL Serverless v2 (tenant data) + ElastiCache for Redis or MemoryDB (policy cache)
  • CodeDeploy blue-green deployment, or the same webhook pattern via Lambda + API Gateway
Why AWS wins: App Runner gives a genuinely simpler zero-infrastructure container deploy path for a straightforward multi-tenant API than managing Container Apps environments directly. MemoryDB adds Multi-AZ durability with microsecond read latency for the policy cache — a meaningful upgrade over a self-hosted Redis Stack container if this SaaS product needs to guarantee cache availability per tenant SLA.
Automated Social Content Factory (SLM Content Pipeline)
Same cross-cloud pattern as Park Agent Chat — compute on Azure, model calls to AWS Bedrock. See the Containers section below for the deeper Lambda-vs-Fargate breakdown on this one.
As Built — Azure Container Apps Jobs + Bedrock (hybrid)
  • Azure Container Apps Jobs running the LangChain agentic loop (30+ tools) — the Phi-3 Mini query classifier used elsewhere in the platform runs separately on Azure Functions
  • Claude Haiku (pass 1) + Claude Sonnet (passes 2–3) via AWS Bedrock
  • PostgreSQL RAG, ThreadPoolExecutor parallel tool calls
AWS-Native Equivalent
  • Lambda container-image deployment (or Fargate if telemetry shows the multi-pass loop regularly exceeds 15 min) in the same account/region as Bedrock
  • Claude Haiku + Sonnet via Bedrock — no cross-cloud call
  • Aurora PostgreSQL Serverless v2 + pgvector for RAG
Why AWS wins: Identical argument to Park Agent Chat — the model calls already go to Bedrock today, so running the orchestration layer on Lambda/Fargate in the same account removes a live cross-cloud network hop on every one of the 30+ tool calls per pipeline run, which compounds into a measurable latency and egress-cost difference at scale.
Compute Architecture Lessons Learned — Containers & Serverless
This is the section I'd use as the live proof-of-concept in the interview: not "AWS has containers and serverless too," but a documented trail of real decisions across containers I have running in production today on GCP and Azure — scheduled Cloud Run Jobs, Container Apps Jobs, an always-on ML inference container, and long-running MCP servers — that show how I actually choose between managed containers and serverless functions, and exactly what changes when I redesign each one AWS-native.
A. Containers — What's Actually Running in Production on GCP & Azure Today
Lesson 1: Scheduled serverless containers across all three clouds — a real three-way, not archived experiments
GCP Cloud Run Jobs (Weather Whisperer, Lightning Tracker) and Azure Container Apps Jobs (Park Data Ingest and the Automated Content Pipeline) are live in production right now. → weather-whisperer.html · lightning-tracker.html · park-data-ingest.html · slm-content-pipeline.html
DimensionGCP — Cloud Run JobsAzure — Container Apps JobsAWS-Native Equivalent
What's running5 independently-scheduled jobs (3 ingestion + ML scoring + insights) for Weather Whisperer; lightning-watcher-optimized (15-min cadence) + weather-ride-outage-tracker (5-min cadence) for Lightning Tracker2 workloads: Park Data Ingest (1 scheduled ETL job, every 10 min, 2–3 min runtime, 286 entities) and the Automated Content Pipeline (10 pipeline types, each a scheduled job running a 3-pass Claude generation loop with 30+ LangChain tool calls)Fargate Scheduled Tasks (EventBridge Scheduler → ECS RunTask) or AWS Batch on Fargate for the multi-job pipeline
Trigger mechanismCloud Scheduler cron per job, independently configuredContainer Apps' built-in cron trigger, one per job/pipeline typeEventBridge Scheduler — same cron-per-job model, or a single Batch job queue with explicit dependencies
Job dependenciesNone native — 3 ingestion jobs and the scoring job run on offset cron schedules and rely on timing, not an enforced dependency graphContent Pipeline has an internal sequential dependency (KPI gate → Pass 1 → Pass 2 → Pass 3) enforced in code within one job execution, not across separate scheduled jobsAWS Batch (or Step Functions) can express "run scoring only after all 3 ingestion jobs succeed" as an explicit, enforced cross-job dependency rather than a timing assumption — and could promote the Content Pipeline's in-code pass sequence to a visible, auditable Step Functions state machine
Seasonal / on-demand pauseLightning Tracker's watcher job is paused Nov–Mar with a single gcloud scheduler jobs pause command — no code change, no infra teardown, BigQuery table persistsNot exercised in these pipelinesIdentical property: aws scheduler update-schedule --state DISABLED — same one-command pause, no infra removed
Idle costScales to zero between runsScales to zero between runsFargate tasks incur zero cost when not running — same scale-to-zero economics
The honest caveat: Cloud Run Jobs, Container Apps Jobs, and Fargate Scheduled Tasks are philosophically the same product — serverless, pay-per-execution-second, no cluster to manage. This isn't a case where AWS does something the others can't; the seasonal-pause pattern in particular is functionally identical on all three.
The genuine AWS-native win: it's orchestration, not execution. Today, Weather Whisperer's 5 jobs coordinate through cron offsets and convention, and the Content Pipeline's 3-pass sequence is enforced in application code inside a single job rather than as a visible workflow. AWS Batch or Step Functions would make both of those an explicit, enforced, and auditable dependency graph instead — a real reliability and observability improvement, not a lateral service swap.
Lesson 2: A heavier container Job — using the Content Pipeline to test the real Lambda-vs-Fargate boundary
Automated Content Pipeline — Azure Container Apps Jobs, 10 pipeline types, each running a KPI gate + 3-pass Claude generation loop (30+ LangChain tool calls via ThreadPoolExecutor, then Claude Sonnet formatting, then a Reels script pass). The Phi-3 Mini query classifier used elsewhere in the platform runs separately on Azure Functions — not part of this container comparison. → slm-content-pipeline.html · Architecture
  1. Why this is the better container comparison than a simple ETL job: Park Data Ingest is a short, bounded 2–3 minute job — an obvious Lambda candidate. The Content Pipeline is a heavier, less obviously "serverless function"-shaped workload: 30+ tool calls plus three sequential Claude Haiku/Sonnet calls per run, across 10 independently scheduled pipeline types. It's the more honest test case for where the Lambda/Fargate line actually sits.
  2. The packaging detail that actually matters: LangChain plus the Claude/Bedrock SDKs and their supporting dependencies is a large dependency tree — large enough to risk exceeding Lambda's 250 MB unzipped deployment package limit. The realistic AWS-native path isn't a plain zip-based Lambda function; it's a Lambda container-image deployment (up to 10 GB), which keeps the serverless execution model while accommodating the dependency footprint.
  3. The duration question, answered honestly: this case study didn't need to measure per-run duration precisely, but with the 30+ tool calls parallelized through ThreadPoolExecutor rather than run serially, a realistic per-execution runtime very likely still fits inside Lambda's 15-minute cap. I'd default to a Lambda container image first, and only move to Fargate if production telemetry showed runs regularly approaching that ceiling — which isn't something I've had to prove out at this workload's current scale.
Redesign target: Lambda (container-image deployment, not zip) as the default for each of the 10 pipeline types, promoted to Fargate only for whichever specific pipeline type's telemetry shows it consistently running long — not a blanket move to containers just because the workload is "heavier" than a simple ETL job.
Lesson 3: Managed containers, AWS vs Azure, for long-running stateful services
EARE Platform — the same HIPAA-compliant MCP servers, built on both clouds. → eare-aws.html · eare-azure.html
DimensionAWS — ECS FargateAzure — Container Apps
Compute2 MCP servers as Fargate tasks in a VPC2 MCP servers as Container Apps in a VNet
Private networkingVPC endpoints — one resource per AWS service type, attached to the route tableExplicit Private Endpoint + Private DNS Zone per service — more discrete resources for the same zero-public-egress guarantee
Identity / secretsIAM task roles + Secrets Manager — assembled per serviceManaged Identity — 7 identities, zero secrets in code, more turnkey out of the box
Compliance overheadHIPAA resource tagging + VPC Flow Logs, 90-day retention — extra config, not extra servicesComparable tagging/logging, layered on top of the per-service Private Endpoint model
The honest caveat: Azure's Managed Identity model is arguably more turnkey than assembling IAM roles + Secrets Manager for the same zero-secrets guarantee — this isn't a clean sweep for AWS. The concrete AWS win is specifically on the networking dimension: fewer discrete resources to define and audit to reach the same "no traffic leaves the private network" posture, which matters directly for a customer's HIPAA/PCI reviewer counting resources during an audit.
Redesign target: for any future HIPAA/regulated container workload, I'd lead with ECS Fargate + VPC endpoints specifically because the audit surface is smaller — fewer networking resources to explain to a compliance reviewer for the same guarantee.
B. Serverless — Lambda vs. Azure Functions, Step Functions vs. Durable Functions
Lesson 4: Declarative state machines audit better than code-based orchestration
EARE ETL pipeline — Step Functions (AWS) vs. Durable Functions (Azure), both orchestrating the same PHI-masking ETL. → eare-aws-arch.html · eare-azure-arch.html
DimensionAWS — Step FunctionsAzure — Durable Functions
Orchestration modelDeclarative state machine (Amazon States Language, JSON)Code-based orchestrator function (async/await replay pattern)
Audit trailNative visual state-transition history — every step, retry, and branch logged as structured data by defaultExecution replay log — reconstructable, but not a declarative diagram a compliance reviewer can read cold
Function durationLambda: 15-minute hard cap per invocation — forces small, composable stepsConsumption plan: 5 min default (extendable to 10); Premium/Dedicated plans remove the cap entirely
Flexibility ceilingMore rigid by default — but that rigidity is the forcing function for composable stepsMore flexible at the top end if you're willing to pay for Premium/Dedicated
The honest caveat: Azure Functions on a Premium plan genuinely has a higher duration ceiling than Lambda — if a workload needs a single very long-running function, Azure has more headroom without changing compute models. Lambda's cap is a constraint, not an unqualified advantage.
Redesign target: for a regulated-industry ETL pipeline specifically, I'd still lead with Step Functions — the built-in, structured audit trail is a stronger artifact to hand a HIPAA/PCI reviewer than reconstructing history from a Durable Functions replay log, and Lambda's duration cap pushes toward the smaller, individually-testable steps that a reviewer can actually validate one at a time.
Lesson 5: Put serverless compute next to the AI service it calls
Park Agent Chat and the SLM Content Pipeline both run on Azure Functions but call AWS Bedrock for every model invocation — a live cross-cloud hop today, not a hypothetical one. → park-agent-chat.html · slm-content-pipeline.html
  1. The lesson: when the AI/model layer and the compute layer are on different clouds, every request pays a real network hop and crosses an extra security boundary — it works, but it's not the design I'd choose starting from scratch.
  2. AWS-native redesign: move the orchestration compute onto Lambda (or Fargate only if a single run exceeds 15 minutes) in the same AWS account and region as Bedrock. Same model calls, same tool logic — one fewer network boundary to secure and monitor.
Lesson 6: Lambda's duration ceiling as a design constraint, not just a limit
Digital Twin App Dependency Simulator — 5 AI simulations across 1,053 servers, running on AWS Lambda. → digital-twin.html
  1. Where this stands today: the simulations run on Lambda now; whether any single run has approached the 15-minute cap isn't something I've had to solve for at this portfolio's scale.
  2. The forward-looking design point: if a customer's portfolio were large enough to risk a single simulation exceeding 15 minutes, the right move is a Step Functions Map state fanning the work out across many parallel Lambda invocations — not jumping to Fargate. That preserves serverless economics (pay only for the compute-seconds used) while removing the single-invocation duration ceiling entirely.
Using This as the Interview Proof-of-Concept
Suggested narrative arc — lead with the failure, not the finished design
  1. Open with what's actually running: "I have scheduled containers in production right now on two different clouds — Cloud Run Jobs on GCP, Container Apps Jobs on Azure — coordinating a multi-job weather data pipeline through cron offsets and convention rather than an enforced dependency graph. That's a real, current gap I can name, not a hypothetical one." (Weather Whisperer, Lightning Tracker, Park Data Ingest)
  2. Show the applied boundary on AWS: "On the AWS side of the governance platform I built, ECS Fargate is reserved specifically for the two long-running MCP servers — everything else is Lambda and Step Functions. That boundary is deliberate, and AWS Batch or Step Functions would close the dependency gap the GCP/Azure pipeline has today." (EARE AWS)
  3. Name the redesign target, live: "Anything I've built on Azure Functions that also calls Bedrock, I'd move onto Lambda in the same AWS account first — that removes a network hop that exists in production today, not a hypothetical one." (Park Agent Chat, SLM Content Pipeline)
  4. Close with the meta point: "That's the same judgment call I'd bring to a customer's Well-Architected review — I don't default to 'always serverless' or 'always containers.' I have a tested boundary for when each one is right, because I've paid the cost of getting it wrong once already."
The Pattern Across All of Them
Two arguments repeat because they're genuinely the strongest ones I have, not because I'm reaching for filler: (1) where a system already calls Bedrock from non-AWS compute (Park Agent Chat, SLM Content Pipeline), moving compute onto AWS removes a real, current cross-cloud network hop — not a hypothetical one; and (2) the Cosmos DB hot-partition failure was a genuine, already-lived incident, and DynamoDB's storage model and default composite-key idiom would have prevented or softened most of it — though not all of it, and the full breakdown (with the honest caveats) is in the Data Pipeline Evolution card above. Everywhere else, the honest answer is that the Azure/GCP choice was reasonable at the time — client stack, team familiarity, or the specific managed service available — and the AWS-native path is a credible, comparably strong alternative rather than a strictly "better" one.