GitHub Actions Is More Powerful Than Most Teams Use It For

by Arif Ikhsanudin, Backend Developer

What Your Team Uses GitHub Actions For vs. What It Can Do

Most teams' Actions usage looks like: trigger on push and pull_request, run tests, maybe build and push a Docker image. That covers maybe 20% of what the platform can do.

GitHub Actions is an event-driven automation platform. The trigger isn't limited to pushes — it can be issues, pull request reviews, releases, scheduled cron jobs, repository dispatch (webhooks), workflow calls, and manual workflow_dispatch events. The available compute isn't limited to hosted runners — you can run self-hosted runners on your own infrastructure, including ARM, GPU, and Windows machines. The output isn't limited to logs — you can create GitHub releases, update PR comments dynamically, post deployment status to GitHub Environments, and integrate with the entire GitHub API.

The teams that treat it as a CI runner are leaving substantial automation value on the table.

Reusable Workflows: DRY for Your Pipeline

If you have multiple repositories with similar CI patterns, you're probably maintaining the same YAML in each one. Reusable workflows let you define a workflow once in a shared repository and call it from others — including passing inputs and secrets to the called workflow.

# In shared-workflows repo: .github/workflows/java-ci.yml
on:
  workflow_call:
    inputs:
      java-version:
        required: false
        type: string
        default: '21'
    secrets:
      SONAR_TOKEN:
        required: true

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
      - uses: actions/setup-java@v4
        with:
          java-version: ${{ inputs.java-version }}
          distribution: 'temurin'
          cache: 'gradle'
      - run: ./gradlew build
      - name: SonarQube analysis
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: ./gradlew sonar
# In your application repo: .github/workflows/ci.yml
jobs:
  ci:
    uses: myorg/shared-workflows/.github/workflows/java-ci.yml@main
    with:
      java-version: '21'
    secrets:
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

Every application repo gets the same CI behavior, maintained in one place. When you update the shared workflow, all callers get the update on their next run.

Dynamic PR Comments for Test and Coverage Reporting

Instead of requiring developers to dig into logs to see test results, you can post structured summaries directly to the PR:

- name: Post test summary to PR
  uses: actions/github-script@v7
  if: always() && github.event_name == 'pull_request'
  with:
    script: |
      const fs = require('fs');
      // Read JUnit XML results
      const { execSync } = require('child_process');
      const summary = execSync('python3 scripts/parse-junit.py build/reports/tests/').toString();

      // Find existing bot comment
      const comments = await github.rest.issues.listComments({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
      });
      const botComment = comments.data.find(c =>
        c.user.type === 'Bot' && c.body.includes('## Test Results')
      );

      const body = `## Test Results\n\n${summary}`;

      if (botComment) {
        // Update existing comment instead of creating a new one
        await github.rest.issues.updateComment({
          owner: context.repo.owner,
          repo: context.repo.repo,
          comment_id: botComment.id,
          body,
        });
      } else {
        await github.rest.issues.createComment({
          owner: context.repo.owner,
          repo: context.repo.repo,
          issue_number: context.issue.number,
          body,
        });
      }

This posts a test summary to the PR, updating the same comment on each push so the PR doesn't fill up with bot noise.

Manual Deployment Triggers With Environment Approvals

For production deployments that require human approval, workflow_dispatch combined with GitHub Environments gives you a structured approval flow without external tooling:

# Manual deployment with required approvals
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options: [staging, production]
      image-tag:
        description: 'Image tag to deploy'
        required: true
        type: string

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}  # Requires approval if configured in repo settings
    steps:
      - name: Deploy ${{ inputs.image-tag }} to ${{ inputs.environment }}
        run: |
          aws ecs update-service \
            --cluster ${{ inputs.environment }} \
            --service myapp \
            --task-definition myapp:${{ inputs.image-tag }}

In the repository's Environment settings, you can configure required reviewers for the production environment. When the workflow runs, it pauses and sends a notification to required reviewers — who can approve or reject from the GitHub UI or mobile app. No external deployment approval system required.

Scheduled Maintenance Workflows

Scheduled cron triggers enable automation that doesn't fit the commit-triggered model:

on:
  schedule:
    - cron: '0 2 * * 1'  # Every Monday at 2am UTC

jobs:
  dependency-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
      - run: ./gradlew dependencyCheckAnalyze
      - name: Open issue if vulnerabilities found
        uses: actions/github-script@v7
        if: failure()
        with:
          script: |
            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: 'Weekly dependency vulnerability scan found issues',
              body: 'See workflow run for details: ' + context.serverUrl +
                    '/' + context.repo.owner + '/' + context.repo.repo +
                    '/actions/runs/' + context.runId,
              labels: ['security', 'dependencies']
            });

This runs OWASP Dependency Check weekly and automatically opens a GitHub issue when it finds vulnerabilities — no human needs to remember to check the dashboard.

The platform is deep. Most teams are using the shallow end.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

Your Dockerfile Works But Your Image Is Bigger Than It Needs to Be

A working Dockerfile is not the same as a good one. Most images carry megabytes of unnecessary weight that slows builds, bloats registries, and widens the attack surface — and the fixes are straightforward once you know where to look.

Read more

The Struggle of Trying to Look Busy for Clients

You’ve just sent an update to a client and now find yourself staring at your screen, wondering what to do next. Looking busy can feel like a full-time job on its own.

Read more

Where You Put Your Cache Matters More Than You Think

Cache placement — at the client, at the service, at the database, at the edge — determines what problems you can solve and which new ones you create. The wrong layer makes the cache useless or dangerous.

Read more

Dublin's Best Backend Developers Work for Google and Meta — What the Rest of Us Do

You posted a backend role three weeks ago. The only applicants who fit are already at a FAANG company and just "seeing what's out there." They're not leaving.

Read more