home / skills / thebushidocollective / han / cicd-best-practices

This skill helps you optimize GitLab CI/CD pipelines for performance, reliability, and maintainability by applying best practices for parallelization,

npx playbooks add skill thebushidocollective/han --skill cicd-best-practices

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.9 KB
---
name: gitlab-ci-best-practices
user-invocable: false
description: Use when optimizing GitLab CI/CD pipelines for performance, reliability, or maintainability. Covers pipeline optimization and organizational patterns.
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash
  - Grep
  - Glob
---

# GitLab CI - Best Practices

Optimize GitLab CI/CD pipelines for performance, reliability, and maintainability.

## Pipeline Optimization

### Use DAG with Needs

```yaml
stages:
  - build
  - test
  - deploy

build:frontend:
  stage: build
  script: npm run build:frontend

build:backend:
  stage: build
  script: npm run build:backend

test:frontend:
  stage: test
  needs: ["build:frontend"]
  script: npm run test:frontend

test:backend:
  stage: test
  needs: ["build:backend"]
  script: npm run test:backend

deploy:
  stage: deploy
  needs: ["test:frontend", "test:backend"]
  script: ./deploy.sh
```

### Parallel Execution

```yaml
test:
  parallel:
    matrix:
      - SUITE: [unit, integration, e2e]
  script:
    - npm run test:$SUITE
```

### Interruptible Jobs

```yaml
test:
  interruptible: true
  script:
    - npm test

deploy:production:
  interruptible: false  # Never cancel
  script:
    - ./deploy.sh
```

## Configuration Organization

### Split Configuration Files

```yaml
# .gitlab-ci.yml
include:
  - local: .gitlab/ci/build.yml
  - local: .gitlab/ci/test.yml
  - local: .gitlab/ci/deploy.yml

stages:
  - build
  - test
  - deploy
```

### Reusable Templates

```yaml
.node_template: &node_template
  image: node:20-alpine
  before_script:
    - npm ci
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/

test:unit:
  <<: *node_template
  script:
    - npm run test:unit

test:lint:
  <<: *node_template
  script:
    - npm run lint
```

### Extends Keyword

```yaml
.base_job:
  image: node:20-alpine
  before_script:
    - npm ci

test:
  extends: .base_job
  script:
    - npm test

build:
  extends: .base_job
  script:
    - npm run build
```

## Resource Management

### Resource Groups

```yaml
deploy:staging:
  resource_group: staging
  script:
    - ./deploy.sh staging

deploy:production:
  resource_group: production
  script:
    - ./deploy.sh production
```

### Runner Tags

```yaml
heavy_build:
  tags:
    - high-memory
    - docker
  script:
    - ./build.sh
```

## Error Handling

### Retry Configuration

```yaml
test:flaky:
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure
      - script_failure
```

### Allow Failure

```yaml
test:experimental:
  allow_failure: true
  script:
    - npm run test:experimental

test:experimental:soft:
  allow_failure:
    exit_codes: [42]  # Only allow specific exit code
```

## Security Best Practices

### Protected Pipelines

```yaml
deploy:production:
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
  environment:
    name: production
```

### Secure Variables

```yaml
# Use protected and masked variables
deploy:
  script:
    - echo "$API_KEY"  # Masked in logs
  rules:
    - if: $CI_COMMIT_REF_PROTECTED == "true"
```

## Monitoring & Debugging

### Job Logging

```yaml
test:
  script:
    - set -x  # Enable debug output
    - npm test
  after_script:
    - echo "Job status: $CI_JOB_STATUS"
```

### Pipeline Badges

```markdown
[![Pipeline](https://gitlab.com/group/project/badges/main/pipeline.svg)](https://gitlab.com/group/project/-/pipelines)
[![Coverage](https://gitlab.com/group/project/badges/main/coverage.svg)](https://gitlab.com/group/project/-/pipelines)
```

## Common Anti-Patterns

1. **Avoid**: Running all jobs in sequence
   **Do**: Use `needs` for parallel execution

2. **Avoid**: Downloading all artifacts
   **Do**: Use `dependencies` to limit downloads

3. **Avoid**: Rebuilding node_modules every job
   **Do**: Use cache with lock file keys

4. **Avoid**: Hardcoded secrets
   **Do**: Use CI/CD variables with protection

5. **Avoid**: Single monolithic `.gitlab-ci.yml`
   **Do**: Split into multiple included files

Overview

This skill helps optimize GitLab CI/CD pipelines for performance, reliability, and maintainability. It captures practical patterns for pipeline structure, resource management, error handling, and security. Use it to reduce pipeline runtime, simplify configuration, and avoid common anti-patterns.

How this skill works

The skill inspects pipeline design and recommends concrete changes: adopt DAG-style dependencies with needs, enable parallel matrix runs, and mark interruptible jobs to cancel redundant work. It suggests splitting the CI config into included files, creating reusable templates and base jobs, and applying resource groups, runner tags, and caching. It also flags flaky-job handling, secret management, and monitoring hooks.

When to use it

  • You want to shorten CI feedback loops and run independent tasks in parallel.
  • Your .gitlab-ci.yml is large, duplicated, or hard to maintain.
  • Jobs rebuild identical artifacts (like node_modules) on every run.
  • Deployments must be serialized or protected across environments.
  • You need to harden pipelines against leaked secrets or accidental runs.

Best practices

  • Use needs to create a DAG and run independent jobs in parallel rather than strict stage sequencing.
  • Split configuration into included files and use reusable templates or extends to reduce duplication.
  • Use cache keyed by lockfile and runner tags for sensible resource allocation.
  • Mark non-critical jobs interruptible and protect production deploys with rules and manual gates.
  • Handle flakiness with limited retry policies and allow_failure for experiments.
  • Mask and protect secrets; use environment rules for protected branches.

Example use cases

  • Convert sequential build/test/deploy stages into a needs-based DAG to cut pipeline time.
  • Define a node template for multiple test jobs to centralize image, before_script, and cache settings.
  • Run unit, integration, and e2e suites with a parallel matrix to utilize runners efficiently.
  • Serialize production deployments with resource_group and require manual approval on main.
  • Configure retries for flaky tests and allow specific experimental jobs to fail without breaking pipeline.

FAQ

When should I prefer needs over stages?

Use needs when jobs can run as soon as their inputs are ready; keep stages only for simple linear flows or when strict ordering is required.

How do I avoid re-installing dependencies in every job?

Use a reusable template with cache paths keyed by the lockfile, and run npm ci in before_script so jobs reuse cached node_modules across runs.