
Andrew Hanna

Andrew Hanna

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.
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
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.
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.
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.
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.
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.
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 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.
Vor der Pipeline sollten Sie festlegen, wie Arbeit durch Git läuft. Die häufigsten Optionen sind GitFlow und trunk-based development.
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.
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.
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.
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.
| 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 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.
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.
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.
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 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 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.
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
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
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 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.
feature/* → developer work item branches
develop → deployed to QA
release/* → package version build
main → production history
hotfix/* → urgent production fixes
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Erstellen Sie eine Connected App im Salesforce App Manager.
Setzen Sie die callback URL auf http://localhost:1717/OauthRedirect.
Wählen Sie OAuth scopes wie api (Manage user data via APIs) und refresh_token, offline_access (Perform requests at any time).
Aktivieren Sie Use digital signatures, laden Sie ein certificate hoch und speichern Sie den private key in GitHub Secrets.
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.
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.
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.
|
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.
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.
Unverbindlich.