Building CI/CD pipelines with GitHub Actions: Automation strategies to protect the “spirit” of developers

As a professional editor for technology blogs and gadget reviews, I reviewed the draft you submitted.

The original text is very emotional and has a wonderful structure that reflects the developer's pain. While taking advantage of these strengths, we have improved the readability of a technical blog, accurately highlighting information, and adding the essence of encouraging readers to take action.

The following is the revised and proofread [finished version].


Building CI/CD pipelines with GitHub Actions: Automation strategies to protect the “spirit” of developers

Silence late at night. I write development notes while muttering to myself at my desk, where only the light from the monitor reflects.

Today we will be talking about "GitHub Actions". This is not just an introduction to the functionality of the tool. I would like to write here some practical considerations for individual developers to free themselves from the unavoidable "deployment hell" routine and focus on creative work.


In the end, I do CI/CD to "protect my spirit."

"Build again manually, SSH to the server, and hit the command..."
How many times have we repeated this simple process?

In personal development, one thing that cannot be avoided is the "deployment ritual." Write code, commit, push. If the subsequent process consists of a manual chain, it is no longer "development" but more like "transportation work."

Particularly when multiple people are involved in development or for web applications that require frequent updates, this ``manual wall'' creates great stress as it accumulates.

The CI/CD pipeline powered by GitHub Actions comes to our rescue.
It's not just about automating tasks. "Preventing mistakes through mechanisms and creating a sanctuary to protect the mental health of developers". That's why I pursue this technology so deeply.

Why GitHub Actions over other tools?

There are other CI/CD options. CircleCI, Travis CI, or GitLab CI each have excellent characteristics. However, for individual developers, especially those who use GitHub as their main repository, the overwhelming advantage of GitHub Actions lies in its ``seamless integration.''

Let's check its position in the comparison table.

Tool nameStrengthsWeaknessesSuitable cases
GitHub ActionsFully integrated with GitHub, rich MarketplaceLimited free tier (*above a certain level), complex descriptionsPersonal development, OSS, products up to medium scale
CircleCIFast builds, advanced cache controlCost of learning settings, fast paid migrationFor companies, sites requiring large-scale, high-speed builds
GitLab CIAffinity with the GitLab ecosystemUI/UX that is difficult for GitHub usersTeams that primarily use GitLab

What we are looking for in personal development is not ``excessive customization,'' but ``how easily and reliable an environment can be built.'' From that perspective, GitHub Actions is currently the most balanced option.

Deep dive into the basics: Workflow, jobs, steps, actions

When using GitHub Actions, it is extremely important not to confuse the following four concepts: If you don't understand this point accurately, you'll end up in a labyrinth where YAML files become complicated and you end up with something that doesn't work for some reason.

1. Workflow: Overall flow (e.g. build → test → deploy). This applies to each YAML file under `.github/workflows`.
2. Job: A set of processing units within a Workflow. Normally, jobs are executed in parallel, but by using the `needs` keyword, you can define dependencies such as ``deploy after building''.
3. Step: An individual step within a job. It is the smallest unit that executes a shell script or calls a specific action.
4. Action: A component for reusing useful functions created by others. Using `actions/checkout@v4` and `actions/setup-node@v4` prevents "reinventing the wheel".

Practice: Building a robust CI/CD pipeline

Here, we will introduce an example of a workflow that can be used in actual practice, assuming a Node.js application. Not only does it work, but it is configured with consideration to ``utilization of cache'' and **``separation of environment variables.''

Many people overlook this, but simply specifying `cache: 'npm' (or `yarn`, `pnpm`) when using `actions/setup-node` can dramatically reduce build time. This difference of just a few minutes can greatly affect developer stress.

`

name: CI_CD_Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm' # [Important] Enable dependency caching to reduce build time

      - name: Install dependencies
        run: npm ci # Install with stricter lock file compliance than npm install

      - name: Lint and Build
        run: |
          npm run lint
          npm run build

      - name: Run Unit Tests
        run: npm test

  deploy:
    needs: build-and-test # Runs only if the previous job is successful
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy to Production
        # Here, describe the actions that match the actual environment (AWS, Vercel, Cloudflare, etc.)
        run: |
          echo "Deploying to production environment..."
          # Example: npm run deploy -- --env=production
`

Points of this code

  • Adoption of `npm ci`: `npm install` may behave unstable due to dependency resolution, but in a CI environment it is a golden rule to use `npm ci`, which strictly follows the lock file.
  • `Dependency by `needs`: Physically prevents the test from proceeding with the deployment process if it fails.
  • Conditional execution (`if`): Controls the deployment to run only when there is a `push` to the `main` branch, rather than on every pull request.

Here are a number of "landmines" that beginners will definitely fall into

The problems faced when implementing GitHub Actions are often due to "inadequate operations" rather than technical errors.

1. Mismanagement of secrets

The most dangerous thing to do is to write the API key and password directly in YAML. This is of course a no-no, but what is even more troublesome is ``different use of environment variables.'' Make sure to properly utilize `Settings > Secrets and variables > Actions` on the GitHub Actions settings screen and register with an abstract name like `DEPLOY_TOKEN`.

2. Cache mismatch

Even though caching is enabled in `actions/setup-node`, the build may be slow for some reason. This is often due to `package-lock.json` not being updated or hash calculation failing. At this time, the solution is often to manually clear the Actions cache (many developers spend hours worrying about this).

3. Runner timeout

The free tier of GitHub Hosted Runner has limitations. Especially when building a large Docker image or uploading a large number of assets, the job may sometimes time out in the middle. In this case, you should consider optimizations such as multi-stage builds, or consider using a more powerful machine.

To the next level: Utilizing Matrix Strategy

If you want to run tests in multiple environments (e.g. Node.js 18, 20, 22, etc.), you should implement `strategy: matrix`.

`

strategy:
  matrix:
    node-version: [18.x, 20.x, 22.x]
    
steps:
  - uses: actions/setup-node@v4
    with:
      node-version: ${{ matrix.node-version }}
`

Just by writing like this, GitHub Actions will automatically build three different environments in parallel and run the tests. This "sense of scale" is the reason why CI/CD should be designed seriously.

Conclusion: What lies beyond automation

Why do I write this in detail? This is because ``Understanding technology creates mental leeway.''**

Setting up GitHub Actions is difficult because it's not just about building an environment. It's "creating a system to trust your code." Once you've built the perfect pipeline, you won't have to sweat every time you deploy.

Submit a pull request and quietly watch it get a green check mark (✅). At that moment, your work proves that it is ready for the world to see.

What lies ahead after overcoming technological barriers is not just automation. It's the freedom to maximize your creativity.

The joy of seeing that green check mark after a late night of work. To experience this, we will write YAML again today.


🤖 Developer blog/public content

The developer's personal blog is also available, so please take a look. muttyan.com blog

Amazon great free trial/benefits summary