What a Proof-of-Concept Pipeline Actually Needs

The four things a stage-one CI/CD pipeline needs and nothing more, plus why I stopped feeling guilty about hardcoding a bucket name.

September 2, 20265 min read1 / 4

Knowing you're allowed to keep stage one simple is one thing. Knowing exactly what "simple" includes is another.

I used to stack tests, linting, multiple environments, and approvals onto every pipeline from day one, before a single real user touched the thing. Then I watched a proof-of-concept take longer to ship than the feature it was supposed to validate.

A proof-of-concept pipeline exists to answer one question: does this work end to end? Everything that doesn't serve that question is a cost you're paying too early.

The real cost of doing it "right" too soon

I once worked somewhere that compared its CI bill against its actual production hosting bill for the same project. CI cost more.

Read that again. The pipeline built to ship the product was more expensive than running the product itself. Nobody set out to build that. It happened one "just in case" step at a time: an extra test suite here, a security scan there, a matrix build across three Node versions nobody used.

None of those steps were wrong alone. Stacked together, before the project earned that level of care, they turned CI/CD into its own product instead of a way to ship one.

CI/CD is supposed to be a complement to what you're building, not a system that outweighs it. If your pipeline costs more attention than the thing it deploys, that's not rigor. That's a pipeline solving problems the project doesn't have yet.

What a proof-of-concept pipeline needs: four ingredients, nothing else

Strip a proof-of-concept pipeline down to what it actually needs, and you're left with four things:

  1. Somewhere to put the code. A repo, pushed straight to the main branch. No branch protection, no required reviews, not yet.
  2. Somewhere to host the result. For a static frontend, that's an S3 bucket with static website hosting turned on.
  3. A way for CI to reach that host. An IAM user with programmatic access, scoped to that one bucket.
  4. Somewhere to store that access safely. GitHub repo secrets, so the credentials never sit in the workflow file itself.

That's the whole list. Not "a good list to start with." The whole list.

Ask three engineers to build this and you'll get three different pipelines, because "just get it deployed" leaves a lot of room for opinion. Writing the four ingredients down first is what keeps that room from turning into scope creep.

What the pipeline actually looks like

Once those four pieces exist, the workflow itself splits cleanly into two jobs: one that builds, one that ships.

A proof-of-concept pipeline: a build job and a deploy job connected by a single artifact ExpandA proof-of-concept pipeline: a build job and a deploy job connected by a single artifact

The build job does the part that has nothing to do with AWS:

YAML
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npm run build - uses: actions/upload-artifact@v4 with: name: dist path: dist/

The deploy job does the part that only cares about getting that build onto S3:

YAML
deploy: needs: build runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4 with: name: dist path: dist/ - uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - run: aws s3 sync dist/ s3://your-poc-bucket --delete

Every GitHub Actions job runs on its own fresh virtual machine. Nothing survives between jobs except what you explicitly hand off, which is why the artifact step exists: the one bridge between "here's the built code" and "here's where it lives now."

That means the build job can't leak a leftover file into the deploy job, and the deploy job can't accidentally ship something slightly different from what was tested. One artifact, one source of truth, passed across the gap on purpose.

I ran into a version of this same "keep the source of truth in one place" problem designing request DTOs for a booking system: the fewer places a piece of data can drift, the fewer bugs you get from two copies quietly disagreeing.

Where the line actually is

None of this means skipping best practices forever. It means sequencing them.

A proof-of-concept doesn't need tests, multiple environments, or approval gates, because none of those change whether the core idea works. They start to matter the moment a second person needs to trust the pipeline without asking you first, exactly where the next stage in this series picks up.

Until then, four ingredients and two jobs is a complete, correct answer. Not a shortcut you'll apologize for later.

The Essentials

  1. A proof-of-concept pipeline answers one question: does this work end to end? Anything that doesn't serve that question is a cost paid too early.
  2. Four ingredients are all it needs. A repo on main, a hosting target, a scoped IAM user, and repo secrets to hold the credentials.
  3. CI/CD should complement the project, not outweigh it. A pipeline that costs more than the thing it ships is solving problems you don't have yet.
  4. Split build and deploy into separate jobs. Each job runs on a fresh VM, so only the uploaded artifact crosses between them.
  5. One artifact is the single source of truth. It's what stops the deployed code from silently drifting from what was actually built.

Further Reading and Watching