ABAP Cleaner is an open-source tool that automatically cleans up, formats, and modernizes ABAP code. It applies over 60 cleanup rules and helps enforce consistent code standards across teams.
What is ABAP Cleaner?
ABAP Cleaner analyzes ABAP code and automatically transforms it according to configurable rules. Unlike the Pretty Printer, ABAP Cleaner goes far beyond pure formatting and also modernizes language constructs.
| Aspect | Pretty Printer | ABAP Cleaner |
|---|---|---|
| Focus | Indentation, case | Complete code modernization |
| Rules | ~5 basic rules | 60+ configurable rules |
| Language constructs | No changes | Modernization (e.g., NEW instead of CREATE OBJECT) |
| Empty lines | Simple rules | Intelligent grouping |
| Comments | No changes | Alignment and formatting |
| Performance | - | Removal of unnecessary statements |
| Profiles | Fixed | Individually configurable |
Benefits
- Consistency: Uniform code style across the entire team
- Productivity: Automatic cleanup saves manual work
- Modernization: Old syntax is automatically updated
- Readability: Better structure and formatting
- Code Review: Fewer discussions about style questions
- Onboarding: New team members write compliant code immediately
Installation in ADT
Prerequisites
- Eclipse with ADT (ABAP Development Tools)
- Eclipse version 2022-03 or newer
- Java 11 or newer
Installation via Eclipse Marketplace
- Open Eclipse and navigate to Help → Eclipse Marketplace
- Search for “ABAP Cleaner”
- Click Install and follow the instructions
- Restart Eclipse
Manual Installation via Update Site
If the Marketplace is not available:
Help → Install New Software...
Update Site URL:https://sap.github.io/abap-cleaner/updatesite
Available Features:☑ ABAP Cleaner
→ Next → Accept License → Finish → Restart EclipseVerify Installation
After restart, ABAP Cleaner should be available:
┌──────────────────────────────────────────────────────────────┐│ ADT Toolbar │├──────────────────────────────────────────────────────────────┤│ ││ [📁] [💾] [↩️] [↪️] ... [🧹 ABAP Cleaner] ││ ││ Or: Source → Clean Up With Interactive ABAP Cleaner... ││ Shortcut: Ctrl+4 (configurable) ││ │└──────────────────────────────────────────────────────────────┘Key Cleanup Rules
ABAP Cleaner groups its rules into categories. Here are the most important ones:
1. Empty Lines - Optimize Whitespace
Intelligent grouping of code blocks:
Before:
METHOD process_order.
DATA lv_status TYPE string. DATA lv_amount TYPE p DECIMALS 2.
SELECT SINGLE status FROM zorders INTO lv_status WHERE order_id = iv_order_id.
IF lv_status = 'OPEN'. lv_amount = calculate_total( ).
update_order( lv_amount ). ENDIF.
ENDMETHOD.After:
METHOD process_order. DATA lv_status TYPE string. DATA lv_amount TYPE p DECIMALS 2.
SELECT SINGLE status FROM zorders INTO lv_status WHERE order_id = iv_order_id.
IF lv_status = 'OPEN'. lv_amount = calculate_total( ). update_order( lv_amount ). ENDIF.ENDMETHOD.2. Declarations - Modernize Declarations
Before:
DATA: lv_count TYPE i, lt_items TYPE TABLE OF zitem, ls_item TYPE zitem, lo_processor TYPE REF TO zcl_processor.
CREATE OBJECT lo_processor.CLEAR lt_items.After:
DATA lv_count TYPE i.DATA lt_items TYPE TABLE OF zitem.DATA ls_item TYPE zitem.DATA lo_processor TYPE REF TO zcl_processor.
lo_processor = NEW #( ).CLEAR lt_items.3. Syntax - Use Modern ABAP Syntax
Before:
CALL METHOD lo_service->get_data EXPORTING iv_id = lv_id RECEIVING rt_data = lt_data.
IF lt_data IS INITIAL. " ...ENDIF.
LOOP AT lt_data INTO ls_data. MOVE-CORRESPONDING ls_data TO ls_result. APPEND ls_result TO lt_result.ENDLOOP.After:
lt_data = lo_service->get_data( iv_id = lv_id ).
IF lt_data IS INITIAL. " ...ENDIF.
LOOP AT lt_data INTO DATA(ls_data). lt_result = VALUE #( BASE lt_result ( CORRESPONDING #( ls_data ) ) ).ENDLOOP.4. Commands - Remove Unnecessary Statements
Before:
CLEAR lv_result.lv_result = calculate_value( ).
IF sy-subrc = 0. " SuccessENDIF.
MOVE lv_source TO lv_target.After:
lv_result = calculate_value( ).
IF sy-subrc = 0. " SuccessENDIF.
lv_target = lv_source.5. Alignment - Improve Alignment
Before:
ls_order-order_id = lv_id.ls_order-customer = lv_customer.ls_order-status = 'NEW'.ls_order-created_at = sy-datum.ls_order-created_by = sy-uname.ls_order-amount = lv_amount.After:
ls_order-order_id = lv_id.ls_order-customer = lv_customer.ls_order-status = 'NEW'.ls_order-created_at = sy-datum.ls_order-created_by = sy-uname.ls_order-amount = lv_amount.6. Pretty Print - Formatting
Before:
IF LV_STATUS='OPEN'AND LV_AMOUNT>100. CALL METHOD LO_SERVICE->PROCESS(EXPORTING IV_ID=LV_ID CHANGING CT_DATA=LT_DATA).ENDIF.After:
IF lv_status = 'OPEN' AND lv_amount > 100. lo_service->process( EXPORTING iv_id = lv_id CHANGING ct_data = lt_data ).ENDIF.7. Inline Declarations - Use VALUE and NEW
Before:
DATA lt_orders TYPE TABLE OF zorder.DATA ls_order TYPE zorder.DATA lo_service TYPE REF TO zcl_order_service.
CREATE OBJECT lo_service EXPORTING iv_client = sy-mandt.
ls_order-order_id = '12345'.ls_order-status = 'NEW'.APPEND ls_order TO lt_orders.After:
DATA(lo_service) = NEW zcl_order_service( iv_client = sy-mandt ).
DATA(lt_orders) = VALUE zorders_t( ( order_id = '12345' status = 'NEW' )).8. Comments - Format Comments
Before:
"This method calculates the total price"incl. VAT and discount."Parameter: iv_net_price - Net price" iv_discount - Discount in percent"Return: Gross priceMETHOD calculate_gross_price.After:
" This method calculates the total price" incl. VAT and discount." Parameter: iv_net_price - Net price" iv_discount - Discount in percent" Return: Gross priceMETHOD calculate_gross_price.Configuration and Profiles
Profile Concept
ABAP Cleaner uses profiles for configuration. A profile defines which rules are active and how they are applied.
┌──────────────────────────────────────────────────────────────┐│ Profile Manager │├──────────────────────────────────────────────────────────────┤│ ││ Profiles: ││ ┌────────────────────────────────────────────────────────┐ ││ │ [★] Default │ ││ │ [ ] Team Standard │ ││ │ [ ] Strict Modernization │ ││ │ [ ] Minimal Changes │ ││ │ [ ] + Create new profile... │ ││ └────────────────────────────────────────────────────────┘ ││ ││ [ Edit ] [ Copy ] [ Delete ] [ Export ] [ Import ] ││ │└──────────────────────────────────────────────────────────────┘Create and Configure Profile
- Open Window → Preferences → ABAP Cleaner
- Select Profiles
- Click Create new profile
Rule Configuration
Each rule can be individually configured:
┌──────────────────────────────────────────────────────────────┐│ Rule Configuration: "Use inline declarations" │├──────────────────────────────────────────────────────────────┤│ ││ ☑ Rule enabled ││ ││ Options: ││ ┌────────────────────────────────────────────────────────┐ ││ │ Convert DATA declarations: │ ││ │ ○ Only if used once │ ││ │ ● If used in same block │ ││ │ ○ Always (aggressive) │ ││ │ │ ││ │ ☑ Convert FIELD-SYMBOLS │ ││ │ ☑ Keep declarations for complex types │ ││ │ ☐ Convert even if type is lost │ ││ └────────────────────────────────────────────────────────┘ ││ ││ Preview: ││ ┌─────────────────────┐ ┌─────────────────────┐ ││ │ DATA lv_name TYPE │ │ DATA(lv_name) = │ ││ │ string. │→│ get_name( ). │ ││ │ lv_name = get_name( │ │ │ ││ └─────────────────────┘ └─────────────────────┘ ││ │└──────────────────────────────────────────────────────────────┘Export and Share Profile
Profiles can be exported as JSON files and shared within the team:
{ "profileName": "Team Standard", "rules": { "EmptyLinesInClassDefinition": { "enabled": true, "maxEmptyLines": 1 }, "UseInlineDeclaration": { "enabled": true, "convertFieldSymbols": true, "onlyIfUsedOnce": false }, "ReplaceCreateObject": { "enabled": true }, "RemoveUnusedVariables": { "enabled": false } }}Distribute within team:
1. Export profile: Preferences → ABAP Cleaner → Export Profile2. Place JSON file in Git repository: /config/abap-cleaner-profile.json3. Team members import: Preferences → ABAP Cleaner → Import ProfileDaily Usage
Interactive Mode
Interactive mode shows all changes before applying them:
Shortcut: Ctrl+4 (or Source → Clean Up With Interactive ABAP Cleaner...)┌──────────────────────────────────────────────────────────────┐│ Interactive ABAP Cleaner │├──────────────────────────────────────────────────────────────┤│ ││ Rule: "Replace obsolete ADD with modern syntax" ││ ││ ┌─────────────────────────────┬─────────────────────────┐ ││ │ Before │ After │ ││ ├─────────────────────────────┼─────────────────────────┤ ││ │ ADD 1 TO lv_count. │ lv_count += 1. │ ││ │ ADD lv_amount TO lv_total. │ lv_total += lv_amount. │ ││ └─────────────────────────────┴─────────────────────────┘ ││ ││ [✓ Apply] [✗ Skip] [Apply All] [Skip All] [Show Rule Info] ││ ││ Changes: 2 of 15 applied | Profile: Team Standard ││ │└──────────────────────────────────────────────────────────────┘Automatic Mode
Apply all rules without confirmation:
Shortcut: Ctrl+Shift+4 (or Source → Clean Up With Automatic ABAP Cleaner)Cleanup on Save
ABAP Cleaner can be automatically executed on every save:
Preferences → ABAP Cleaner → Clean-up on save
☑ Enable clean-up on save Profile: [Team Standard ▼]
Scope: ○ Current method only ● Changed code blocks ○ Entire source code
☑ Show notification after clean-upConfigure Keyboard Shortcuts
Window → Preferences → General → Keys
Search: "ABAP Cleaner"
Results:- Clean Up With Interactive ABAP Cleaner: Ctrl+4- Clean Up With Automatic ABAP Cleaner: Ctrl+Shift+4- Open ABAP Cleaner Preferences: Ctrl+Shift+Alt+4Complete Before/After Example
Before: Legacy Code
CLASS zcl_order_processor DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS: process_order IMPORTING iv_order_id TYPE zorder_id RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_order_error.
PRIVATE SECTION.
DATA: mv_client TYPE mandt. DATA: mo_logger TYPE REF TO zcl_logger.
ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION.
METHOD process_order.
DATA: lv_status TYPE zorder_status, lt_items TYPE TABLE OF zorder_item, ls_item TYPE zorder_item, lv_total TYPE p DECIMALS 2, lo_validator TYPE REF TO zcl_order_validator.
CREATE OBJECT lo_validator.
CLEAR lv_total.
SELECT SINGLE status FROM zorders INTO lv_status WHERE order_id EQ iv_order_id.
IF sy-subrc NE 0. RAISE EXCEPTION TYPE zcx_order_error. ENDIF.
IF lv_status EQ 'CLOSED'. rv_success = abap_false. RETURN. ENDIF.
SELECT * FROM zorder_items INTO TABLE lt_items WHERE order_id EQ iv_order_id.
LOOP AT lt_items INTO ls_item. ADD ls_item-amount TO lv_total. ENDLOOP.
CALL METHOD lo_validator->validate EXPORTING iv_order_id = iv_order_id iv_total = lv_total RECEIVING rv_valid = DATA(lv_valid).
IF lv_valid EQ abap_true. UPDATE zorders SET status = 'PROCESSING' WHERE order_id = iv_order_id. rv_success = abap_true. ELSE. rv_success = abap_false. ENDIF.
ENDMETHOD.
ENDCLASS.After: Modernized Code
CLASS zcl_order_processor DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS process_order IMPORTING iv_order_id TYPE zorder_id RETURNING VALUE(rv_success) TYPE abap_bool RAISING zcx_order_error.
PRIVATE SECTION. DATA mv_client TYPE mandt. DATA mo_logger TYPE REF TO zcl_logger.ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION. METHOD process_order. DATA(lo_validator) = NEW zcl_order_validator( ).
SELECT SINGLE status FROM zorders INTO @DATA(lv_status) WHERE order_id = @iv_order_id.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_order_error. ENDIF.
IF lv_status = 'CLOSED'. rv_success = abap_false. RETURN. ENDIF.
SELECT * FROM zorder_items INTO TABLE @DATA(lt_items) WHERE order_id = @iv_order_id.
DATA(lv_total) = REDUCE #( INIT sum = 0 FOR ls_item IN lt_items NEXT sum += ls_item-amount ).
DATA(lv_valid) = lo_validator->validate( iv_order_id = iv_order_id iv_total = lv_total ).
IF lv_valid = abap_true. UPDATE zorders SET status = 'PROCESSING' WHERE order_id = iv_order_id. rv_success = abap_true. ELSE. rv_success = abap_false. ENDIF. ENDMETHOD.ENDCLASS.Summary of Changes
| Category | Change |
|---|---|
| Empty lines | Removed unnecessary empty lines, logical grouping |
| Declarations | Resolved chained DATA: declarations |
| CREATE OBJECT | Replaced with NEW #( ) |
| CLEAR | Removed unnecessary CLEAR (variable is directly assigned) |
| CALL METHOD | Used functional notation |
| EQ/NE | Replaced with =/<> |
| Inline DATA | Inline declarations where appropriate |
| REDUCE | Replaced LOOP AT with ADD with REDUCE |
| Alignment | Consistent parameter alignment |
CI/CD Integration
ABAP Cleaner can be used as part of the CI/CD pipeline to check code standards:
name: ABAP Style Check
on: pull_request: paths: - 'src/**.abap'
jobs: style-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Java uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin'
- name: Run ABAP Cleaner Check run: | # Download ABAP Cleaner CLI wget https://github.com/SAP/abap-cleaner/releases/latest/download/abap-cleaner-cli.jar
# Check if cleanup changes would be needed java -jar abap-cleaner-cli.jar \ --profile config/abap-cleaner-profile.json \ --check-only \ --source src/
- name: Upload Report if: failure() uses: actions/upload-artifact@v4 with: name: cleanup-report path: cleanup-report.txtBest Practices
| Topic | Recommendation |
|---|---|
| Profile selection | Start with default profile, adjust gradually |
| Team standard | Define a shared profile for the entire team |
| Cleanup on save | Enable for consistent code |
| Interactive mode | Check interactively first for legacy code |
| Code review | Perform cleanup before review |
| New rules | Check new rules on ABAP Cleaner updates |
| Exceptions | Rules can be disabled for specific files |
| CI/CD | Integrate as style check in pipeline |
| Migration | Clean up legacy code gradually, not all at once |
| Documentation | Document team profile decisions |
Related Topics
- gCTS - Git-based Transport Management
- CI/CD with ABAP Cloud - Automated Pipelines
- ADT Tips & Tricks - Efficient Development in Eclipse