Migration von Classic ABAP zu ABAP Cloud: Der Praxisleitfaden

kategorie
General
Veröffentlicht
autor
Johannes

Die Migration zu ABAP Cloud ist die größte technische Herausforderung für SAP-Kunden im S/4HANA-Zeitalter. Dieser Leitfaden zeigt Ihnen Schritt für Schritt, wie Sie Legacy-ABAP-Code in moderne, cloud-fähige Anwendungen transformieren.

Warum migrieren?

Technische Gründe:

  • Upgrade-Sicherheit: Released APIs brechen nicht bei S/4HANA-Updates
  • Cloud-Readiness: Läuft auf SAP BTP und S/4HANA Cloud
  • Performance: Moderne Patterns (CDS, RAP) nutzen HANA optimal
  • Wartbarkeit: Klare Architektur statt Spaghetti-Code

Business-Gründe:

  • 💰 Reduzierte TCO: Weniger Aufwand bei Upgrades (30-50% Einsparung)
  • 🚀 Schnellere Innovation: Neue Features ohne Breaking Changes
  • ☁️ Cloud-Strategie: Voraussetzung für S/4HANA Cloud (Public/Private)
  • 📊 Compliance: SAP deprecates Classic ABAP langfristig

Migrations-Phasen im Überblick

┌────────────────────────────────────────────────────────────┐
│ Phase 1: ASSESSMENT (2-4 Wochen) │
│ → Custom Code analysieren │
│ → Komplexität bewerten │
│ → Roadmap erstellen │
└────────────┬───────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ Phase 2: PREPARATION (4-8 Wochen) │
│ → Tools einrichten (ATC, Custom Code Migration App) │
│ → Team schulen │
│ → Development Guidelines definieren │
└────────────┬───────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ Phase 3: REMEDIATION (6-24 Monate) │
│ → APIs ersetzen │
│ → Refactoring zu RAP │
│ → Tests schreiben │
└────────────┬───────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ Phase 4: VALIDATION (2-4 Wochen) │
│ → End-to-End Tests │
│ → Performance-Vergleich │
│ → User Acceptance Tests │
└────────────┬───────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ Phase 5: GOVERNANCE (ongoing) │
│ → ATC in CI/CD │
│ → Code Reviews │
│ → Continuous Improvement │
└────────────────────────────────────────────────────────────┘

Phase 1: Assessment

Custom Code analysieren

Tool: ABAP Test Cockpit (ATC)

" In Eclipse ADT:
" 1. Projekt → Properties → ABAP Development → ATC
" 2. Check Variant: S4HANA_READINESS_REMOTE
" 3. Run → ATC Check
" Oder im System:
" Transaktion: ATC
" Check Variant: S4HANA_CLOUD_DEVELOPMENT

Typische Findings:

KategorieBeispielAufwandPriorität
Nicht-freigegebene TabellenSELECT FROM but000MittelHoch
Obsolete StatementsCALL TRANSACTIONHochHoch
Implicit EnhancementsENHANCEMENT-POINTSehr hochKritisch
Dynpro/ScreensMODULE status_0100 OUTPUTSehr hochMittel
Nicht-released FuBasCALL FUNCTION 'RFC_READ_TABLE'NiedrigMittel
DB-Direkt-ZugriffEXEC SQLHochHoch

Custom Code Migration App (Fiori)

SAP GUI → Fiori Launchpad
→ App: "Custom Code Migration" (F2802)
Features:
- Dashboard mit Finding-Übersicht
- Kategorisierung nach Aufwand
- Tracking von Remediation-Status
- Empfehlungen für Alternativen

Priorisierungskriterien:

Priorität = (Business Impact × Usage Frequency × Technical Risk) / Effort
Beispiel:
- Billing Report: Impact=10, Frequency=1000, Risk=8, Effort=40
→ Priority = (10 × 1000 × 8) / 40 = 2000 ⭐⭐⭐
- Selten genutzter Report: Impact=2, Frequency=5, Risk=3, Effort=50
→ Priority = (2 × 5 × 3) / 50 = 0.6 → Stilllegung prüfen!

Phase 2: Preparation

ABAP Cloud Entwicklungsumgebung

" 1. Package mit ABAP Cloud Language Version erstellen
" Eclipse ADT:
" → New ABAP Package: Z_CLOUD_TRAVEL
" → Properties → ABAP Language Version: "ABAP for Cloud Development"
" 2. Alle Objekte in diesem Package sind automatisch Cloud-konform!
" → Compiler blockiert nicht-released APIs
" → Nur ABAP Cloud Syntax erlaubt

Wrapper-Pattern einrichten

" Zentraler Wrapper für Legacy-Zugriffe
" (während Migration schrittweise ersetzen)
INTERFACE zif_bp_facade.
METHODS:
get_business_partner
IMPORTING iv_partner TYPE bu_partner
RETURNING VALUE(rs_partner) TYPE i_businesspartner
RAISING cx_static_check.
ENDINTERFACE.
CLASS zcl_bp_facade DEFINITION PUBLIC CREATE PRIVATE.
PUBLIC SECTION.
INTERFACES zif_bp_facade.
CLASS-METHODS get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_bp_facade.
PRIVATE SECTION.
CLASS-DATA go_instance TYPE REF TO zcl_bp_facade.
ENDCLASS.
CLASS zcl_bp_facade IMPLEMENTATION.
METHOD get_instance.
IF go_instance IS NOT BOUND.
CREATE OBJECT go_instance.
ENDIF.
ro_instance = go_instance.
ENDMETHOD.
METHOD zif_bp_facade~get_business_partner.
" Heute: Released API
SELECT SINGLE * FROM i_businesspartner
WHERE businesspartner = @iv_partner
INTO @rs_partner.
IF sy-subrc <> 0.
" Falls API nicht reicht: Temporär Legacy-Zugriff
" TODO: SAP um API-Enhancement bitten (Influence Request)
" SELECT SINGLE * FROM but000 ... " ← Langfristig entfernen!
RAISE EXCEPTION TYPE zcx_bp_not_found.
ENDIF.
ENDMETHOD.
ENDCLASS.
" In Legacy-Code überall:
DATA(ls_bp) = zcl_bp_facade=>get_instance( )->zif_bp_facade~get_business_partner( lv_partner ).
" → Nur 1 Stelle anpassen wenn SAP API ändert!

Phase 3: Remediation

Strategie 1: API-Ersetzung (Quick Wins)

Beispiel 1: Tabellen → CDS Views

" ❌ Vorher: Nicht-released Tabelle
SELECT kunnr, name1, ort01, land1
FROM kna1
WHERE land1 = 'DE'
INTO TABLE @DATA(lt_customers).
" ✅ Nachher: Released CDS View
SELECT customernumber, customername, cityname, country
FROM i_customer
WHERE country = 'DE'
INTO TABLE @DATA(lt_customers).

API finden:

  • SAP API Business Hub: api.sap.com
  • Filter: “API Type” → “ABAP Cloud”
  • Suche nach Domain (z.B. “Customer”, “Sales Order”)

Beispiel 2: Funktionsbausteine → Klassen

" ❌ Vorher: Nicht-released Function
CALL FUNCTION 'BAPI_MATERIAL_GET_DETAIL'
EXPORTING
material = lv_matnr
IMPORTING
material_general_data = ls_data.
" ✅ Nachher: Released API (falls verfügbar)
SELECT SINGLE * FROM i_product
WHERE product = @lv_matnr
INTO @DATA(ls_product).
" ✅ Alternative: Released Wrapper-Klasse
TRY.
DATA(lo_material) = cl_md_bp_material=>get_instance( lv_matnr ).
DATA(ls_data) = lo_material->get_data( ).
CATCH cx_md_bp_material INTO DATA(lx_mat).
" Error handling
ENDTRY.

Beispiel 3: CALL TRANSACTION → RAP/Fiori

" ❌ Vorher: Dynpro via CALL TRANSACTION
CALL TRANSACTION 'VA03'
WITH PARAMETERS p_vbeln = lv_order
AND SKIP FIRST SCREEN.
" ✅ Nachher: RAP Business Object + Fiori App
" → UI wird Fiori Elements, keine Transaction Calls mehr
" Backend: RAP Intent-based Navigation
" (wenn nötig, ansonsten direkter BO-Zugriff via EML)

Strategie 2: Reports zu RAP Query migrieren

Beispiel: ALV Report → RAP Query + Fiori

Vorher: Classic Report (200+ Zeilen)

" ❌ salesorder_report.prog.abap
REPORT zsalesorder_report.
TABLES: vbak, vbap, kna1.
SELECT-OPTIONS: s_vbeln FOR vbak-vbeln,
s_erdat FOR vbak-erdat,
s_kunnr FOR vbak-kunnr.
START-OF-SELECTION.
SELECT v~vbeln, v~erdat, v~kunnr, k~name1, v~netwr
FROM vbak AS v
INNER JOIN kna1 AS k ON v~kunnr = k~kunnr
WHERE v~vbeln IN @s_vbeln
AND v~erdat IN @s_erdat
AND v~kunnr IN @s_kunnr
INTO TABLE @DATA(lt_orders).
" ALV anzeigen (50 Zeilen Code für ALV-Setup...)
" ...

Nachher: RAP Query (10 Zeilen + UI auto-generiert!)

-- 1. CDS View mit Parameters
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Report'
@Metadata.allowExtensions: true
@UI.headerInfo.typeName: 'Sales Order'
define view entity ZC_SalesOrderReport
with parameters
@Consumption.defaultValue: ''
@EndUserText.label: 'Sales Order'
p_SalesOrder : vbeln_va,
@Consumption.defaultValue: ''
@EndUserText.label: 'Customer'
p_Customer : kunnr
as select from I_SalesOrder as Order
association [0..1] to I_Customer as _Customer
on Order.SoldToParty = _Customer.Customer
{
@UI.lineItem: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
key Order.SalesOrder,
@UI.lineItem: [{ position: 20 }]
Order.SalesOrderDate,
@UI.lineItem: [{ position: 30 }]
@UI.selectionField: [{ position: 20 }]
Order.SoldToParty,
@UI.lineItem: [{ position: 40 }]
_Customer.CustomerName,
@UI.lineItem: [{ position: 50 }]
@Semantics.amount.currencyCode: 'TransactionCurrency'
Order.TotalNetAmount,
Order.TransactionCurrency
}
where
Order.SalesOrder like $parameters.p_SalesOrder
and Order.SoldToParty like $parameters.p_Customer
-- 2. Service Definition
@EndUserText.label: 'Sales Order Report Service'
define service ZUI_SalesOrderReport {
expose ZC_SalesOrderReport as SalesOrder;
}
3. Service Binding (OData V4 UI)
→ In ADT: New Service Binding
→ Binding Type: OData V4 - UI
→ Publish
4. Fiori Preview starten
→ UI ist AUTOMATISCH generiert (Fiori Elements)!
→ Keine ALV-Programmierung mehr nötig

Ergebnis:

  • 🟢 200 Zeilen → 30 Zeilen (85% weniger Code)
  • 🟢 UI auto-generiert (Fiori Elements statt ALV)
  • 🟢 Responsive (Mobile, Tablet, Desktop)
  • 🟢 Filter/Search/Export out-of-the-box
  • 🟢 Cloud-fähig

Strategie 3: Dynpro → RAP Fiori App

Beispiel: Transaktionscode VA03 → RAP BO

Vorher: Dynpro-Transaktion (1000+ Zeilen)

" ❌ Z_SALESORDER_EDIT
" - Dynpro Screens (100, 200, 300...)
" - PAI/PBO Modules
" - Table Controls
" - OK-Code Handling
" → 1000+ Zeilen nicht-wartbarer Code

Nachher: RAP Business Object (100 Zeilen + UI auto)

-- 1. CDS Root View
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order - Editable'
define root view entity ZI_SalesOrderEdit
as select from I_SalesOrder
composition [0..*] of ZI_SalesOrderItemEdit as _Items
{
key SalesOrder,
SalesOrderType,
SalesOrganization,
SoldToParty,
CreationDate,
TotalNetAmount,
TransactionCurrency,
OverallSDProcessStatus,
_Items
}
-- 2. Behavior Definition
managed implementation in class zbp_i_salesorderedit unique;
strict ( 2 );
with draft;
define behavior for ZI_SalesOrderEdit alias SalesOrder
persistent table I_SalesOrder // Oder eigene Tabelle
draft table zsalesorder_draft
lock master
total etag LastChangedAt
authorization master ( instance )
{
// CRUD
create;
update;
delete;
// Fields
field ( readonly ) SalesOrder;
field ( mandatory ) SoldToParty, SalesOrderType;
// Actions
action release result [1] $self;
action block result [1] $self;
// Draft
draft action Edit;
draft action Activate;
draft action Discard;
draft action Resume;
draft determine action Prepare;
// Composition
association _Items { create; with draft; }
}
define behavior for ZI_SalesOrderItemEdit alias Item
persistent table I_SalesOrderItem
draft table zsalesorderitem_draft
lock dependent by _SalesOrder
authorization dependent by _SalesOrder
{
update;
delete;
field ( readonly ) SalesOrder, SalesOrderItem;
field ( mandatory ) Material, RequestedQuantity;
association _SalesOrder { with draft; }
}
-- 3. Projection View (UI-spezifisch)
@EndUserText.label: 'Sales Order - Projection'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@UI.headerInfo: {
typeName: 'Sales Order',
typeNamePlural: 'Sales Orders',
title: { value: 'SalesOrder' }
}
define root view entity ZC_SalesOrderEdit
provider contract transactional_query
as projection on ZI_SalesOrderEdit
{
@UI.facet: [
{ position: 10, type: #IDENTIFICATION_REFERENCE, label: 'General' },
{ position: 20, type: #LINEITEM_REFERENCE, label: 'Items', targetElement: '_Items' }
]
@UI.identification: [{ position: 10 }]
@UI.lineItem: [{ position: 10 }]
key SalesOrder,
@UI.identification: [{ position: 20 }]
@UI.lineItem: [{ position: 20 }]
SalesOrderType,
@UI.identification: [{ position: 30 }]
@UI.lineItem: [{ position: 30 }]
SoldToParty,
@UI.identification: [{ position: 40 }]
TotalNetAmount,
TransactionCurrency,
_Items : redirected to composition child ZC_SalesOrderItemEdit
}
-- 4. Service Definition + Binding (wie vorher)
-- → Fiori Elements UI automatisch!

Ergebnis:

  • 🟢 1000 Zeilen Dynpro → 100 Zeilen RAP (90% weniger)
  • 🟢 UI responsive (statt Fixed-Screens)
  • 🟢 Draft-Funktionalität (Zwischenspeichern)
  • 🟢 Validation/Actions klar strukturiert
  • 🟢 Wartbar und testbar

Strategie 4: Enhancements → RAP Business Events

Beispiel: Implicit Enhancement → Event

Vorher: Enhancement Point

" ❌ SAP-Standardprogramm (z.B. SAPMV45A)
ENHANCEMENT-SECTION zenhancement_vbap.
ENHANCEMENT 1 zorder_validation.
" Globaler Zugriff auf SAP-Variablen (fragil!)
IF vbap-matnr = 'BLOCKED_MAT'.
MESSAGE 'Material gesperrt' TYPE 'E'.
ENDIF.
ENDENHANCEMENT.
END-ENHANCEMENT-SECTION.

Nachher: RAP Business Event

-- 1. Event in Behavior Definition
define behavior for ZI_SalesOrder alias SalesOrder
{
// ...
event MaterialChecked parameter ZA_MaterialCheckParam;
}
-- 2. Event im Behavior Implementation auslösen
METHOD validateMaterial.
READ ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrderItem
FIELDS ( Material )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_items).
LOOP AT lt_items INTO DATA(ls_item).
" Prüfung
IF ls_item-Material = 'BLOCKED_MAT'.
" Event auslösen
RAISE ENTITY EVENT zi_salesorder~MaterialChecked
FROM VALUE #( ( %key-SalesOrder = ls_item-SalesOrder
%param-Material = ls_item-Material
%param-BlockReason = 'Quality issue' ) ).
" Fehler melden
APPEND VALUE #( %tky = ls_item-%tky ) TO failed-salesorderitem.
APPEND VALUE #(
%tky = ls_item-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Material gesperrt'
)
) TO reported-salesorderitem.
ENDIF.
ENDLOOP.
ENDMETHOD.
-- 3. Event Consumer (optional, für Side-Effects)
CLASS zcl_material_event_handler DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_material_event_handler IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
" Reagiere auf MaterialChecked Event
CASE event_key.
WHEN 'MaterialChecked'.
" Z.B. Email senden, externes System benachrichtigen
DATA(lv_material) = event_data[ 1 ]-%param-Material.
cl_email_sender=>send_alert(
subject = |Material { lv_material } blockiert|
recipient = 'quality@company.com'
).
ENDCASE.
ENDMETHOD.
ENDCLASS.

Vorteile:

  • ✅ Kein Zugriff auf SAP-Interna
  • ✅ Klare Event-basierte Architektur
  • ✅ Entkoppelt (Consumer optional)
  • ✅ Testbar

Phase 4: Validation

Testing-Strategie

" 1. Unit Tests mit Test Doubles (siehe /test-doubles-mocking/)
CLASS ltc_salesorder DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_env TYPE REF TO if_cds_test_environment.
METHODS:
setup,
teardown,
test_validate_material FOR TESTING.
ENDCLASS.
CLASS ltc_salesorder IMPLEMENTATION.
METHOD setup.
mo_env = cl_cds_test_environment=>create_for_multiple_cds(
i_for_entities = VALUE #(
( i_for_entity = 'ZI_SalesOrder' )
( i_for_entity = 'ZI_SalesOrderItem' )
)
).
ENDMETHOD.
METHOD test_validate_material.
" Arrange: Testdaten
mo_env->insert_test_data(
i_data = VALUE zi_salesorder( ( SalesOrder = '0001' ) )
).
" Act: Validation ausführen
MODIFY ENTITIES OF zi_salesorder
ENTITY SalesOrderItem
CREATE FIELDS ( Material Quantity )
WITH VALUE #( ( SalesOrder = '0001' Material = 'BLOCKED_MAT' Quantity = 10 ) )
FAILED DATA(failed).
" Assert: Fehler erwartet
cl_abap_unit_assert=>assert_not_initial( failed-salesorderitem ).
ENDMETHOD.
METHOD teardown.
mo_env->destroy( ).
ENDMETHOD.
ENDCLASS.
" 2. Integration Tests (E2E via OData)
CLASS ltc_integration DEFINITION FINAL FOR TESTING
DURATION MEDIUM RISK LEVEL DANGEROUS.
PRIVATE SECTION.
DATA mo_client TYPE REF TO if_web_http_client.
METHODS:
test_create_order_via_odata FOR TESTING.
ENDCLASS.
CLASS ltc_integration IMPLEMENTATION.
METHOD test_create_order_via_odata.
" OData POST simulieren
mo_client = cl_web_http_client_manager=>create_by_http_destination( ... ).
DATA(lv_payload) = `{"SalesOrderType":"OR","SoldToParty":"0001"}`.
DATA(lo_request) = mo_client->get_http_request( ).
lo_request->set_text( lv_payload ).
lo_request->set_header_field( i_name = 'Content-Type' i_value = 'application/json' ).
DATA(lo_response) = mo_client->execute( if_web_http_client=>post ).
" Assert: Status 201 Created
cl_abap_unit_assert=>assert_equals(
exp = 201
act = lo_response->get_status( )-code
).
ENDMETHOD.
ENDCLASS.

Performance-Vergleich

" Benchmark: Alt vs. Neu
REPORT z_performance_benchmark.
DATA: lv_start TYPE timestampl,
lv_end TYPE timestampl.
" Alt: SELECT FROM vbak
GET TIME STAMP FIELD lv_start.
SELECT * FROM vbak INTO TABLE @DATA(lt_vbak) UP TO 10000 ROWS.
GET TIME STAMP FIELD lv_end.
DATA(lv_time_old) = cl_abap_tstmp=>subtract( tstmp1 = lv_end tstmp2 = lv_start ).
" Neu: SELECT FROM I_SalesOrder (CDS View)
GET TIME STAMP FIELD lv_start.
SELECT * FROM i_salesorder INTO TABLE @DATA(lt_orders) UP TO 10000 ROWS.
GET TIME STAMP FIELD lv_end.
DATA(lv_time_new) = cl_abap_tstmp=>subtract( tstmp1 = lv_end tstmp2 = lv_start ).
WRITE: / 'Alt:', lv_time_old, 'Sekunden'.
WRITE: / 'Neu:', lv_time_new, 'Sekunden'.
WRITE: / 'Speedup:', lv_time_old / lv_time_new, 'x'.

Phase 5: Governance

CI/CD Integration

# Azure DevOps Pipeline
trigger:
branches:
include:
- main
- develop
stages:
- stage: Build
jobs:
- job: ABAP_ATC_Check
steps:
- task: ABAP_Unit_Tests@1
inputs:
sapSystem: 'S4D'
package: 'Z_CLOUD_*'
- task: ABAP_ATC@1
inputs:
checkVariant: 'ABAP_CLOUD_READINESS'
failOnErrors: true
maxFindings: 0 # Keine Findings erlaubt!
- task: Code_Coverage@1
inputs:
minimumCoverage: 80 # 80% Coverage Pflicht

Pull Request Checklist

## ABAP Cloud PR Checklist
Vor Merge prüfen:
### Technisch
- [ ] ATC-Check grün (ABAP_CLOUD_READINESS)
- [ ] Unit Tests vorhanden (Coverage ≥ 80%)
- [ ] Nur Released APIs verwendet
- [ ] Keine Implicit Enhancements
- [ ] ABAP Cloud Language Version
### Dokumentation
- [ ] Änderungen im Changelog dokumentiert
- [ ] API-Dokumentation aktualisiert
- [ ] Migrationsnoten für andere Teams
### Testing
- [ ] Unit Tests erfolgreich
- [ ] Integration Tests erfolgreich
- [ ] Manuelle Tests durchgeführt
### Review
- [ ] Code Review durch 2. Entwickler
- [ ] Architektur Review (bei größeren Changes)

Migrations-Fallstricke (und wie Sie sie vermeiden)

Fallstrick 1: “Big Bang” Migration

Fehler: Alle Custom-Objekte auf einmal migrieren

Lösung: Inkrementell migrieren

Woche 1-4: 10% (Quick Wins: API-Ersetzungen)
Woche 5-12: 30% (Reports → RAP Queries)
Woche 13-24: 40% (Dynpro → RAP Fiori)
Woche 25+: 20% (Komplexe Legacy-Integration)

Fallstrick 2: API-Lücken ignorieren

Fehler: Wenn keine Released API existiert → aufgeben

Lösung: 3-Stufen-Ansatz

  1. Wrapper erstellen (temporär Legacy-Zugriff kapseln)
  2. SAP Influence Request (api.sap.com → Request Enhancement)
  3. Workaround dokumentieren (für spätere Ablösung)

Fallstrick 3: Testing vernachlässigen

Fehler: “Funktioniert doch, brauche keine Tests”

Lösung: Test-first Migration

" 1. Erst Legacy-Verhalten als Test dokumentieren
METHOD test_legacy_behavior.
" Act: Legacy-Code ausführen
CALL FUNCTION 'Z_LEGACY_CALC' ...
" Assert: Ergebnis dokumentieren
cl_abap_unit_assert=>assert_equals( exp = 42 act = lv_result ).
ENDMETHOD.
" 2. DANN migrieren
" 3. Test MUSS weiterhin grün sein!

Wichtige Hinweise / Best Practice

  • Inkrementell migrieren: Nicht alles auf einmal, sondern Schritt für Schritt
  • 80/20-Regel: 20% des Codes verursachen 80% der Probleme – priorisieren!
  • Quick Wins first: API-Ersetzungen sind schnell → früh umsetzen für Motivation
  • Wrapper-Pattern: Legacy-Code kapseln während Migration
  • SAP Influence nutzen: Fehlende APIs bei SAP anfordern (influence.sap.com)
  • Training: Team schulen BEVOR Migration startet (RAP, CDS, Fiori)
  • Realistische Timeline: 2-5 Jahre für große Custom-Code-Basis normal
  • Sunset statt Migrate: Alte, selten genutzte Programme stilllegen
  • Test Doubles: Unit Tests mit CDS Test Environment (siehe Test Doubles)
  • Clean Core: Migration = Clean Core implementieren (siehe Clean Core Guide)
  • Continuous: Migration ist kein Projekt, sondern Dauerzustand (neue Entwicklungen = ABAP Cloud)
  • Dokumentieren: Jede Abweichung begründen (z.B. “API fehlt, SAP-Ticket: 123456”)

Weitere Ressourcen