Clean Core is SAP’s strategic answer to the decades-old problem of chaotic, upgrade-threatening SAP modifications. The core idea: Keep the SAP standard code clean and realize extensions exclusively through defined, stable interfaces.
The Problem: Traditional SAP Customizations
Classic Modifications (Pre-Clean Core)
" BAD: Direct intervention in SAP standardENHANCEMENT-POINT ep_vbap_check IN PROGRAM sapmv45a. " Custom logic directly in SAP standard program IF vbap-matnr = 'SPECIAL'. vbap-kwmeng = vbap-kwmeng * 2. ENDIF.END-ENHANCEMENT-POINT.
" BAD: Using non-released tables/fieldsSELECT SINGLE * FROM but000 WHERE partner = lv_partner." but000 is not released - can change at any time!
" BAD: User-Exits/BADIs with unclean accessMETHOD if_ex_badi_name~execute. " Direct access to internal SAP structures DATA(lv_internal) = cl_sap_internal_class=>get_data( ).ENDMETHOD.The Consequences:
- Upgrades are blocked or break customizations
- High maintenance costs (averaging 30-40% of IT budget)
- Slow innovation (fear of breaking changes)
- Technical debt grows exponentially
Clean Core: The Solution
Clean Core means strict separation between:
- SAP Standard Code (Core) - untouched
- Extensions - only via released APIs
+------------------------------------------------------+| SAP Standard || (SAP S/4HANA Core) || || +---------------------------------------------+ || | Released APIs & Extension Points | || | (Guaranteed stable by SAP) | || +----------------------+----------------------+ |+-------------------------|--------------------------+ | | Only via released interfaces | +------------------v--------------------+ | Clean Core Extensions | | | | - Tier-3: In-Stack (ABAP Cloud) | | - Key User Extensibility | | - Side-by-Side (BTP) | | - API Integration | +---------------------------------------+The 3 Tiers: Extensibility Model
SAP defines a 3-tier extension model:
Tier 1: Developer Extensibility (Side-by-Side)
Principle: Extensions run on SAP BTP (outside of S/4HANA)
" Developed on BTP ABAP EnvironmentCLASS zcl_btp_sales_extension DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_oo_adt_classrun.ENDCLASS.
CLASS zcl_btp_sales_extension IMPLEMENTATION. METHOD if_oo_adt_classrun~main. " Access to S/4HANA via OData/APIs DATA(lo_http) = cl_web_http_client_manager=>create_by_http_destination( i_destination = cl_http_destination_provider=>create_by_url( i_url = 'https://my-s4hana.com/sap/opu/odata4/sap/api_salesorder_v1' ) ).
DATA(lo_request) = lo_http->get_http_request( ). lo_request->set_header_field( i_name = 'Accept' i_value = 'application/json' ).
DATA(lo_response) = lo_http->execute( if_web_http_client=>get ). DATA(lv_json) = lo_response->get_text( ).
" Custom business logic " - No access to S/4HANA internals " - Completely isolated ENDMETHOD.ENDCLASS.Advantages:
- Complete decoupling
- Own release cycle
- Scaling independent of S/4HANA
- Any programming languages (ABAP, Java, Node.js…)
Disadvantages:
- Network latency (API calls)
- Higher complexity (two systems)
- Additional license costs (BTP)
Tier 2: Key User Extensibility (No-Code/Low-Code)
Principle: Business users extend S/4HANA without code via tools
Custom Fields (Additional Fields):
SAP GUI -> Settings -> Custom Fields and Logic1. Choose object (e.g., Sales Order)2. "Create New Field"3. Field name: ZZ_PRIORITY4. Data type: String(10)5. Add to UI-> Done! Field is available in table, CDS Views, and UICustom Logic (Business Rules):
1. Choose trigger: "Before Save - Sales Order"2. Condition: IF NetAmount > 100003. Action: SET ZZ_PRIORITY = 'HIGH'-> No ABAP knowledge needed!Advantages:
- Fast (minutes instead of days)
- No developer skills needed
- Integrated in S/4HANA (no latency)
- Automatically upgrade-safe
Disadvantages:
- Limited complexity
- Only for released Business Objects
Tier 3: Developer Extensibility (In-Stack)
Principle: ABAP development inside S/4HANA with ABAP Cloud
This is the sweet spot for classic ABAP developers:
" GOOD: ABAP Cloud Extension in S/4HANA" Only use released APIsCLASS zcl_sales_validator DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_badi_interface.ENDCLASS.
CLASS zcl_sales_validator IMPLEMENTATION. METHOD if_badi_interface~validate. " Access ONLY via Released APIs SELECT SINGLE * FROM i_salesorder " <- Released CDS View (released by SAP) WHERE salesorder = @iv_order_id INTO @DATA(ls_order).
" Business logic IF ls_order-TotalNetAmount > 50000. " Message via Released API DATA(lo_msg) = cl_message_helper=>get_instance( ). " <- Released lo_msg->add_message( id = 'ZMSG' number = '001' v1 = 'Approval required' ). ENDIF. ENDMETHOD.ENDCLASS.How do I recognize Released APIs?
" In Eclipse ADT:" 1. Open API (e.g., CDS View, Class)" 2. Show Properties -> "API State"" 3. Look for "Released" or "C1" (Cloud-Ready)
" Example: I_SalesOrder@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Sales Order'@Metadata.allowExtensions: true@ObjectModel.usageType.serviceQuality: #A@VDM.viewType: #BASIC
" -> If @VDM.viewType is present: Part of Virtual Data Model (VDM)" = Released for Consumption!
define view I_SalesOrder as select from vbak " <- vbak is NOT released, but I_SalesOrder is!{ key vbeln as SalesOrder, erdat as CreationDate, netwr as TotalNetAmount, waerk as TransactionCurrency}Finding Released APIs:
SAP API Business Hub: api.sap.com
Filter: "API Type" -> "ABAP Cloud" "Product" -> "SAP S/4HANA"
Examples:- I_SalesOrder, I_Customer, I_Product (CDS Views)- CL_BALI_LOG (Application Logging)- CL_NUMBERRANGE_RUNTIME (Number Ranges)- CL_WEB_HTTP_* (HTTP Client)Clean Core Implementation Strategy
Phase 1: Assessment (As-Is Analysis)
Use Custom Code Analyzer:
" Transaction: ATC (ABAP Test Cockpit)" Or in Eclipse ADT:" Project -> Properties -> ABAP Development -> Code Inspector
" Check Variant: S4HANA_READINESS_REMOTE" -> Shows all uses of non-released APIsTypical Findings:
| Category | Example | Risk | Effort |
|---|---|---|---|
| Non-released tables | SELECT FROM but000 | High | Medium |
| Implicit Enhancements | ENHANCEMENT-POINT | High | High |
| Non-released APIs | CALL FUNCTION 'RFC_READ_TABLE' | Medium | Low |
| Direct DB access | OPEN CURSOR FOR SELECT * FROM (lv_tabname) | Very high | High |
Phase 2: Cleanup (Remediation)
Strategy 1: Replace APIs
" BEFORE: Non-releasedSELECT SINGLE * FROM but000 WHERE partner = @lv_partner.
" AFTER: Released APISELECT SINGLE * FROM i_businesspartner WHERE businesspartner = @lv_partner INTO @DATA(ls_bp).Strategy 2: Wrapper Layer
" Central wrapper for critical accessCLASS zcl_bp_access DEFINITION PUBLIC CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_bp_access,
get_business_partner IMPORTING iv_partner TYPE bu_partner RETURNING VALUE(rs_partner) TYPE i_businesspartner.
PRIVATE SECTION. CLASS-DATA go_instance TYPE REF TO zcl_bp_access.ENDCLASS.
CLASS zcl_bp_access IMPLEMENTATION. METHOD get_instance. IF go_instance IS NOT BOUND. CREATE OBJECT go_instance. ENDIF. ro_instance = go_instance. ENDMETHOD.
METHOD get_business_partner. " Today: Released API SELECT SINGLE * FROM i_businesspartner WHERE businesspartner = @iv_partner INTO @rs_partner.
" If API is not sufficient: " TODO: Ask SAP for API extension (via Influence Request) ENDMETHOD.ENDCLASS.
" In custom code everywhere:DATA(ls_bp) = zcl_bp_access=>get_instance( )->get_business_partner( lv_partner )." -> If SAP API changes: only adjust zcl_bp_access, not 100 placesStrategy 3: Refactoring to RAP
" BEFORE: Dynpro program with direct DB accessREPORT zsales_order_maintain.
PARAMETERS: p_order TYPE vbeln.
START-OF-SELECTION. " Direct table access UPDATE vbak SET status = 'CLOSED' WHERE vbeln = p_order. COMMIT WORK.
" AFTER: RAP Business Object" -> see /en/rap-basics/ for detailsMODIFY ENTITIES OF i_salesordertp ENTITY SalesOrder UPDATE FIELDS ( OverallSDProcessStatus ) WITH VALUE #( ( SalesOrder = lv_order OverallSDProcessStatus = 'C' ) ) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.Phase 3: Governance (Staying Clean Long-Term)
1. ATC Checks in CI/CD Pipeline
# Azure DevOps / Jenkins Pipeline- task: ABAP_ATC_Check inputs: checkVariant: 'CUSTOM_CLOUD_READINESS' failOnErrors: true excludePackages: '$TMP' # Exclude test developments2. Document Development Guidelines
# ABAP Development Standards (Example)
## Mandatory Rules1. Only Released APIs (Check: API Business Hub)2. ABAP Cloud Syntax (no CALL TRANSACTION, etc.)3. RAP for transactional apps (no Dynpro)4. CDS Views instead of SELECT on DB tables5. No Implicit Enhancement Framework
## Recommendations- Wrapper classes for frequently used APIs- Unit Tests with Test Doubles (see /en/test-doubles-mocking/)- EML for BO access (see /en/eml-guide/)3. Code Reviews with Checklist
Pull Request Checklist:[ ] ATC check green (Clean Core Variant)?[ ] Only Released APIs used?[ ] Unit Tests present (Coverage >= 80%)?[ ] CDS Views documented (Labels/Descriptions)?[ ] No Implicit Enhancements?Clean Core & ABAP Cloud
ABAP Cloud = Technical Implementation of Clean Core
" ABAP Cloud Language Version: Compiler enforces Clean Core!
CLASS zcl_order_processor DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_oo_adt_classrun.ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION. METHOD if_oo_adt_classrun~main. " Allowed: Released API SELECT * FROM i_salesorder INTO TABLE @DATA(lt_orders).
" Compiler error: vbak is not released! " SELECT * FROM vbak INTO TABLE @DATA(lt_vbak). " -> "vbak is not released for ABAP Cloud"
" Compiler error: CALL TRANSACTION not allowed " CALL TRANSACTION 'VA03'. " -> "CALL TRANSACTION is not allowed in ABAP Cloud" ENDMETHOD.ENDCLASS.Enable ABAP Cloud in S/4HANA:
Eclipse ADT:1. Create/Open Package2. Properties -> ABAP Language Version3. Select: "ABAP for Cloud Development"-> All objects in this package are Clean Core compliant!Migration: Step by Step
Example: Sales Order Enhancement
Starting Situation (Classic ABAP):
" User-Exit built into SAP StandardENHANCEMENT-SECTION zenhancement_vbap IN sapmv45a. ENHANCEMENT 1 zorder_validation. " Access to global variables of SAP program IF vbap-matnr CO '0123456789'. MESSAGE 'Invalid material number' TYPE 'E'. ENDIF. ENDENHANCEMENT.END-ENHANCEMENT-SECTION.Migration to Clean Core (ABAP Cloud + RAP):
" Step 1: Use BAdI (if available)CLASS zcl_order_badi DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_ex_sd_sales_item_check. " <- SAP BAdIENDCLASS.
CLASS zcl_order_badi IMPLEMENTATION. METHOD if_ex_sd_sales_item_check~validate_item. " Access only via interface parameters (no global access) IF is_item-material CO '0123456789'. " Released API for Messages DATA(lo_msg) = cl_bali_message_setter=>create( severity = if_bali_constants=>c_severity_error id = 'ZMSG' number = '001' ). APPEND lo_msg TO ct_messages. ENDIF. ENDMETHOD.ENDCLASS.
" Step 2: If no BAdI -> Wrapper Event" See RAP Business Events: /en/events-raise-handler/
" Step 3: Long-term -> RAP Validation" In Behavior Definition:validation validateMaterialNumber on save { field Material; }
" Behavior Implementation:METHOD validateMaterialNumber. 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) WHERE Material CO '0123456789'. APPEND VALUE #( %tky = ls_item-%tky %element-Material = if_abap_behv=>mk-on ) TO failed-salesorderitem.
APPEND VALUE #( %tky = ls_item-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Invalid material number' ) ) TO reported-salesorderitem. ENDLOOP.ENDMETHOD.Tools for Clean Core
| Tool | Purpose | Transaction/Link |
|---|---|---|
| Custom Code Migration App | Analysis + Tracking | Fiori Launchpad: F2802 |
| ATC (ABAP Test Cockpit) | Static Code Analysis | SE80/ADT |
| SAP API Business Hub | Find Released APIs | api.sap.com |
| ABAP Cloud Flight Checker | Clean Core Violations | Eclipse ADT |
| Code Inspector | Legacy Checks | SCI/ATC |
| Transport Dependency Analyzer | Impact Analysis | SE03 |
Important Notes / Best Practice
- Clean Core is not optional: From S/4HANA 2025+ it becomes mandatory for Cloud editions
- Start early: Migration takes 2-5 years depending on custom code volume
- 80/20 rule: 20% of code causes 80% of problems - prioritize!
- Use SAP Influence: Missing a Released API? -> influence.sap.com
- Don’t migrate everything: Retire old, rarely used programs
- Wrapper Pattern: Encapsulate transition code in central classes
- ABAP Cloud = Compiler support: Use the Language Version for new developments
- Test Doubles: See Test Doubles & Mocking for Clean Testing
- RAP-First: Always build new transactional apps with RAP (see RAP Basics)
- Documentation: Justify every deviation (e.g., “SAP delivers API in release XY”)
- Establish Governance: Pull Requests + ATC Checks = permanent Clean Core
- Training: Train team - Clean Core is mindset change, not just syntax
Further Resources
- ABAP Cloud: /en/abap-cloud-definition/
- RAP Basics: /en/rap-basics/
- Migration Guide: /en/migration-classic-to-cloud/