Andrew Hanna

Andrew Hanna

Eine Salesforce CI/CD Pipeline mit GitHub Actions bauen

Eine Salesforce CI/CD Pipeline mit GitHub Actions bauen

Salesforce development hat sich weit von manuellen Change Sets entfernt. Wenn Teams wachsen und Releases häufiger werden, werden manuelle deployments schnell zum Engpass.

Hier kommt CI/CD (Continuous Integration and Continuous Delivery) ins Spiel. Eine gute Pipeline validiert, testet und promotet Änderungen automatisch von development bis production. Viele Teams nutzen dafür kommerzielle DevOps-Plattformen, trotzdem lohnt es sich zu verstehen, wie die automation darunter funktioniert.

In diesem Guide zeigen wir, wie Salesforce CI/CD funktioniert, wie Sie eine Pipeline mit GitHub Actions bauen und wann DIY automation an ihre Grenzen kommt. Dabei zeigen wir auch, welcher Ansatz je nach Teamgröße, Release-Kadenz und organisatorischer Komplexität am besten passt.

Wir schauen uns außerdem an, wo DIY-Pipelines an ihre Grenzen stoßen und warum viele Teams irgendwann auf eine dedizierte Salesforce-DevOps-Plattform wechseln, sobald ihr Prozess reift.

Neu in der Disziplin? Unser ausführlicher Guide zu Salesforce DevOps behandelt die Grundlagen, auf denen diese Pipeline aufbaut.

Warum CI/CD für Salesforce wichtig ist

Salesforce Teams liefern keine kompilierte App aus. Sie deployen metadata und Konfiguration:

  • Apex classes und triggers

  • Lightning Web Components

  • Flows und automation

  • Profiles und permission sets

  • Objects, fields, layouts und schema

Ohne automation entstehen environment drift, fehlende dependencies, manuelle Fehler und wenig Sichtbarkeit darüber, was sich wirklich geändert hat, dazu schlechte Nachvollziehbarkeit zwischen work items und Release-Inhalt.

CI/CD pipelines lösen das, indem sie:

  • Änderungen automatisch validieren

  • Tests vor deployment ausführen

  • Git als source of truth nutzen

  • nur genehmigte Änderungen promoten

  • Umgebungen über die Zeit konsistent halten

Was Sie brauchen, bevor Sie starten

Dieser Guide setzt ein Repository und Orgs voraus, die bereits auf Automatisierung ausgelegt sind. Fehlt eines davon, schließen Sie diese Lücke zuerst: eine Pipeline darüber automatisiert nur die Lücke.

  • Metadata bereits versioniert, im source format. Alles, was Sie deployen wollen, liegt unter force-app und hat eine Commit-Historie. Die Pipeline promotet Commits, keine Orgs, also ist alles, was nur in einer Sandbox existiert, für sie unsichtbar.

  • Salesforce CLI v2 (sf) auf dem Runner. Jeder Befehl in diesem Guide ist die v2-Form, im Workflow installiert mit npm install @salesforce/cli --global. Die alte sfdx CLI ist deprecated.

  • Eine Org je Stufe, mit klarem Owner. Mindestens eine Sandbox zum Validieren und die Org, in die Sie releasen. Die Branching-Strategie weiter unten bildet Branches eins zu eins auf diese Orgs ab.

  • Eine Connected App und ein integration user in jeder Ziel-Org, mit aktivierten digital signatures und vorab autorisiertem User. Die Einrichtung zeigt weiter unten Connected App und JWT authentication.

Salesforce development models

1. Org-based development

Dies ist weiterhin das häufigste Modell. Developers arbeiten in sandboxes, metadata wird nach Git geholt und CI/CD promotet diese metadata durch die Umgebungen.

Developer Sandbox
      ↓
QA
      ↓
UAT / Staging
      ↓
Production

Viele Enterprise-Teams arbeiten weiterhin so, auch wenn sie später im Release-Flow unlocked packages einführen.

2. Package-based development

Package-based development nutzt modulare Packages, statt den gesamten metadata tree als eine Einheit zu promoten. Jedes Package steht für ein logisches feature-Set oder eine Domäne. Das passt zu ISVs, AppExchange partners und Enterprise Teams mit mehreren Workstreams.

In der Praxis starten viele Teams org-based und führen Packages nach und nach für release versioning und sauberere Promotion ein.

Empfohlene Patterns nach Teamgröße

Kleine Teams

Developer Sandbox
      ↓
Retrieve to Git
      ↓
Pull Request validation
      ↓
Build unlocked package version
      ↓
Install in Staging / QA Sandbox
      ↓
Install same package version in Production

Das funktioniert, wenn das Team klein und eng abgestimmt ist, eine staging oder QA Umgebung für signoff reicht, die Release-Frequenz hoch ist und das Team Disziplin ohne zu viel Prozess-Overhead will.

Entscheidend ist: Auch ein kleines Team braucht einen sauberen Release-Prozess. Er sollte nur nicht schwerer sein als das Team selbst.

Wachsende Teams

Größere Teams brauchen meist getrennte QA und UAT. QA validiert integrierte source changes, während UAT das release artifact prüft, das nach production geht.

Qa Devops

Developer Sandbox
      ↓
Feature branch
      ↓
Pull Request validation
      ↓
Merge to develop
      ↓
Deploy source to QA
      ↓
Create release branch
      ↓
Build unlocked package version
      ↓
Install package in UAT
      ↓
Install same package version in Production

Production erhält dieselbe package version, die UAT bestanden hat. Das sorgt für bessere Release-Traceability, sauberere Rollback-Optionen und weniger Überraschungen in production.

ISVs und modular packages

ISVs starten oft package-led. Große orgs wechseln häufig zu modular unlocked packages, etwa für sales, service operations, onboarding und core automation.

Scratch Org / Dev Org
      ↓
Source tracked to Git
      ↓
Pull Request validation
      ↓
Package version build
      ↓
Install in internal test org
      ↓
Install in staging / UAT
      ↓
Promote release artifact

Auch hier gilt dasselbe Prinzip: Git als source of truth, klare Promotion-Schritte und versioned release artifacts.

Das ist besonders nützlich in großen Unternehmen, in denen mehrere Teams parallel an unterschiedlichen Domänen arbeiten. Wächst das Team oder wird die org komplexer, muss der DevOps-Prozess mitwachsen, um Tempo, Qualität, Sichtbarkeit und Traceability zu erhalten.

Branching strategy

Vor der Pipeline sollten Sie festlegen, wie Arbeit durch Git läuft. Die häufigsten Optionen sind GitFlow und trunk-based development.

GitFlow

main      → production history
develop   → integration branch
feature/* → work item branches
release/* → release candidate branches
hotfix/*  → urgent production fixes

GitFlow passt zu Teams mit QA, UAT, scheduled releases und expliziten release candidate branches. Der Nachteil von GitFlow: mehr branch management und mehr merge-Koordination. Trunk-based development passt besser zu kleinen reifen Teams mit starker automation und häufigen deployments.

Trunk-Based Development

main       → always releasable
feature/*  → short-lived branches merged quickly

Das funktioniert gut, wenn:

  • das Team klein ist

  • deployments häufig sind

  • validation und testing stark ausgeprägt sind

  • das Team weniger langlebige Branches möchte

Der Nachteil: schwache test-Disziplin kann den gemeinsamen Branch schnell destabilisieren.

Welches Modell empfehlen wir?

Für viele org-based Salesforce Teams ist GitFlow einfacher einzuführen, weil es das vertraute Dev → QA → UAT → Prod Release-Modell widerspiegelt. Trunk-based development kann bei kleinen, reifen Teams mit starker automation gut funktionieren.

Entscheidend ist nicht, dass ein Modell grundsätzlich besser ist. Entscheidend ist, sich bewusst für ein Modell zu entscheiden und Umgebungen, Freigaben und Pipeline-Logik konsistent danach auszurichten.

Empfohlene Pipeline-Architektur

Für die meisten wachsenden Salesforce Teams empfehlen wir:

Developer Sandbox
      ↓
Retrieve metadata to Git
      ↓
Feature branch
      ↓
Pull Request validation
      ↓
Merge to develop
      ↓
Deploy source to QA
      ↓
Create release branch
      ↓
Build unlocked package version
      ↓
Install package in UAT
      ↓
Regression testing and signoff
      ↓
Install same package version in Production

Source deploys nach QA sind schnell für integration testing. Die package version wird danach zum stabilen release artifact für UAT und production. UAT validiert genau das artifact, das für production vorgesehen ist, und production erhält dieselbe getestete package version.

Das ergibt einen Prozess, der sowohl praktisch als auch kontrolliert ist. Er funktioniert während des developments gut mit source tracking und gibt Ihnen später im Flow bessere Release-Traceability.

package.xml vs source tracking vs Serpent workflow

Kategorie Package.xml Source tracking + unlocked packages Serpent workflow
Modell Manifest-driven deployment Git-driven development mit package promotion Task-driven workflow rund um Git, Umgebungen und Releases
Best for Hotfixes und legacy teams Moderne Teams mit versioned releases Teams, die Tempo, Sichtbarkeit und weniger friction wollen
Metadata-Auswahl Manuell Source tracking plus Git-Auswahl Geführt, task-based und dependency-aware
Dependencies Manuell Besser, aber Disziplin nötig Unterstützt durch metadata analysis und workflow logic
Traceability in production Eingeschränkt Gut bei package versions Starke Sichtbarkeit über work items, Releases und Orgs
Rollback-Klarheit Schwieriger, besonders bei destructive changes Besser mit versioned packages Klarer durch release artefacts, workflow-Historie und package promotion
Hotfix / Back-Promotion Nützlich und oft nötig Braucht teils weiterhin package.xml für production recovery Unterstützt als Teil des Release-Workflows und Sichtbarkeitsmodells
Operativer Aufwand Anfangs gering, später hoch Moderat Geringerer laufender Aufwand, da mehr vom Workflow nativ und sichtbar ist
Zusammenarbeit Meist manuell Git-centric und developer-friendly Für developers, admins, testers und release managers geeignet
Wann wir es empfehlen Spezielle edge cases und legacy Prozesse Empfohlener Standard für die meisten modernen Teams Empfohlen, wenn Sie dieselben DevOps-Prinzipien mit deutlich weniger friction wollen

package.xml und source tracking

package.xml ist das traditionelle manifest für metadata, die Sie retrieve oder deployen wollen.

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
  <types>
    <members>AccountService</members>
    <name>ApexClass</name>
  </types>
  <types>
    <members>AccountTrigger</members>
    <name>ApexTrigger</name>
  </types>
  <version>61.0</version>
</Package>

Es gibt explizite Kontrolle, ist aber manuell, dependency-anfällig und bei großen releases schwerer zu pflegen. package.xml funktioniert in allen org-Typen inklusive production und ist nützlich für hotfixes und gezielte deploys, spiegelt aber nicht automatisch wider, was sich in der org geändert hat.

Viele Teams nutzen package.xml weiterhin, und historisch macht das Sinn. Lange war es die praktikabelste Option, besonders als source tracking noch unausgereift war.

Source tracking vergleicht Änderungen in einer org mit lokaler source. Dieser Bereich hat sich nach dem Umstieg von der alten SFDX CLI auf die moderne sf CLI deutlich verbessert und wird seit zwei Jahren kontinuierlich weiterentwickelt.

sf project retrieve start
sf project deploy start

Das gibt schnelleres Feedback im development, reduziert manuelle manifest-Pflege und bessere Developer-Ergonomie, kombiniert mit Git-driven promotion funktioniert es besonders gut. Die Kehrseite: partial feature selection ist ohne das richtige tooling weiterhin unhandlich, und Teams brauchen trotzdem eine Strategie für release versioning.

Für die meisten modernen Teams ist source tracking im development sinnvoll, während unlocked packages die release promotion übernehmen.

Warum wir Source Tracking + Unlocked Packages empfehlen

Für die meisten modernen Teams empfehlen wir source tracking während des developments und unlocked packages für die release promotion.

Warum?

  • source tracking ist im Alltag eine bessere Developer-Erfahrung als manuelle manifests

  • unlocked packages geben Ihnen später im Flow echtes release versioning

  • Traceability in production wird durch package versions deutlich klarer

  • rollback und der Umgang mit destructive changes sind leichter nachvollziehbar

package.xml bleibt trotzdem in edge cases relevant. Das eigentliche Ziel ist also nicht Reinheit, sondern das richtige tool für den richtigen Schritt.

Was ist mit Production?

In production werden die Grenzen von source tracking wichtig. Sie können production nicht wie eine scratch org behandeln und Salesforce sauber erzählen lassen, was sich dort über die Zeit geändert hat.

Das heißt, production deployments hängen in der Regel ab von:

  • package.xml manifests

  • source-Verzeichnissen, die aus Git gebaut werden

  • unlocked package versions

Das ist einer der Hauptgründe, warum versioned package promotion so wertvoll ist. Sie gibt production eine klarere Release-Identität, als source tracking allein dort liefern kann.

Der praktische Punkt ist einfach: production verhält sich nicht wie eine source-tracked dev org, also brauchen Teams einen anderen Weg, um den Release-Status explizit zu machen. Unlocked packages sind einer der besten Wege dafür.

Feature-level changes sicher auswählen

Eine sandbox enthält oft mehrere Änderungen. Das sichere Muster ist: alles retrieve, nur die Komponenten einer feature auswählen und diesen Scope in eine feature branch committen.

sf project retrieve start
git status
git add force-app/main/default/classes/PricingService.cls
git add force-app/main/default/triggers/OpportunityTrigger.trigger
git add force-app/main/default/flows/Discount_Approval.flow-meta.xml
git commit -m "Feature: pricing approval flow"

Die Herausforderung ist, dass dependencies nicht immer offensichtlich sind. Ein Flow kann ein Feld referenzieren, das Feld kann von einem permission set abhängen, und eine Änderung am permission set kann nötig sein, damit das feature nutzbar ist.

Hier übersehen Teams oft metadata oder deployen sicherheitshalber zu viel.

Mit Serpent wird das einfacher, weil work items, metadata selection und dependency analysis in einem Workflow zusammenliegen.

Delta deployments vs full branch deployments

Delta deployments sind nützlich für schnelle PR validation und kleine isolierte Features, sowie für kürzere validation-Zeiten in sehr großen orgs.

sf sgd source delta \
  --to HEAD \
  --from origin/develop \
  --output-dir delta \
  --generate-delta
sf project deploy validate --source-dir delta --test-level RunLocalTests
sf project deploy start --source-dir delta

Open-source CLI suites verpacken dieselben Befehle in ein geführtes Menü, ein vernünftiger Mittelweg zwischen rohen scripts und einer Plattform. Wie Serpent im Vergleich zu sfdx-hardis abschneidet, lesen Sie, falls das der Weg ist, den Sie abwägen.

Full deployments sind besser für destructive changes, revert commits und das Zurücksetzen von drift in gemeinsamen Umgebungen, und dafür, dass eine Umgebung genau einem bekannten Branch-Stand entspricht. Das gilt ebenso für Typen, die noch in großen gemeinsamen Dateien liegen, allen voran Profiles und Permission Sets, die sich nicht sauber diffen lassen und von einem Delta teilweise übersehen oder überschrieben werden können.

Wählen Sie ein full deployment auch bei profiles und permission sets: diese Datei muss als Ganzes landen, nicht als berechnetes Delta.

sf project deploy start --source-dir force-app

In der Praxis kombiniert ein guter Prozess beides: delta deploys für Tempo und full deploys für Sicherheit, wenn der Change-Typ es verlangt.

Beide Varianten schieben Code nach oben. Die Gegenrichtung zählt genauso: nach einem Hotfix direkt auf Production hinken alle unteren Umgebungen hinterher, bis Sie Back-Promotion in der Pipeline einrichten.

Serpent hilft hier ebenfalls, indem es den deployment-Flow weniger brüchig macht. Statt dass jedes Team diese Logik in GitHub Actions selbst nachbaut, kann der Workflow anzeigen, wann gezielte Bewegung ausreicht und wann eine breitere promotion sicherer ist.

Unlocked packages für release versioning

Unlocked packages machen releases expliziter: Sie promoten ein versioned artifact statt lose metadata. Das verbessert traceability, UAT-to-production promotion und rollback planning, dazu klare Versionsnummern für Releases und saubereres Dependency-Handling.

Empfohlener Unlocked-Package-Flow

Für ein größeres org-based Team sieht der Release-Flow meist so aus:

Developer Sandbox
      ↓
Retrieve to Git
      ↓
Pull Request validation
      ↓
Deploy source to QA
      ↓
Create release branch
      ↓
Build unlocked package version
      ↓
Install in UAT
      ↓
Regression and approval
      ↓
Install same package version in Production

Für ein sehr kleines Team kann es einfacher sein:

Developer Sandbox
      ↓
Retrieve to Git
      ↓
Pull Request validation
      ↓
Build unlocked package version
      ↓
Install in Staging
      ↓
Promote same package version to Production

Beispielbefehle

sf package create --name SalesApp --package-type Unlocked --path force-app
sf package version create --package SalesApp --wait 20 --installation-key-bypass
sf package install --package 04tXXXXXXXXXXXX --target-org myUATOrg --wait 20
sf package install --package 04tXXXXXXXXXXXX --target-org myProdOrg --wait 20

Warum Rollback leichter nachvollziehbar ist

Unlocked packages lösen nicht magisch jedes rollback-Problem, besonders wenn Daten betroffen sind. Im Vergleich zu reinem source deployment oder reiner package.xml-Promotion machen sie rollback aber leichter nachvollziehbar, weil:

  • das release ein versioned artefact ist

  • Sie genau wissen, welche package version die Änderung eingeführt hat

  • Sie klarer vergleichen können, was sich zwischen versions geändert hat

  • destructive changes im Kontext der Release-Historie leichter nachzuverfolgen sind

Das zählt in production umso mehr, wo source tracking Ihnen nicht dieselbe Sichtbarkeit geben kann wie in development-Umgebungen.

GitHub Actions Pipeline bauen

GitHub Actions eignet sich für Teams, die die mechanics verstehen und ihre eigene CI/CD foundation im repository bauen wollen. Es integriert sich direkt in Ihr repository, und Workflows werden als YAML-Dateien definiert, die zusammen mit dem Code im repository liegen.

Vorgeschlagenes Branch-zu-Umgebung-Mapping

feature/*   → developer work item branches
develop     → deployed to QA
release/*   → package version build
main        → production history
hotfix/*    → urgent production fixes

Vollständiges End-to-End-Beispiel der GitHub-Actions-Pipeline

Das folgende Beispiel zeigt ein praktisches End-to-End-Setup mit:

  • PR validation in develop

  • automatischem source deploy zu QA beim merge in develop

  • package version build auf release/*

  • package install nach UAT

  • manueller promotion derselben package version nach Production

name: Salesforce CI/CD

on:
  pull_request:
    branches: [develop]
  push:
    branches:
      - develop
      - 'release/*'
  workflow_dispatch:
    inputs:
      package_version_id:
        required: false

env:
  PROJECT_DIR: force-app
  DELTA_DIR: delta
  QA_ALIAS: qa
  UAT_ALIAS: uat
  PROD_ALIAS: prod
  PACKAGE_NAME: SalesApp

jobs:
  pr-validate:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install @salesforce/cli --global
      - run: sf plugins install sfdx-git-delta
      - run: echo "${{ secrets.SF_JWT_KEY_QA }}" > server.key
      - run: |
          sf org login jwt \
            --client-id ${{ secrets.SF_CLIENT_ID_QA }} \
            --jwt-key-file server.key \
            --username ${{ secrets.SF_USERNAME_QA }} \
            --instance-url ${{ secrets.SF_INSTANCE_URL_QA }} \
            --alias $QA_ALIAS
      - run: |
          sf sgd source delta --to HEAD --from origin/develop --output-dir delta --generate-delta
          sf project deploy validate --source-dir delta --target-org $QA_ALIAS --test-level RunLocalTests

  deploy-to-qa:
    if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: npm install @salesforce/cli --global
      - run: sf plugins install sfdx-git-delta
      - run: echo "${{ secrets.SF_JWT_KEY_QA }}" > server.key
      - run: |
          sf org login jwt \
            --client-id ${{ secrets.SF_CLIENT_ID_QA }} \
            --jwt-key-file server.key \
            --username ${{ secrets.SF_USERNAME_QA }} \
            --instance-url ${{ secrets.SF_INSTANCE_URL_QA }} \
            --alias $QA_ALIAS
      - run: |
          sf sgd source delta \
            --to HEAD \
            --from HEAD^ \
            --output-dir $DELTA_DIR \
            --generate-delta || true
      - run: |
          if [ -d "$DELTA_DIR" ] && [ "$(find $DELTA_DIR -type f | wc -l)" -gt 0 ]; then
            sf project deploy start \
              --source-dir $DELTA_DIR \
              --target-org $QA_ALIAS \
              --test-level RunLocalTests
          else
            sf project deploy start \
              --source-dir $PROJECT_DIR \
              --target-org $QA_ALIAS \
              --test-level RunLocalTests
          fi

  build-package-and-install-uat:
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/release/')
    runs-on: ubuntu-latest
    outputs:
      package_version_id: ${{ steps.capture_version.outputs.package_version_id }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: npm install @salesforce/cli --global
      - run: sf plugins install @salesforce/plugin-packaging
      - run: echo "${{ secrets.SF_JWT_KEY_DEVHUB }}" > server.key
      - run: |
          sf org login jwt \
            --client-id ${{ secrets.SF_CLIENT_ID_DEVHUB }} \
            --jwt-key-file server.key \
            --username ${{ secrets.SF_USERNAME_DEVHUB }} \
            --instance-url ${{ secrets.SF_INSTANCE_URL_DEVHUB }} \
            --alias devhub
      - run: |
          sf package version create \
            --package $PACKAGE_NAME \
            --target-dev-hub devhub \
            --wait 30 \
            --installation-key-bypass \
            --code-coverage
      - id: capture_version
        run: |
          VERSION_ID=$(sf package version list --packages $PACKAGE_NAME --target-dev-hub devhub --json | jq -r '.result[0].SubscriberPackageVersionId')
          echo "package_version_id=$VERSION_ID" >> $GITHUB_OUTPUT
      - run: echo "${{ secrets.SF_JWT_KEY_UAT }}" > server.key
      - run: |
          sf org login jwt \
            --client-id ${{ secrets.SF_CLIENT_ID_UAT }} \
            --jwt-key-file server.key \
            --username ${{ secrets.SF_USERNAME_UAT }} \
            --instance-url ${{ secrets.SF_INSTANCE_URL_UAT }} \
            --alias $UAT_ALIAS
      - run: |
          sf package install \
            --package ${{ steps.capture_version.outputs.package_version_id }} \
            --target-org $UAT_ALIAS \
            --wait 30 \
            --publish-wait 10 \
            --no-prompt

  promote-to-production:
    if: github.event_name == 'workflow_dispatch' && github.event.inputs.package_version_id != ''
    runs-on: ubuntu-latest
    environment: production
    steps:
      - run: npm install @salesforce/cli --global
      - run: echo "${{ secrets.SF_JWT_KEY_PROD }}" > server.key
      - run: |
          sf org login jwt \
            --client-id ${{ secrets.SF_CLIENT_ID_PROD }} \
            --jwt-key-file server.key \
            --username ${{ secrets.SF_USERNAME_PROD }} \
            --instance-url ${{ secrets.SF_INSTANCE_URL_PROD }} \
            --alias $PROD_ALIAS
      - run: |
          sf package install \
            --package ${{ github.event.inputs.package_version_id }} \
            --target-org $PROD_ALIAS \
            --wait 30 \
            --publish-wait 10 \
            --no-prompt

In production fügen Teams meist separate Jobs für deploy to QA, package version build, install in UAT und manuelle promotion nach production hinzu.

Das ist nicht das einzig gültige GitHub-Actions-Design, aber es bildet die empfohlene Aufteilung ab: source-based validation in den unteren Umgebungen und package-based promotion nach UAT und Production.

Weitere Pipeline-Praktiken und operative Hinweise

Eine CI/CD-Pipeline ist nur ein Teil eines funktionierenden Salesforce-DevOps-Prozesses. Was oft über Erfolg oder Misserfolg entscheidet, ist, wie Teams die operativen Details rund um development, testing, promotion und Release-Koordination handhaben.

Feature-Level-Änderungen sicher managen

Eine Herausforderung in der Salesforce-Entwicklung ist, dass sich in sandboxes häufig mehrere Änderungen gleichzeitig ansammeln. Ein Developer arbeitet vielleicht an zwei oder drei Features, aber nur eines ist bereit für die Promotion.

Wer einfach alle metadata retrieved und weiterdeployt, riskiert, unfertige Arbeit zu promoten.

Das sicherere Muster:

Developer Sandbox
      ↓
Alle metadata lokal retrieven
      ↓
Nur die Komponenten der Feature auswählen
      ↓
Diese Komponenten in eine feature branch committen
      ↓
Pull Request zur Validierung einreichen

Beispiel-Workflow:

sf project retrieve start

git checkout -b feature/pricing-update

git add force-app/main/default/classes/PricingService.cls
git add force-app/main/default/triggers/PricingTrigger.trigger
git add force-app/main/default/lwc/pricingWidget

git commit -m "Feature: pricing update"

So spiegelt das repository ein einzelnes logisches feature wider statt eines Mixes unzusammenhängender Änderungen.

Die Schwierigkeit ist, dass Salesforce metadata oft eng verzahnt ist. Ein Flow kann abhängen von:

  • custom fields

  • Apex classes

  • permission sets

  • record types

Fehlt eine dieser dependencies, kann das die deployment validation brechen.

Viele Teams lösen das über Erfahrung und manuelle Prüfungen. Plattformen wie Serpent helfen, indem sie metadata-Beziehungen analysieren und dependencies automatisch identifizieren, sodass das richtige Set an Komponenten gemeinsam bewegt wird.

KI-gestützte Metadata-Auswahl

Aktuelle Verbesserungen im developer tooling haben KI-Unterstützung für dependency-analysis eingeführt.

In der Praxis heißt das:

  • eine feature-Beschreibung kann analysiert werden

  • verwandte metadata-Komponenten können erkannt werden

  • dependency graphs können ausgewertet werden

  • der wahrscheinlichste metadata-Scope kann vorgeschlagen werden

Serpent enthält KI-Agenten, die auf Salesforce-metadata-Muster trainiert sind und helfen, das logische Set an Komponenten für ein feature zu bestimmen, was das Risiko unvollständiger deployments senkt.

Das nimmt Developern nicht die Kontrolle ab. Es macht die Auswahl nur schneller und sicherer.

Umgang mit Sandbox Drift

Ein weiteres verbreitetes Problem in Salesforce-Umgebungen ist sandbox drift.

Das passiert, wenn jemand direkt in einer gemeinsamen Umgebung wie QA oder UAT eine Änderung vornimmt, die nie ins Git-repository gelangt.

Mit der Zeit weicht die Umgebung von der source control ab.

Um drift zu erkennen, führen Teams oft periodische retrieval-Vergleiche durch:

sf project retrieve start
git diff

Tauchen unerwartete Unterschiede auf, sollten sie sofort abgeglichen werden, entweder durch commit nach Git oder durch Entfernen.

Wenn drift sich ansammelt, werden spätere deployments unberechenbar.

Umgebungsstrategie

Jede Umgebung sollte einen klaren Zweck haben.

Umgebung Zweck
Developer Sandbox Feature-Entwicklung und frühes Testen
QA Integriertes Testen über mehrere Features hinweg
UAT Business-Validierung von release candidates
Production Live-System

Probleme entstehen oft, wenn Umgebungen ihren Zweck verlieren. Wird QA zu einer weiteren development-sandbox, funktioniert es nicht mehr als verlässlicher integration checkpoint.

Teststrategie

Ein robuster CI/CD-Prozess umfasst automatisierte Tests auf mehreren Ebenen.

  • unit tests für Apex

  • component tests für LWC

  • integration tests für cross-feature workflows

PR-validation-pipelines laufen oft mit gezielten tests für Tempo, während release-pipelines breitere test suites ausführen.

Beispiel PR-validation:

sf project deploy validate \
  --source-dir delta \
  --test-level RunSpecifiedTests

Beispiel release-validation:

sf project deploy validate \
  --source-dir force-app \
  --test-level RunLocalTests

Ziel ist es, Probleme früh zu erkennen, ohne den developer-feedback-cycle unnötig zu verlangsamen. Welche suite an welchem gate läuft und wo UI coverage hineinpasst, ist ein eigenes Thema: wie man eine Salesforce test automation pipeline in CI baut geht die Ebenen der Reihe nach durch.

Release-Sichtbarkeit

Eine der am meisten unterschätzten DevOps-Herausforderungen ist schlicht zu wissen, was in einem release steckt.

Fragen, die Teams häufig stellen:

  • Welche features sind im nächsten release enthalten?

  • Welche Komponenten wurden gestern nach QA deployed?

  • Welche package version ist aktuell in production installiert?

Ohne einen strukturierten workflow erfordert die Beantwortung dieser Fragen oft das Durchsuchen von Git-Historie, pipeline-Logs und manuellen release notes.

Serpent löst das, indem es deployments mit work items verknüpft, was releases über Umgebungen hinweg leichter verständlich macht.

Developer Experience

Auch die Produktivität von Developern zählt.

Viele Developer arbeiten am liebsten in VS Code mit den Salesforce-Extensions und CLI-tools.

DevOps-Plattformen sollten sich in diesen workflow integrieren, statt Developer in völlig separate Interfaces zu zwingen.

Serpent enthält eine VS Code Extension, mit der Developer:

  • metadata vergleichen

  • deployments validieren

  • release-tasks verwalten

  • Änderungen deployen können

Das hält DevOps-Operationen nah an der development-Umgebung.

Connected App und JWT authentication

Die Pipeline-Beispiele oben setzen voraus, dass Ihr GitHub-Actions-Runner sich headless bei Salesforce authentifizieren kann. Das Standardmuster ist JWT-based auth über eine Connected App. Hier ist das praktische Setup, Schritt für Schritt.

1. Eine Connected App in Salesforce erstellen

  1. Erstellen Sie eine Connected App im Salesforce App Manager.

  2. Setzen Sie die callback URL auf http://localhost:1717/OauthRedirect.

  3. Wählen Sie OAuth scopes wie api (Manage user data via APIs) und refresh_token, offline_access (Perform requests at any time).

  4. Aktivieren Sie Use digital signatures, laden Sie ein certificate hoch und speichern Sie den private key in GitHub Secrets.

2. Das Zertifikat generieren

openssl genrsa -out server.key 2048
openssl req -new -x509 -days 1825 -key server.key -out server.crt

Laden Sie server.crt in der Connected App hoch. Bewahren Sie server.key sicher auf: es gehört in GitHub Secrets, niemals ins repository.

3. GitHub Secrets hinzufügen

Gehen Sie in Ihrem GitHub-repository zu Settings → Secrets and variables → Actions und fügen Sie hinzu:

Der Workflow oben authentifiziert sich bei vier getrennten Orgs - QA, dem Dev Hub, UAT und Production - daher braucht jedes Secret eine Kopie pro Umgebung, mit dem Suffix _QA, _DEVHUB, _UAT oder _PROD (zum Beispiel SF_CLIENT_ID_QA und SF_CLIENT_ID_DEVHUB). Wiederholen Sie die vier Secrets unten für jedes Suffix, jeweils zur Connected App und Org dieser Umgebung:

  • SF_CLIENT_ID_<ENV> - Consumer Key aus der Connected App dieser Umgebung.

  • SF_USERNAME_<ENV> - der Salesforce-username dieser Umgebung, zum Beispiel [email protected].

  • SF_JWT_KEY_<ENV> - der Inhalt der server.key-Datei dieser Umgebung.

  • SF_INSTANCE_URL_<ENV> - https://test.salesforce.com für sandboxes (QA, UAT, Dev Hub) oder https://login.salesforce.com für production.

Häufige Fehler

Die meisten CI/CD-Fehlschläge fallen in eine kleine Gruppe wiederkehrender Probleme. Die Tabelle unten deckt die häufigsten ab.

Error Wahrscheinliche Ursache Fix
INVALID_LOGIN JWT key oder client ID falsch Secrets und trailing newlines prüfen.
INSUFFICIENT_ACCESS Connected App permissions Scopes und Aktivierung für den integration user prüfen.
Tests failing on deploy Code coverage unter 75% sf apex run test lokal ausführen und coverage erhöhen.
Metadata deployt nicht Falsche SFDX-Projektstruktur Prüfen Sie, ob sfdx-project.json die richtigen packageDirectories hat.
Job läuft beim deploy in ein timeout Lange validation oder full-org tests Nutzen Sie --test-level RunLocalTests bei der PR-validation. Reservieren Sie RunAllTestsInOrg für den production-Job.

FAQ

Wie authentifiziert sich GitHub Actions an einer Salesforce Org?

Über eine Connected App mit dem JWT-Flow. Sie legen die App im App Manager an, laden ein Zertifikat hoch und hinterlegen Private Key und Consumer Key in den GitHub Secrets, womit sich der Workflow vor jedem Deploy-Schritt anmeldet.

Warum scheitert meine Pipeline an INVALID_LOGIN?

Der JWT Key oder die Client ID passt nicht zu dem, was die Connected App erwartet. Prüfen Sie die Secrets Zeichen für Zeichen und achten Sie auf einen überzähligen Zeilenumbruch am Ende des Private Key.

Warum schlagen meine Tests beim Deploy fehl?

Meist liegt die Code Coverage unter 75 Prozent. Führen Sie sf apex run test lokal aus, um die betroffenen Klassen zu sehen, und nutzen Sie RunLocalTests bei der PR-Validierung, damit der Job schnell bleibt. Welche Suite an welchem Gate läuft, klärt der Guide zu Test Automation in CI.

Delta Deployments oder Full Deployments?

Delta Deployments pushen nur das Geänderte und passen zur PR-Validierung und zu kleinen Features in großen Orgs. Full Deployments sind sicherer bei Löschungen, Reverts und immer dann, wenn eine Umgebung einem bekannten Branch-Stand entsprechen muss.

Brauche ich sfdx-git-delta?

Nein, aber es lohnt sich, sobald Deploys langsam werden. Kombinieren Sie es bei Löschungen mit einem Full Deploy, damit destructive changes behandelt und nicht still übersprungen werden.

Soll ich sfdx oder sf für eine Salesforce-GitHub-Actions-Pipeline nutzen?

Nutzen Sie sf, die moderne Salesforce CLI, denn die alte sfdx-CLI, die sie ablöst, ist deprecated und sollte nicht die Basis einer neuen Pipeline sein. Eine bestehende Pipeline umzustellen ist vor allem eine Frage der Befehlsnamen: Aus sfdx force:source:deploy wird sf project deploy start, aus sfdx force:apex:test:run wird sf apex run test, und Plugins werden über sf plugins install neu installiert.

Warum beim Pull Request validieren und beim Merge erneut deployen?

Der Pull-Request-Job belegt, dass der Branch kompiliert und seine Tests gegen die Ziel-Org bestehen, ohne etwas hineinzuschreiben. Der Merge-Job verändert die Org, und er läuft gegen den Merge-Commit, nicht gegen den Stand, den der Pull Request validiert hat.

Fazit

Salesforce DevOps hängt nicht von einem einzelnen Tool oder einem perfekten Pipeline Format ab. Entscheidend ist ein zuverlässiger Prozess, in dem Git die source of truth ist, Änderungen automatisch validiert werden, releases versioned und traceable sind und Umgebungen konsistent bleiben.

Kleine Teams starten oft mit einfachen Pipelines und einer einzigen Staging-Umgebung. Größere Organisationen brauchen strukturierte Umgebungen, versionierte Releases und modulare Packages.

GitHub Actions hilft Teams, automation unter der Oberfläche zu verstehen. Wenn Salesforce environments komplexer werden, helfen Tools wie Serpent, Tempo, Kontrolle, Sichtbarkeit und release confidence zu behalten.

Ähnliche Artikel

Neugierig auf schnelleres Shipping, bevor Sie einsteigen? Sprechen wir

Unverbindlich.