Clean Core Strategy for S/4HANA: The Complete Guide

Category
General
Published
Author
Johannes

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 standard
ENHANCEMENT-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/fields
SELECT SINGLE * FROM but000 WHERE partner = lv_partner.
" but000 is not released - can change at any time!
" BAD: User-Exits/BADIs with unclean access
METHOD 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:

  1. SAP Standard Code (Core) - untouched
  2. 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 Environment
CLASS 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 Logic
1. Choose object (e.g., Sales Order)
2. "Create New Field"
3. Field name: ZZ_PRIORITY
4. Data type: String(10)
5. Add to UI
-> Done! Field is available in table, CDS Views, and UI

Custom Logic (Business Rules):

1. Choose trigger: "Before Save - Sales Order"
2. Condition: IF NetAmount > 10000
3. 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 APIs
CLASS 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 APIs

Typical Findings:

CategoryExampleRiskEffort
Non-released tablesSELECT FROM but000HighMedium
Implicit EnhancementsENHANCEMENT-POINTHighHigh
Non-released APIsCALL FUNCTION 'RFC_READ_TABLE'MediumLow
Direct DB accessOPEN CURSOR FOR SELECT * FROM (lv_tabname)Very highHigh

Phase 2: Cleanup (Remediation)

Strategy 1: Replace APIs

" BEFORE: Non-released
SELECT SINGLE * FROM but000 WHERE partner = @lv_partner.
" AFTER: Released API
SELECT SINGLE * FROM i_businesspartner
WHERE businesspartner = @lv_partner
INTO @DATA(ls_bp).

Strategy 2: Wrapper Layer

" Central wrapper for critical access
CLASS 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 places

Strategy 3: Refactoring to RAP

" BEFORE: Dynpro program with direct DB access
REPORT 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 details
MODIFY 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 developments

2. Document Development Guidelines

# ABAP Development Standards (Example)
## Mandatory Rules
1. 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 tables
5. 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 Package
2. Properties -> ABAP Language Version
3. 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 Standard
ENHANCEMENT-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 BAdI
ENDCLASS.
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

ToolPurposeTransaction/Link
Custom Code Migration AppAnalysis + TrackingFiori Launchpad: F2802
ATC (ABAP Test Cockpit)Static Code AnalysisSE80/ADT
SAP API Business HubFind Released APIsapi.sap.com
ABAP Cloud Flight CheckerClean Core ViolationsEclipse ADT
Code InspectorLegacy ChecksSCI/ATC
Transport Dependency AnalyzerImpact AnalysisSE03

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