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:
App rationalization / migration assessment: the Enterprise COTS App Rationalization pipeline (rarf-v4) — a 7-agent Bedrock pipeline (Claude Sonnet 4.6) with 22 deterministic validation rules and a Python-not-LLM financial engine, $24 / 4.9 hrs for a 408-VM healthcare portfolio — is a directly reusable accelerator for any customer's MAP-style assessment engagement, and it's already been proven reusable once: the same pipeline was re-pointed at a brand-new client engagement with a disciplined file-by-file reuse classification (copy-verbatim vs. copy-modify) to minimize regression risk. → rarf-v4.html
Regulated-industry application platforms: the EARE AWS platform — a full HIPAA-compliant Bedrock 5-agent swarm with MCP servers on Fargate, generated entirely from spec — is a ready pattern for any healthcare or regulated customer that needs an AI-assisted application built and audited fast. → eare-aws.html
AI governance / compliance-as-code: the Advisory AI Governance Platform (AWS) — Lambda + DynamoDB + Bedrock, scanning against 10 regulatory frameworks with SDLC gate enforcement — is a reusable accelerator for any customer whose AI adoption is blocked on a governance story, which is most regulated-industry conversations today. → advisory-aws.html
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.
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.
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
Bedrock — same model layer, now same-cloud as the compute calling it
Why this is the most concrete live example in the whole page
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.
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.
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.
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.
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.
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 (rarf-v4) — AWS Bedrock (Claude Sonnet 4.6) 7-agent pipeline, 22 deterministic validation rules, a Python-only financial engine, $24 / 4.9 hrs for a 408-VM portfolio, live public dashboard on S3. → rarf-v4.html
EARE Platform — AWS — Bedrock 5-agent swarm, 2 MCP servers on ECS Fargate, Step Functions ETL with PHI masking, 9 CloudFormation modules. Built the Azure twin too (see below) — this is the side-by-side I know cold. → eare-aws.html
Enterprise AI Governance Platform — AWS — Lambda + DynamoDB + CloudFormation, ported from the Azure original with zero forked business logic via a cloud-adapter layer. → advisory-aws.html
Digital Twin App Dependency Simulator — dual-cloud: Amazon Neptune (AWS side) and Cosmos DB Gremlin API (Azure side) both hold the same dependency graph model. → digital-twin.html
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.
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.
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
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."
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.
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.
COPY_MODIFY (targeted string substitutions only): config, data loader, pipeline entry point, and the two report generators — version labels and file paths, nothing structural.
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.
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.
The validator accepts both blast_radius and blast_radius_estimate field names to absorb minor model output drift rather than failing the run.
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.
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.
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)
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)
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.
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.
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 /doctype — waitSnapshot 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.
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.
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.
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.
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.
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
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.
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
5 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 Tracker
2 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 mechanism
Cloud Scheduler cron per job, independently configured
Container Apps' built-in cron trigger, one per job/pipeline type
EventBridge Scheduler — same cron-per-job model, or a single Batch job queue with explicit dependencies
Job dependencies
None native — 3 ingestion jobs and the scoring job run on offset cron schedules and rely on timing, not an enforced dependency graph
Content 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 jobs
AWS 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 pause
Lightning Tracker's watcher job is paused Nov–Mar with a single gcloud scheduler jobs pause command — no code change, no infra teardown, BigQuery table persists
Not exercised in these pipelines
Identical property: aws scheduler update-schedule --state DISABLED — same one-command pause, no infra removed
Idle cost
Scales to zero between runs
Scales to zero between runs
Fargate 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
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.
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.
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
VPC endpoints — one resource per AWS service type, attached to the route table
Explicit Private Endpoint + Private DNS Zone per service — more discrete resources for the same zero-public-egress guarantee
Identity / secrets
IAM task roles + Secrets Manager — assembled per service
Managed Identity — 7 identities, zero secrets in code, more turnkey out of the box
Compliance overhead
HIPAA resource tagging + VPC Flow Logs, 90-day retention — extra config, not extra services
Comparable 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
Dimension
AWS — Step Functions
Azure — Durable Functions
Orchestration model
Declarative state machine (Amazon States Language, JSON)
Code-based orchestrator function (async/await replay pattern)
Audit trail
Native visual state-transition history — every step, retry, and branch logged as structured data by default
Execution replay log — reconstructable, but not a declarative diagram a compliance reviewer can read cold
Function duration
Lambda: 15-minute hard cap per invocation — forces small, composable steps
Consumption plan: 5 min default (extendable to 10); Premium/Dedicated plans remove the cap entirely
Flexibility ceiling
More rigid by default — but that rigidity is the forcing function for composable steps
More 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
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.
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
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.
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
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)
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)
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)
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.