<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rajaram Yadav</title>
    <description>The latest articles on Forem by Rajaram Yadav (@rajaramyadav).</description>
    <link>https://forem.com/rajaramyadav</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F456759%2F0113222f-1493-47a2-beb4-5b7f32c862b2.png</url>
      <title>Forem: Rajaram Yadav</title>
      <link>https://forem.com/rajaramyadav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rajaramyadav"/>
    <language>en</language>
    <item>
      <title>Deploy React (Vite) to AWS the Right Way — S3 + CloudFront + CodePipeline</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sun, 03 May 2026 13:54:54 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/deploy-react-vite-to-aws-the-right-way-s3-cloudfront-codepipeline-146f</link>
      <guid>https://forem.com/rajaramyadav/deploy-react-vite-to-aws-the-right-way-s3-cloudfront-codepipeline-146f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Stop paying for nginx containers to serve static files. Here's the complete S3 + CloudFront setup with automated CI/CD — costs under $2/month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomsacmi9weketvg8fu8g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomsacmi9weketvg8fu8g.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Your React app is a collection of static files after npm run build. HTML. JavaScript. CSS. Images.&lt;br&gt;
Serving static files from a Docker container on ECS is paying a chef to microwave leftovers. S3 + CloudFront costs less than a coffee per month, serves files from 400+ global edge locations, and scales to millions of requests automatically.&lt;br&gt;
Here's the complete setup — S3 bucket, CloudFront distribution, CodePipeline CI/CD — so every push to main automatically builds and deploys your Vite app in under 3 minutes.&lt;/p&gt;

&lt;p&gt;Why Not a Container?&lt;br&gt;
ECS + nginx container:&lt;br&gt;
  EC2 instance     : ~$15/month&lt;br&gt;
  ALB (for HTTPS)  : ~$16/month&lt;br&gt;
  Total            : ~$31/month&lt;br&gt;
  Regions served   : 1&lt;/p&gt;

&lt;p&gt;S3 + CloudFront:&lt;br&gt;
  S3 storage       : ~$0.12/month&lt;br&gt;
  CloudFront       : ~$0.88/month&lt;br&gt;
  Total            : ~$1/month&lt;br&gt;
  Regions served   : 400+ edge locations worldwide&lt;br&gt;
The only case for containerising React: you're using Next.js with server-side rendering. Vite + React? Static files. S3 + CloudFront.&lt;/p&gt;

&lt;p&gt;The Complete Pipeline&lt;br&gt;
Push to main&lt;br&gt;
    ↓&lt;br&gt;
CodeStar webhook → CodePipeline&lt;br&gt;
    ↓&lt;br&gt;
Stage 1: SOURCE&lt;br&gt;
  Pulls repo → zips → S3 artifact bucket&lt;br&gt;
    ↓&lt;br&gt;
Stage 2: BUILD (CodeBuild)&lt;br&gt;
  npm ci&lt;br&gt;
  npm run build → /dist&lt;br&gt;
  aws s3 sync dist/ → frontend S3 bucket&lt;br&gt;
  CloudFront cache invalidation&lt;br&gt;
    ↓&lt;br&gt;
Users hit CloudFront URL&lt;br&gt;
  Served from nearest of 400+ edge locations&lt;br&gt;
  Under 3 minutes from push to live&lt;/p&gt;

&lt;p&gt;Step 1 — S3 Bucket&lt;br&gt;
bashaws s3 mb s3://your-app-frontend-ACCOUNTID --region us-east-1&lt;br&gt;
Settings:&lt;/p&gt;

&lt;p&gt;Block all public access: ON — CloudFront accesses via OAC, not public URL&lt;br&gt;
Static website hosting: OFF — OAC is more secure, doesn't need this&lt;br&gt;
Versioning: OFF — not needed for static hosting&lt;/p&gt;

&lt;p&gt;Step 2 — CloudFront Distribution&lt;br&gt;
Go to CloudFront → Create distribution.&lt;br&gt;
Origin domain         : your-app-frontend-ACCOUNTID.s3.us-east-1.amazonaws.com&lt;br&gt;
                        ← select from dropdown, don't type manually&lt;/p&gt;

&lt;p&gt;Origin access         : Origin Access Control (OAC)&lt;br&gt;
                        → Create new OAC&lt;br&gt;
                        → Signing behavior: Sign requests&lt;/p&gt;

&lt;p&gt;Viewer protocol       : Redirect HTTP to HTTPS&lt;br&gt;
Cache policy          : CachingOptimized&lt;br&gt;
Default root object   : index.html    ← critical&lt;br&gt;
After creating: CloudFront shows a banner with a bucket policy to copy. Copy it — you need it next.&lt;br&gt;
Your CloudFront domain: d1abc2def3.cloudfront.net — this is your app's URL.&lt;/p&gt;

&lt;p&gt;Step 3 — S3 Bucket Policy&lt;br&gt;
Paste the policy CloudFront generated into your S3 bucket:&lt;br&gt;
S3 → your bucket → Permissions → Bucket policy → Edit&lt;br&gt;
json{&lt;br&gt;
    "Version": "2012-10-17",&lt;br&gt;
    "Statement": [&lt;br&gt;
        {&lt;br&gt;
            "Sid": "AllowCloudFrontServicePrincipal",&lt;br&gt;
            "Effect": "Allow",&lt;br&gt;
            "Principal": {&lt;br&gt;
                "Service": "cloudfront.amazonaws.com"&lt;br&gt;
            },&lt;br&gt;
            "Action": "s3:GetObject",&lt;br&gt;
            "Resource": "arn:aws:s3:::your-app-frontend-ACCOUNTID/*",&lt;br&gt;
            "Condition": {&lt;br&gt;
                "StringEquals": {&lt;br&gt;
                    "AWS:SourceArn": "arn:aws:cloudfront::ACCOUNTID:distribution/YOURDISTID"&lt;br&gt;
                }&lt;br&gt;
            }&lt;br&gt;
        }&lt;br&gt;
    ]&lt;br&gt;
}&lt;br&gt;
This is Origin Access Control — only YOUR CloudFront distribution can read your files. Nothing else.&lt;/p&gt;

&lt;p&gt;Step 4 — The Fix Everyone Forgets&lt;br&gt;
React Router handles client-side routing. /dashboard, /settings, /users/123 are all fake URLs — React intercepts them and renders the right component.&lt;br&gt;
When someone refreshes on /dashboard, the browser asks CloudFront for a file named dashboard. It doesn't exist in S3. CloudFront returns 403 or 404. Your app is broken.&lt;br&gt;
Fix: add custom error responses to CloudFront.&lt;br&gt;
CloudFront → your distribution → Error pages → Create custom error response&lt;/p&gt;

&lt;p&gt;Error code        : 403&lt;br&gt;
Response page path: /index.html&lt;br&gt;
HTTP response code: 200&lt;/p&gt;

&lt;p&gt;Error code        : 404&lt;br&gt;
Response page path: /index.html&lt;br&gt;
HTTP response code: 200&lt;br&gt;
Why return 200 instead of 404? Because React Router is about to render the correct page. Returning 404 breaks analytics, SEO, and browser history.&lt;br&gt;
Do this now. You will forget it exists and spend an hour debugging later.&lt;/p&gt;

&lt;p&gt;Step 5 — IAM Role for CodeBuild&lt;br&gt;
Role name   : CodeBuildServiceRole-your-app-frontend&lt;br&gt;
Trust       : codebuild.amazonaws.com&lt;br&gt;
Inline policy:&lt;br&gt;
json{&lt;br&gt;
  "Version": "2012-10-17",&lt;br&gt;
  "Statement": [&lt;br&gt;
    {&lt;br&gt;
      "Sid": "Logs",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],&lt;br&gt;
      "Resource": "&lt;em&gt;"&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
      "Sid": "S3Artifacts",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": ["s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:GetBucketVersioning"],&lt;br&gt;
      "Resource": [&lt;br&gt;
        "arn:aws:s3:::your-artifacts-bucket",&lt;br&gt;
        "arn:aws:s3:::your-artifacts-bucket/&lt;/em&gt;"&lt;br&gt;
      ]&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
      "Sid": "S3Frontend",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket"],&lt;br&gt;
      "Resource": [&lt;br&gt;
        "arn:aws:s3:::your-app-frontend-ACCOUNTID",&lt;br&gt;
        "arn:aws:s3:::your-app-frontend-ACCOUNTID/&lt;em&gt;"&lt;br&gt;
      ]&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
      "Sid": "CloudFront",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": ["cloudfront:CreateInvalidation"],&lt;br&gt;
      "Resource": "&lt;/em&gt;"&lt;br&gt;
    }&lt;br&gt;
  ]&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Step 6 — buildspec.yml&lt;br&gt;
Add to repo root:&lt;br&gt;
yamlversion: 0.2&lt;/p&gt;

&lt;p&gt;env:&lt;br&gt;
  variables:&lt;br&gt;
    S3_BUCKET: "your-app-frontend-ACCOUNTID"&lt;br&gt;
    CLOUDFRONT_DISTRIBUTION_ID: "YOURDISTID"&lt;/p&gt;

&lt;p&gt;phases:&lt;br&gt;
  install:&lt;br&gt;
    runtime-versions:&lt;br&gt;
      nodejs: 22&lt;br&gt;
    commands:&lt;br&gt;
      - echo "Node $(node --version)"&lt;/p&gt;

&lt;p&gt;pre_build:&lt;br&gt;
    commands:&lt;br&gt;
      # npm ci = exact versions from package-lock.json&lt;br&gt;
      # Always use this in CI. npm install can silently update deps.&lt;br&gt;
      - npm ci&lt;/p&gt;

&lt;p&gt;build:&lt;br&gt;
    commands:&lt;br&gt;
      - npm run build&lt;br&gt;
      - ls -la dist/&lt;/p&gt;

&lt;p&gt;post_build:&lt;br&gt;
    commands:&lt;br&gt;
      # --delete removes old files: stale JS chunks, renamed assets, deleted pages&lt;br&gt;
      - aws s3 sync dist/ s3://$S3_BUCKET --delete&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # Cache invalidation — without this, users get old files for up to 24hrs
  # Note: single line. No backslash continuation.
  - aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths "/*"

  - echo "Deployed $(date)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;artifacts:&lt;br&gt;
  files:&lt;br&gt;
    - "*&lt;em&gt;/&lt;/em&gt;"&lt;br&gt;
  base-directory: dist&lt;br&gt;
The YAML Rule That Costs You Hours&lt;br&gt;
Every - under commands: is a separate shell command. Backslash continuation creates a multi-line string — CodeBuild rejects it:&lt;br&gt;
yaml# ❌ YAML_FILE_ERROR: Expected Commands[N] to be of string type&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aws cloudfront create-invalidation \
--distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
--paths "/*"&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ✅ One line — works every time
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths "/*"
Why npm ci Not npm install
npm install can silently update package-lock.json. In CI, you want reproducible builds — the same packages every time. npm ci fails if package-lock.json is out of sync with package.json, making the inconsistency visible instead of hiding it.
Why --delete on S3 Sync
Vite uses content hashing: main.abc1234.js. Every build produces new filenames. Without --delete, old files pile up in S3 — old chunks from previous builds that no one will ever request. More importantly, if a file gets renamed or deleted from your project, the old version stays in S3 and can serve stale content if cache expires.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 7 — GitHub Actions (PR Checks)&lt;br&gt;
CodePipeline only fires on merge to main. For PR feedback:&lt;br&gt;
yaml# .github/workflows/ci.yml&lt;br&gt;
name: Frontend CI&lt;/p&gt;

&lt;p&gt;on:&lt;br&gt;
  pull_request:&lt;br&gt;
    branches: [ main ]&lt;br&gt;
  push:&lt;br&gt;
    branches: [ main ]&lt;/p&gt;

&lt;p&gt;jobs:&lt;br&gt;
  build:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - uses: actions/checkout@v4&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  - uses: actions/setup-node@v4
    with:
      node-version: '22'
      cache: 'npm'

  - name: Install
    run: npm ci

  - name: Lint
    run: npm run lint

  - name: Build
    run: npm run build
    # TypeScript errors, missing imports, bad JSX — all caught here
    # PR is blocked if this fails

  - name: Upload dist
    if: success()
    uses: actions/upload-artifact@v4
    with:
      name: dist
      path: dist/
      retention-days: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every PR gets:&lt;/p&gt;

&lt;p&gt;Lint check&lt;br&gt;
Full production build&lt;/p&gt;

&lt;p&gt;Broken TypeScript, bad imports, missing dependencies — caught before merge. main always has a working build.&lt;/p&gt;

&lt;p&gt;Step 8 — CodePipeline&lt;br&gt;
Create pipeline → "Build custom pipeline" (not Starter Template):&lt;br&gt;
Pipeline settings:&lt;br&gt;
  Name           : your-app-frontend-pipeline&lt;br&gt;
  Type           : V2&lt;br&gt;
  Execution mode : SUPERSEDED&lt;br&gt;
  Service role   : your CodePipeline service role&lt;br&gt;
  Artifact store : Custom → your artifacts S3 bucket&lt;/p&gt;

&lt;p&gt;Source stage:&lt;br&gt;
  Provider       : GitHub (via GitHub App)&lt;br&gt;
  Connection     : your-github-connection&lt;br&gt;
  Repository     : your-org/your-repo&lt;br&gt;
  Branch         : main&lt;br&gt;
  Output         : SourceArtifact&lt;/p&gt;

&lt;p&gt;Build stage:&lt;br&gt;
  Provider       : AWS CodeBuild&lt;br&gt;
  Input          : SourceArtifact&lt;br&gt;
  Project        : your-app-frontend-build&lt;br&gt;
  Output         : BuildArtifact&lt;/p&gt;

&lt;p&gt;Deploy stage:&lt;br&gt;
  Provider       : Amazon S3&lt;br&gt;
  Input          : BuildArtifact&lt;br&gt;
  Bucket         : your-app-frontend-ACCOUNTID&lt;br&gt;
  Extract file   : ✅ YES  ← without this, the zip stays zipped in S3&lt;br&gt;
The S3 Permission Error You Will Hit&lt;br&gt;
If you reuse a CodePipeline service role from another pipeline, it won't have access to the frontend S3 bucket:&lt;br&gt;
not authorized to perform: s3:PutObject on resource:&lt;br&gt;
arn:aws:s3:::your-app-frontend-ACCOUNTID/...&lt;br&gt;
Fix — add the frontend bucket to the role's S3 policy:&lt;br&gt;
json"Resource": [&lt;br&gt;
  "arn:aws:s3:::your-artifacts-bucket",&lt;br&gt;
  "arn:aws:s3:::your-artifacts-bucket/&lt;em&gt;",&lt;br&gt;
  "arn:aws:s3:::your-app-frontend-ACCOUNTID",&lt;br&gt;
  "arn:aws:s3:::your-app-frontend-ACCOUNTID/&lt;/em&gt;"&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;Connecting to Your Backend API&lt;/p&gt;

&lt;h1&gt;
  
  
  .env.production
&lt;/h1&gt;

&lt;p&gt;VITE_API_BASE_URL=&lt;a href="https://api.yourdomain.com" rel="noopener noreferrer"&gt;https://api.yourdomain.com&lt;/a&gt;&lt;br&gt;
javascriptconst api = axios.create({&lt;br&gt;
  baseURL: import.meta.env.VITE_API_BASE_URL,&lt;br&gt;
});&lt;br&gt;
Set VITE_API_BASE_URL as an environment variable in your CodeBuild project — injected at build time by Vite. Never hardcode API URLs.&lt;/p&gt;

&lt;p&gt;Cost Reference&lt;br&gt;
ItemCostS3 (5GB stored)$0.12/monthCloudFront (1M requests)$0.01/monthCloudFront (10GB transfer)$0.85/monthCodePipeline$1.00/monthCodeBuild (10 builds × 2min)$0.10/monthTotal~$2.08/month&lt;br&gt;
vs ECS + nginx + ALB: ~$50/month minimum.&lt;/p&gt;

&lt;p&gt;Error Reference&lt;br&gt;
ErrorCauseFixApp breaks on page refreshSPA routing fix missingAdd 403/404 → /index.html in CloudFront error pagess3:PutObject deniedFrontend bucket not in CodePipeline role S3 policyAdd both bucket ARNs to resource listYAML_FILE_ERROR: Expected Commands[N] to be string typeBackslash continuation in buildspec.ymlOne command per lineOld files served after deployNo CloudFront cache invalidationAdd create-invalidation --paths "/*" to post_buildBuild uses different deps than localnpm install used instead of npm ciAlways npm ci in CI&lt;/p&gt;

&lt;p&gt;What's Next&lt;br&gt;
Custom domain: ACM certificate + Route 53 → app.yourdomain.com → your CloudFront distribution. HTTPS is automatic.&lt;br&gt;
Multi-environment:&lt;br&gt;
merge to main → deploy STAGING → manual approval → deploy PRODUCTION&lt;br&gt;
Two separate S3 + CloudFront pairs. Same CodePipeline, two deploy stages.&lt;br&gt;
Cache headers: fine-grained control — index.html never cached, hashed JS/CSS cached for a year.&lt;/p&gt;

&lt;p&gt;This is the frontend half of a full-stack AWS deployment. The backend — Spring Boot on ECS with CodePipeline — is the companion article.&lt;br&gt;
Drop a ❤️ if this helped. Questions in the comments.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>react</category>
      <category>devops</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Deploying Spring Boot to AWS ECS with CI/CD — The Complete No-BS Guide published: true</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sun, 03 May 2026 13:50:55 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/deploying-spring-boot-to-aws-ecs-with-cicd-the-complete-no-bs-guidepublished-true-2gdf</link>
      <guid>https://forem.com/rajaramyadav/deploying-spring-boot-to-aws-ecs-with-cicd-the-complete-no-bs-guidepublished-true-2gdf</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Every IAM error, every Docker rate limit, every Testcontainers gotcha documented. The guide I wish existed when I started.&lt;br&gt;
Most AWS deployment guides show you the happy path. This one shows you the whole road — including the potholes.&lt;br&gt;
This is the complete walkthrough for deploying a Spring Boot app to AWS ECS with a real CI/CD pipeline. GitHub Actions for testing. CodePipeline + CodeBuild for deployment. Every IAM permission. Every command. Every error documented.&lt;br&gt;
What You're Building&lt;br&gt;
GitHub PR  → GitHub Actions → tests pass → PR shows ✅&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GitHub push to main → CodePipeline&lt;br&gt;
    → CodeBuild (test + build JAR + build Docker image + push to ECR)&lt;br&gt;
    → ECS rolling deploy (zero downtime)&lt;br&gt;
    → SNS email notification&lt;br&gt;
Stack:&lt;/p&gt;

&lt;p&gt;Spring Boot 4.0.5 / Java 21 / Maven&lt;br&gt;
MySQL 8 on Amazon RDS&lt;br&gt;
Docker → Amazon ECR&lt;br&gt;
Amazon ECS&lt;br&gt;
GitHub Actions + AWS CodePipeline + CodeBuild&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp4uv90lvrzhtou7ts0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp4uv90lvrzhtou7ts0c.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 1 — RDS MySQL&lt;br&gt;
Create a db.t3.micro instance (free tier eligible):&lt;br&gt;
Engine        : MySQL 8.0&lt;br&gt;
Template      : Free tier&lt;br&gt;
Public access : Yes (dev only — lock this down in production)&lt;br&gt;
Port          : 3306&lt;br&gt;
Security group: allow 3306 from your local IP and your ECS security group.&lt;br&gt;
properties# application.properties&lt;br&gt;
spring.datasource.url=jdbc:mysql://:3306/yourdb&lt;br&gt;
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver&lt;br&gt;
spring.flyway.enabled=true&lt;br&gt;
spring.flyway.locations=classpath:db/migration&lt;br&gt;
spring.jpa.hibernate.ddl-auto=none&lt;br&gt;
Use Flyway for migrations. ddl-auto=none means Hibernate never touches your schema — Flyway owns it. This prevents accidental data loss.&lt;/p&gt;

&lt;p&gt;Step 2 — Docker Setup&lt;br&gt;
ECR Public for Base Images (Critical)&lt;br&gt;
If you use Docker Hub images in CodeBuild, you will hit this:&lt;br&gt;
429 Too Many Requests&lt;br&gt;
toomanyrequests: You have reached your unauthenticated pull rate limit&lt;br&gt;
Docker Hub allows 100 anonymous pulls per 6 hours per IP. AWS CodeBuild shares IPs. Your builds randomly fail. The fix is ECR Public — AWS's mirror of Docker Hub official images with no rate limits inside AWS.&lt;br&gt;
dockerfile# ❌ Breaks randomly in CodeBuild&lt;br&gt;
FROM maven:3.9-eclipse-temurin-21 AS builder&lt;br&gt;
FROM eclipse-temurin:21-jdk-alpine&lt;/p&gt;
&lt;h1&gt;
  
  
  ✅ No rate limits inside AWS
&lt;/h1&gt;

&lt;p&gt;FROM public.ecr.aws/docker/library/maven:3.9-eclipse-temurin-21 AS builder&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;br&gt;
COPY pom.xml .&lt;br&gt;
RUN mvn dependency:go-offline -q&lt;/p&gt;

&lt;p&gt;COPY src ./src&lt;br&gt;
RUN mvn package -DskipTests -q&lt;/p&gt;

&lt;p&gt;FROM public.ecr.aws/docker/library/eclipse-temurin:21-jdk-alpine&lt;/p&gt;

&lt;p&gt;WORKDIR /app&lt;br&gt;
COPY --from=builder /app/target/*.jar app.jar&lt;/p&gt;

&lt;p&gt;EXPOSE 8080&lt;br&gt;
ENTRYPOINT ["java", "-jar", "app.jar"]&lt;/p&gt;

&lt;p&gt;&lt;em&gt;COPY --from=builder must match the AS builder name exactly. Using --from=build when the stage is named builder gives a cryptic error: "failed to resolve source metadata for docker.io/library/build:latest"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create ECR Repository&lt;br&gt;
bashaws ecr create-repository \&lt;br&gt;
  --repository-name your-app-backend \&lt;br&gt;
  --image-scanning-configuration scanOnPush=true \&lt;br&gt;
  --region us-east-1&lt;/p&gt;

&lt;p&gt;Step 3 — Testing with Testcontainers&lt;br&gt;
Your app needs real MySQL. H2 won't work with MySQL-specific Flyway migrations. Testcontainers starts a real MySQL Docker container during test execution.&lt;br&gt;
pom.xml — Spring Boot 4.x Gotcha&lt;br&gt;
These artifact IDs don't exist in Spring Boot 4.x:&lt;br&gt;
xml&amp;lt;!-- ❌ WRONG — these don't exist --&amp;gt;&lt;br&gt;
spring-boot-starter-webmvc-test&lt;br&gt;
spring-boot-starter-data-jpa-test&lt;br&gt;
Correct dependencies:&lt;br&gt;
xml&amp;lt;!-- ✅ One artifact covers everything: JUnit 5, Mockito, AssertJ, MockMvc --&amp;gt;&lt;br&gt;
&lt;br&gt;
  org.springframework.boot&lt;br&gt;
  spring-boot-starter-test&lt;br&gt;
  test&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  org.springframework.security&lt;br&gt;
  spring-security-test&lt;br&gt;
  test&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  org.springframework.boot&lt;br&gt;
  spring-boot-testcontainers&lt;br&gt;
  test&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  org.testcontainers&lt;br&gt;
  junit-jupiter&lt;br&gt;
  test&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  org.testcontainers&lt;br&gt;
  mysql&lt;br&gt;
  test&lt;br&gt;
&lt;br&gt;
application-test.properties&lt;br&gt;
properties# jdbc:tc: = Testcontainers intercepts this and starts a Docker MySQL container&lt;br&gt;
spring.datasource.url=jdbc:tc:mysql:8.0:///yourapp_test&lt;br&gt;
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver&lt;br&gt;
spring.jpa.hibernate.ddl-auto=none&lt;/p&gt;

&lt;h1&gt;
  
  
  Flyway runs real migrations in test context
&lt;/h1&gt;

&lt;p&gt;spring.flyway.enabled=true&lt;/p&gt;

&lt;p&gt;spring.mail.host=localhost&lt;br&gt;
spring.mail.port=3025&lt;br&gt;
app.jwt.secret=test-secret-key-for-ci-only&lt;br&gt;
logging.level.root=WARN&lt;br&gt;
Unit Test (Pure Mockito — ~200ms, no Spring)&lt;br&gt;
java@ExtendWith(MockitoExtension.class)&lt;br&gt;
class EmployeeServiceTest {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Mock private EmployeeRepository employeeRepository;
@Mock private RoleRepository roleRepository;
@Mock private PasswordEncoder passwordEncoder;
@Mock private IdGeneratorService idGeneratorService;
@Mock private MailService mailService;

@InjectMocks
private EmployeeServiceImpl employeeService;

@Test
@DisplayName("register() → throws when email already exists")
void register_duplicateEmail_throwsException() {
    given(employeeRepository.existsByEmail("john@example.com")).willReturn(true);

    assertThatThrownBy(() -&amp;gt; employeeService.register(buildRequest()))
        .isInstanceOf(DuplicateResourceException.class);

    verify(employeeRepository, never()).save(any());
    verify(mailService, never()).sendRegistrationEmail(any(), any(), any(), any());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Controller Slice Test&lt;br&gt;
java@WebMvcTest(EmployeeController.class)&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/import"&gt;@import&lt;/a&gt;(TestSecurityConfig.class) // disables JWT filter&lt;br&gt;
class EmployeeControllerTest {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Autowired MockMvc mockMvc;
@Autowired ObjectMapper objectMapper;
@MockitoBean EmployeeService employeeService; // @MockitoBean in Spring Boot 4, not @MockBean

@Test
void register_validRequest_returns201() throws Exception {
    given(employeeService.register(any())).willReturn(mockResponse());

    mockMvc.perform(post("/api/employees/register")
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(validRequest())))
        .andExpect(status().isCreated())
        .andExpect(jsonPath("$.data.generatedId").value("EMP-001"));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;Step 4 — GitHub Actions&lt;br&gt;
yaml# .github/workflows/ci.yml&lt;br&gt;
name: Backend CI&lt;/p&gt;

&lt;p&gt;on:&lt;br&gt;
  pull_request:&lt;br&gt;
    branches: [ main ]&lt;br&gt;
  push:&lt;br&gt;
    branches: [ main ]&lt;/p&gt;

&lt;p&gt;jobs:&lt;br&gt;
  test:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - uses: actions/checkout@v4&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  - uses: actions/setup-java@v4
    with:
      java-version: '21'
      distribution: 'temurin'
      cache: maven

  - name: Run tests
    run: mvn test -Dspring.profiles.active=test --no-transfer-progress

  - name: Upload test report
    if: always()
    uses: actions/upload-artifact@v4
    with:
      name: test-report
      path: target/surefire-reports/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Docker is pre-installed on ubuntu-latest. Testcontainers works out of the box.&lt;/p&gt;

&lt;p&gt;Step 5 — CodePipeline + CodeBuild&lt;br&gt;
IAM Roles — Get These Right First&lt;br&gt;
CodePipeline role — the most important permission is iam:PassRole:&lt;br&gt;
json{&lt;br&gt;
  "Statement": [&lt;br&gt;
    {&lt;br&gt;
      "Sid": "PassRoleToECS",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": "iam:PassRole",&lt;br&gt;
      "Resource": [&lt;br&gt;
        "arn:aws:iam::ACCOUNT:role/ecsTaskExecutionRole",&lt;br&gt;
        "arn:aws:iam::ACCOUNT:role/YOUR_ACTUAL_TASK_ROLE"&lt;br&gt;
      ]&lt;br&gt;
    }&lt;br&gt;
  ]&lt;br&gt;
}&lt;br&gt;
Find your exact role ARNs first:&lt;br&gt;
bashaws ecs describe-task-definition \&lt;br&gt;
  --task-definition your-task-definition \&lt;br&gt;
  --query 'taskDefinition.{exec:executionRoleArn,task:taskRoleArn}'&lt;br&gt;
Both ARNs must be in the PassRole resource list. The role you assume in ECS is often NOT named ecsTaskExecutionRole — it has a generated name. Check before writing the policy.&lt;br&gt;
CodeBuild role — needs ECR Public auth (for rate-limit-free base image pulls):&lt;br&gt;
json{&lt;br&gt;
  "Sid": "ECRPublicAuth",&lt;br&gt;
  "Effect": "Allow",&lt;br&gt;
  "Action": [&lt;br&gt;
    "ecr-public:GetAuthorizationToken",&lt;br&gt;
    "sts:GetServiceBearerToken"&lt;br&gt;
  ],&lt;br&gt;
  "Resource": "*"&lt;br&gt;
}&lt;br&gt;
buildspec.yml&lt;br&gt;
yamlversion: 0.2&lt;/p&gt;

&lt;p&gt;env:&lt;br&gt;
  variables:&lt;br&gt;
    AWS_REGION: "us-east-1"&lt;br&gt;
    ECR_REPOSITORY_URI: "123456789012.dkr.ecr.us-east-1.amazonaws.com/your-app"&lt;br&gt;
    ECS_CLUSTER: "your-cluster"&lt;br&gt;
    ECS_SERVICE: "your-service"&lt;br&gt;
    CONTAINER_NAME: "backend"&lt;/p&gt;

&lt;p&gt;phases:&lt;br&gt;
  install:&lt;br&gt;
    runtime-versions:&lt;br&gt;
      java: corretto21&lt;/p&gt;

&lt;p&gt;pre_build:&lt;br&gt;
    commands:&lt;br&gt;
      - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPOSITORY_URI&lt;br&gt;
      - aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws&lt;br&gt;
      - IMAGE_TAG="${CODEBUILD_RESOLVED_SOURCE_VERSION:0:7}"&lt;br&gt;
      - mvn test -Dspring.profiles.active=test --no-transfer-progress&lt;/p&gt;

&lt;p&gt;build:&lt;br&gt;
    commands:&lt;br&gt;
      - mvn package -DskipTests --no-transfer-progress&lt;br&gt;
      - docker build -t $ECR_REPOSITORY_URI:$IMAGE_TAG .&lt;br&gt;
      - docker tag $ECR_REPOSITORY_URI:$IMAGE_TAG $ECR_REPOSITORY_URI:latest&lt;/p&gt;

&lt;p&gt;post_build:&lt;br&gt;
    commands:&lt;br&gt;
      - docker push $ECR_REPOSITORY_URI:$IMAGE_TAG&lt;br&gt;
      - docker push $ECR_REPOSITORY_URI:latest&lt;br&gt;
      - FULL_IMAGE="$ECR_REPOSITORY_URI:$IMAGE_TAG"&lt;br&gt;
      - echo -n "[{\"name\":\"$CONTAINER_NAME\",\"imageUri\":\"$FULL_IMAGE\"}]" &amp;gt; imagedefinitions.json&lt;br&gt;
      - python3 -c "import json; json.load(open('imagedefinitions.json')); print('JSON valid')"&lt;br&gt;
      - cat imagedefinitions.json&lt;/p&gt;

&lt;p&gt;artifacts:&lt;br&gt;
  files:&lt;br&gt;
    - imagedefinitions.json&lt;br&gt;
  discard-paths: yes&lt;br&gt;
Two YAML Gotchas That Cost Hours&lt;br&gt;
Gotcha 1 — No backslash continuation:&lt;br&gt;
yaml# ❌ CodeBuild YAML parser rejects this — "Expected Commands[N] to be string type"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aws cloudfront create-invalidation \
--distribution-id $ID \
--paths "/*"&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ✅ One line
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;aws cloudfront create-invalidation --distribution-id $ID --paths "/*"
Gotcha 2 — imagedefinitions.json spaces:
yaml# ❌ printf with continuation injects spaces → "The image URI contains invalid characters"&lt;/li&gt;
&lt;li&gt;printf '[{"name":"%s","imageUri":"%s"}]' \
$CONTAINER_NAME \
$ECR_REPOSITORY_URI:$IMAGE_TAG \
&amp;gt; imagedefinitions.json&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ✅ echo -n single line — no spaces, no newline
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;FULL_IMAGE="$ECR_REPOSITORY_URI:$IMAGE_TAG"&lt;/li&gt;
&lt;li&gt;echo -n "[{\"name\":\"$CONTAINER_NAME\",\"imageUri\":\"$FULL_IMAGE\"}]" &amp;gt; imagedefinitions.json
CodeBuild Project — Privileged Mode
Without this, Testcontainers fails with Could not find a valid Docker environment:
CodeBuild → project → Edit → Environment → ✅ Privileged mode: ON
CodeStar Connection — Read This Carefully
After creating the connection and doing OAuth, you must ALSO install the GitHub App:
github.com/apps/aws-connector-for-github/installations/new
→ Select your account/org
→ Grant access to your repositories
→ Install
Without installation, every run fails:
[GitHub] No Branch [main] found for FullRepositoryName [user/repo]
The branch exists. The connection shows "Available". The installation step is just missing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common Error Reference&lt;br&gt;
ErrorRoot causeFix429 Too Many Requests from Docker HubAnonymous pull rate limitUse public.ecr.aws/docker/library/ base imagesiam:PassRole deniedMissing role ARN in PassRole resourceRun describe-task-definition to find exact ARNsExpected Commands[N] to be string typeBackslash continuation in YAMLOne command per lineimage URI contains invalid charactersSpaces in imagedefinitions.jsonUse echo -n on single lineNo Branch [main] foundGitHub App not installed (only authorized)Install at github.com/apps/aws-connector-for-githubCould not find valid Docker environmentPrivileged mode off in CodeBuildEnable Privileged mode in project settingsSwagger 403 after deploy/v3/api-docs/** not in security whitelistAdd to PUBLIC_ENDPOINTS alongside /api-docs/**&lt;/p&gt;

&lt;p&gt;The Swagger 403 Nobody Warns You About&lt;br&gt;
After deployment, Swagger UI loads but shows "Failed to load remote configuration."&lt;br&gt;
You have /api-docs/** in your security whitelist. That should be enough. It's not.&lt;br&gt;
SpringDoc internally calls /v3/api-docs/swagger-config on every Swagger UI load — regardless of your custom springdoc.api-docs.path. It's hardcoded in the library.&lt;br&gt;
javapublic static final String[] PUBLIC_ENDPOINTS = {&lt;br&gt;
    "/swagger-ui/&lt;strong&gt;",&lt;br&gt;
    "/swagger-ui.html",&lt;br&gt;
    "/api-docs/&lt;/strong&gt;",&lt;br&gt;
    "/v3/api-docs/**",   // ← add this&lt;br&gt;
    "/actuator/health",&lt;br&gt;
    "/error"&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;That's the complete backend setup. For the frontend companion — React/Vite deployed to S3 + CloudFront with the same CodePipeline approach — check my profile.&lt;/p&gt;

&lt;p&gt;Drop a ❤️ if this saved you time. Questions? Drop them in the comments.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>springboot</category>
      <category>devops</category>
    </item>
    <item>
      <title>I Built an AI Resume Optimizer While Job Hunting — Here’s What Actually Worked</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Wed, 29 Apr 2026 20:18:49 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/i-built-an-ai-resume-optimizer-while-job-hunting-heres-what-actually-worked-5gnc</link>
      <guid>https://forem.com/rajaramyadav/i-built-an-ai-resume-optimizer-while-job-hunting-heres-what-actually-worked-5gnc</guid>
      <description>&lt;p&gt;Try it: &lt;a href="https://shortlisted-one.vercel.app/" rel="noopener noreferrer"&gt;https://shortlisted-one.vercel.app/&lt;/a&gt;&lt;br&gt;
I got tired of rewriting my resume for every job application.&lt;/p&gt;

&lt;p&gt;Different companies, different keywords, slightly different expectations—and somehow your resume needs to match all of them.&lt;/p&gt;

&lt;p&gt;So I built Shortlisted: a tool that takes your resume + a job description and rewrites it into a tailored version.&lt;/p&gt;

&lt;p&gt;What I didn’t expect?&lt;br&gt;
I’d spend more time debugging async pipelines than building the AI itself.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;🚀 What the product does&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
The flow is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload your resume (PDF/DOCX)&lt;/li&gt;
&lt;li&gt;Paste a job description (or URL)&lt;/li&gt;
&lt;li&gt;Get a tailored resume ready to export&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the hood, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parses your resume&lt;/li&gt;
&lt;li&gt;Extracts and analyzes the job description&lt;/li&gt;
&lt;li&gt;Matches and rewrites content&lt;/li&gt;
&lt;li&gt;Exports a polished PDF/DOCX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;🧠 Tech Stack (kept simple)&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: Next.js (App Router), TypeScript, Tailwind&lt;/li&gt;
&lt;li&gt;Backend: FastAPI + PostgreSQL&lt;/li&gt;
&lt;li&gt;Async processing: Redis + Celery&lt;/li&gt;
&lt;li&gt;Auth &amp;amp; billing: Clerk + Stripe (test mode)&lt;/li&gt;
&lt;li&gt;Analytics: PostHog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;⚙️ Architecture (high-level)&lt;br&gt;
*&lt;/em&gt;&lt;code&gt;Client → API → Queue → Worker → Storage&lt;br&gt;
        ↑                        ↓&lt;br&gt;
        └──── Status API ←──────┘&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload route → handles resume ingestion + parsing&lt;/li&gt;
&lt;li&gt;Job route → processes the job description&lt;/li&gt;
&lt;li&gt;Optimize route → kicks off async pipeline&lt;/li&gt;
&lt;li&gt;Worker → runs rewrite, match, export stages&lt;/li&gt;
&lt;li&gt;Status endpoint → returns granular progress for UI polling
**🔥 What was harder than expected
**1. Async pipelines are easy to start, hard to trust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Celery works great… until it doesn’t.&lt;/p&gt;

&lt;p&gt;I ran into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jobs getting stuck in “pending”&lt;/li&gt;
&lt;li&gt;Duplicate executions on retries&lt;/li&gt;
&lt;li&gt;Partial failures mid-pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fixes involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding explicit job state tracking in the DB&lt;/li&gt;
&lt;li&gt;Making tasks idempotent&lt;/li&gt;
&lt;li&gt;Breaking the pipeline into smaller, traceable steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lesson: “it works locally” means nothing for async systems&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;2. Resume parsing is messier than you think&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Resumes are wildly inconsistent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different formats (PDF vs DOCX)&lt;/li&gt;
&lt;li&gt;Layout-heavy designs&lt;/li&gt;
&lt;li&gt;Broken text extraction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regex → fast but brittle&lt;/li&gt;
&lt;li&gt;LLM parsing → flexible but expensive&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hybrid → best balance&lt;br&gt;
Final approach:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Structured extraction where possible&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LLM fallback for messy sections&lt;br&gt;
*&lt;em&gt;3. UX for async systems is underrated&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Users don’t care that you’re using Redis or Celery.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They care that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It doesn’t feel stuck&lt;/li&gt;
&lt;li&gt;They know what’s happening&lt;/li&gt;
&lt;li&gt;Progress feels real&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step-level progress updates (not just “loading…”)&lt;/li&gt;
&lt;li&gt;Backend-driven status tracking&lt;/li&gt;
&lt;li&gt;Polling with meaningful states (not fake progress bars)
*&lt;em&gt;4. LLM reliability is a system design problem
*&lt;/em&gt;
It’s not just “call OpenAI and done”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API failures&lt;/li&gt;
&lt;li&gt;Rate limits&lt;/li&gt;
&lt;li&gt;Cost control&lt;/li&gt;
&lt;li&gt;Output inconsistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What helped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provider fallback logic&lt;/li&gt;
&lt;li&gt;Key validation before execution&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Structured prompts + validation&lt;br&gt;
&lt;strong&gt;📈 What I’d improve next&lt;br&gt;
**&lt;/strong&gt;Product-side&lt;br&gt;
**&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Template system for multiple resume styles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Section-level editing (human-in-the-loop)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better analytics: upload → export conversion&lt;br&gt;
*&lt;em&gt;Engineering-side&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stronger retry + idempotency guarantees&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Job deduplication (same resume + JD = no re-run)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Smarter caching for repeated job descriptions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cost tracking per pipeline run&lt;br&gt;
*&lt;em&gt;💡 What I learned&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Async systems are where most real complexity lives&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LLMs are the easy part—reliability is the hard part&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Good UX matters more than clever backend design&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“AI product” really means “distributed systems + AI”&lt;br&gt;
*&lt;em&gt;🤔 Open questions&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
If you’re building something similar:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How are you handling async job reliability?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are you using Celery, or something like Temporal?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do you manage LLM fallbacks and cost control?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Would love to compare approaches.&lt;/p&gt;

&lt;p&gt;If you’ve built anything in this space, drop a link—I’m curious how others are solving this.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>career</category>
    </item>
    <item>
      <title>Learning Javascript</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Wed, 30 Sep 2020 18:20:44 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/learning-javascript-2d0i</link>
      <guid>https://forem.com/rajaramyadav/learning-javascript-2d0i</guid>
      <description>&lt;p&gt;Hello fellas,&lt;br&gt;
Hope you are doing great.&lt;/p&gt;

&lt;p&gt;The thing is I have recently started learnig front-end from very basic. And I learnt HTML/CSS and built some basic project pages using them. You can check in my profile.&lt;/p&gt;

&lt;p&gt;Now I started learning ##Javascript.&lt;br&gt;
I watched basic tutorial on youtube of basic output perform conditional and logical operation.&lt;/p&gt;

&lt;p&gt;Now what step should I take??&lt;br&gt;
Suggestions and comments.&lt;/p&gt;

&lt;p&gt;Thanks.&lt;br&gt;
Let's grow together.&lt;br&gt;
DEVSOR happy coding.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>javascript</category>
      <category>help</category>
    </item>
    <item>
      <title>Model of official webpage</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Wed, 30 Sep 2020 18:10:45 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/model-of-official-webpage-2040</link>
      <guid>https://forem.com/rajaramyadav/model-of-official-webpage-2040</guid>
      <description>&lt;p&gt;Hello guys,&lt;br&gt;
I had not been recently active due to my mid-sem exam.&lt;/p&gt;

&lt;p&gt;But interests come back and knock you again right.&lt;br&gt;
So I am here with new webpage model to enhance and boost my HTML and css knowledge.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpxmjwptr99psi1d13xmd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpxmjwptr99psi1d13xmd.jpg" alt="Alt Text" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Link to my project is &lt;a href="https://jsfiddle.net/2k4r6ocw/" rel="noopener noreferrer"&gt;this&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the suggestions and comments are highly appreciated.&lt;br&gt;
Let's grow together.&lt;br&gt;
Code DEVSOR&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Beginner Webpage form</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Thu, 03 Sep 2020 15:37:33 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/a-responsive-form-1med</link>
      <guid>https://forem.com/rajaramyadav/a-responsive-form-1med</guid>
      <description>&lt;p&gt;Hello guys,&lt;br&gt;
Today I created a responsive from as a project of HTML/CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6xllr08d0g0o56y54hkp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6xllr08d0g0o56y54hkp.jpg" alt="Alt Text" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am learning htmls/css as I have other works to di I can't give much time there.&lt;br&gt;
But Learning is a continous process.&lt;br&gt;
We need to learn things side by side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Link
&lt;/h2&gt;

&lt;p&gt;You can see my project &lt;a href="https://jsfiddle.net/417age5k/1/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hope You guys like it.
&lt;/h2&gt;

&lt;p&gt;And it would be great help if you comment or suggest anything.&lt;br&gt;
Thanks.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>css</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Created Webpage Model</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Tue, 01 Sep 2020 17:24:48 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/created-webpage-model-55if</link>
      <guid>https://forem.com/rajaramyadav/created-webpage-model-55if</guid>
      <description>&lt;p&gt;Today as a project, I just completed my webpage model using HTML and CSS.&lt;br&gt;
It was a good journey learning HTML and CSS.&lt;br&gt;
I learnt things from basics and with increasing practise and projects I believe that I would be able to go further and improve it through time.&lt;/p&gt;

&lt;p&gt;Indeed it was good and after completion when I saw my page I felt happy .&lt;/p&gt;

&lt;p&gt;Take little time to view it.&lt;br&gt;
Here is link for that.&lt;br&gt;
&lt;a href="https://jsfiddle.net/gcux9s5d/" rel="noopener noreferrer"&gt;https://jsfiddle.net/gcux9s5d/&lt;/a&gt;&lt;br&gt;
All the improvements and suggestions are highly recommended.&lt;br&gt;
Thanks.&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Learning data structure and Algorithm</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sun, 30 Aug 2020 14:37:04 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/learning-data-structure-and-algorithm-26ml</link>
      <guid>https://forem.com/rajaramyadav/learning-data-structure-and-algorithm-26ml</guid>
      <description>&lt;p&gt;I am in second semester and I have got this subject in my semester. I am struggling with Data and structure, can't understand concepts,programs. In dilemma of which path to choose to excel in this as we all know how important this subject is for CSE guy.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>algorithms</category>
      <category>datastructure</category>
      <category>help</category>
    </item>
    <item>
      <title>Starting journey on CSS</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sun, 30 Aug 2020 10:55:24 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/starting-journey-on-css-2n4e</link>
      <guid>https://forem.com/rajaramyadav/starting-journey-on-css-2n4e</guid>
      <description>&lt;p&gt;Hello guys, &lt;/p&gt;

&lt;p&gt;So you can build skeleton of page using html.But you need extra to decorate it.&lt;br&gt;
It's like extra items of dinner that makes dinner tastier.&lt;/p&gt;

&lt;p&gt;Yup guys, I am talking about CSS&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi6e1qt034qre5tnxp0lv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi6e1qt034qre5tnxp0lv.jpg" alt="Alt Text" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CSS makes your website decorative as it comes with many decorating features.&lt;br&gt;
I has just started my journey on 28/08/2020 on this.&lt;br&gt;
Yes, I don't want my site look just skeleton I want to dress it make it colorful and attractive that's what CSS do.&lt;/p&gt;

&lt;p&gt;Basically you can use CSS in 3 ways.&lt;br&gt;
*Inline,*Outline(Internal) and *Creating seperate documents(i.e External).&lt;br&gt;
So many developers choose seperate file for CSS as they don't want to mix things and increase readability of program.&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Learning html</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sun, 30 Aug 2020 10:48:19 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/learning-html-2h9d</link>
      <guid>https://forem.com/rajaramyadav/learning-html-2h9d</guid>
      <description>&lt;p&gt;I started my HTML journey on 23/08/2020 and i completed my basic tutorials basic programs on 27/08/2020.&lt;br&gt;
I felt html as such an easy platform you can learn it in few hours.&lt;br&gt;
Basically you need is:&lt;br&gt;
&amp;lt;!DOCTYPE HTML&amp;gt; to specify it as document.&lt;br&gt;
then there are two types of syntax &lt;br&gt;
1) Comes with opening and closing both like&lt;br&gt;
 -specifying html&lt;br&gt;
&lt;/p&gt; -specify head section&lt;br&gt;
&lt;a href=""&gt;&lt;/a&gt; -to link another page or site&lt;br&gt;
&lt;b&gt;&lt;/b&gt; - bold &lt;br&gt;
 -ittalic&lt;br&gt;
   -paragraph&lt;br&gt;
&lt;h1&gt;&lt;/h1&gt;  -heading or bigger size&lt;br&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;/table&gt;&lt;/div&gt; -specify table&lt;br&gt;
  - division or a section &lt;br&gt;
&lt;ul&gt;&lt;/ul&gt; -unordered list&lt;br&gt;
&lt;ol&gt;&lt;/ol&gt; -ordered list&lt;br&gt;
&lt;li&gt; -list element
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpfamtk6rj7bvgaun51yx.jpg" alt="Alt Text" width="800" height="800"&gt;

&lt;p&gt;and other many more i have only mentioned basics.&lt;br&gt;
2) comes singly&lt;br&gt;
 -meta tag with charset&lt;br&gt;
&lt;br&gt; -breaking line&lt;br&gt;
&lt;/p&gt;
 -horizontal ruler

&lt;p&gt;We can insert inages with img tag and many more are there if you want to learn can easily learn from W3schools and freecodecamp.&lt;br&gt;
For further query Can contact me.&lt;br&gt;
Thanks&lt;/p&gt;
&lt;/li&gt;

</description>
      <category>html</category>
    </item>
    <item>
      <title>Starting new journey on dev web</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sun, 30 Aug 2020 10:35:34 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/starting-new-journey-on-dev-web-1n55</link>
      <guid>https://forem.com/rajaramyadav/starting-new-journey-on-dev-web-1n55</guid>
      <description>&lt;p&gt;Hello everyone out there,&lt;br&gt;
Today I am going to write about my plan for now.&lt;br&gt;
I recently took a new domain for my interest.&lt;br&gt;
I want to learn front-end developer.&lt;br&gt;
So , I started from basic. I started to learn it from 23/08/2020.&lt;br&gt;
In these days i learnt HTML5 and can build basic framework of webpages for now.&lt;/p&gt;

&lt;p&gt;I started my journey chasing my interest.&lt;br&gt;
Are there anyone out there having same interest and in basic level now can join my journey.&lt;/p&gt;

&lt;p&gt;And yaa I am learning through Youtube tutorials mostly, W3schools and freecodecamp.&lt;br&gt;
Thanks for now &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Python</title>
      <dc:creator>Rajaram Yadav</dc:creator>
      <pubDate>Sat, 22 Aug 2020 18:03:26 +0000</pubDate>
      <link>https://forem.com/rajaramyadav/python-i36</link>
      <guid>https://forem.com/rajaramyadav/python-i36</guid>
      <description>&lt;p&gt;So if any of you want to get into programming. Then it's not that hard.&lt;/p&gt;

&lt;p&gt;Basic thing you need is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Knowledge of English&lt;/li&gt;
&lt;li&gt;Good at logical thinking&lt;/li&gt;
&lt;li&gt;And Basic math stuffs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can search &lt;br&gt;
online,&lt;br&gt;
watch tutorials on youtube&lt;br&gt;
Learn through basics by printing hello world. And then you will get into other deep technical things.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>python</category>
    </item>
  </channel>
</rss>
