REFACTORING LEGACY ABAP
Modern syntax is a tool, not the goal. A successful refactoring keeps observable behavior stable while making intent easier to understand, test and change. In this lecture you will modernize realistic legacy patterns without turning readable ABAP into a single compressed expression.
1. The safe refactoring workflow
- Characterize current behavior. Record inputs, outputs, exceptions, messages, database effects and important edge cases.
- Create a safety net. Add ABAP Unit tests where practical and preserve representative test data.
- Capture a quality baseline. Run syntax checks, ATC or the appropriate checks in your system before editing.
- Change one idea at a time. Rename, extract, modernize and optimize in small reviewable steps.
- Compare behavior and measurements. Re-run tests and inspect database or runtime traces when performance is part of the change.
- Review for clarity. A shorter program is not automatically a clearer program.
Refactoring should not quietly change business rules. Separate a behavior change from a structural cleanup so reviewers can see which decision caused which result.
2. A realistic before-and-after example
DATA: lt_order TYPE TABLE OF zso_header,
ls_order TYPE zso_header,
lt_result TYPE TABLE OF zty_order_view,
ls_result TYPE zty_order_view.
SELECT * INTO TABLE lt_order
FROM zso_header
WHERE erdat GE p_date.
LOOP AT lt_order INTO ls_order.
IF ls_order-status EQ 'O'.
MOVE-CORRESPONDING ls_order TO ls_result.
IF ls_order-netwr GT 1000.
ls_result-priority = 'HIGH'.
ELSE.
ls_result-priority = 'NORMAL'.
ENDIF.
APPEND ls_result TO lt_result.
CLEAR ls_result.
ENDIF.
ENDLOOP.SELECT FROM zso_header
FIELDS vbeln, kunnr, erdat, netwr, waerk
WHERE erdat >= @date_from
AND status = 'O'
INTO TABLE @DATA(open_orders).
DATA(order_views) = VALUE ztt_order_view(
FOR order IN open_orders
( CORRESPONDING #(
BASE ( priority = COND #(
WHEN order-netwr > 1000
THEN 'HIGH'
ELSE 'NORMAL' ) )
order ) ) ).The query selects only required rows and columns. The constructor expression describes the resulting table. If the nested expression feels hard to scan in your team, extract the priority decision into a small method; readability wins over novelty.
3. Refactor declarations close to use
" Before
DATA lv_total TYPE decfloat34.
lv_total = calculate_total( it_items ).
" After
DATA(total) = calculate_total( items ).
" Explicit type remains useful when it documents a contract
DATA tax_amount TYPE ztax_amount.Inline declarations reduce distance between declaration and first use. Keep an explicit type when domain meaning, numeric precision, method boundaries or a public contract would otherwise be hidden.
4. Replace READ TABLE boilerplate carefully
Before
READ TABLE customers
INTO DATA(customer)
WITH KEY id = order-customer_id.
IF sy-subrc = 0.
name = customer-name.
ELSE.
CLEAR name.
ENDIF.After
DATA(customer) = VALUE #(
customers[ id = order-customer_id ]
OPTIONAL ).
DATA(name) = customer-name.OPTIONAL returns an initial value when the row is missing. That is correct only when “not found” is a normal case. If missing data is exceptional, use the table expression deliberately and handle cx_sy_itab_line_not_found, or validate with line_exists( ). Do not hide a data error by adding OPTIONAL mechanically.
5. Use guard clauses to flatten nested logic
METHOD process_order.
IF order-is_blocked = abap_true.
RETURN.
ENDIF.
IF order-items IS INITIAL.
RETURN.
ENDIF.
validate_items( order-items ).
save_order( order ).
ENDMETHOD.Guard clauses make exceptional or irrelevant cases visible at the top. The main path then reads from validation to action without several levels of nested IF. Preserve required cleanup and transaction behavior before introducing early exits.
6. Extract methods that describe business intent
A long method often mixes selection, validation, calculation and persistence. Extract coherent steps and name them in business language:
METHOD create_invoice.
DATA(items) = load_billable_items( customer_id ).
validate_billable_items( items ).
DATA(total) = calculate_invoice_total( items ).
persist_invoice( customer_id = customer_id
items = items
total = total ).
ENDMETHOD.
ABAP Development Tools provides Quick Assist refactorings, including extracting selected statements into a method where supported. Use the tool as an aid, then improve method names, parameters and responsibility boundaries.
7. Replace SELECT inside LOOP with set-oriented access
SELECT FROM zso_header AS h
INNER JOIN zso_item AS i
ON i~vbeln = h~vbeln
FIELDS h~vbeln,
h~kunnr,
i~posnr,
i~material,
i~quantity
WHERE h~vbeln IN @order_ranges
INTO TABLE @DATA(order_items).Check join cardinality, buffering, authorization rules and duplicate behavior. A single larger query is not automatically correct; it is valuable when it accurately represents the required data set.
8. Modernize names, not only syntax
- Prefer pronounceable, searchable names such as
open_ordersandcalculate_total. - Use a consistent language and domain vocabulary.
- Avoid encoding technical types in variable names when the declaration already shows the type.
- Name boolean variables and methods so a condition reads naturally.
- Use comments for the reason or constraint, not to repeat the statement.
Renaming can produce more value than replacing five lines with one expression. ADT refactoring support can update references more safely than manual search-and-replace.
9. Avoid the “clever one-liner” trap
VALUE, FOR, REDUCE, COND, SWITCH and LET are powerful. Combine them only while the business rule remains obvious. Introduce an intermediate variable or extract a method when a reader must decode several nested expressions. Clean code optimizes for the next developer, not for the fewest physical lines.
10. Release and language-version compatibility
- Confirm the minimum ABAP release and support package used by every target system.
- Check the configured ABAP language version; ABAP Cloud restricts access to released APIs and objects.
- Activate and test in the actual target landscape instead of trusting an example from a newer release.
- Keep syntax modernization separate from functional change so downporting and review stay manageable.
11. Review checklist
- Behavior is protected by tests or a documented comparison.
- The method has one clear responsibility and stays at one abstraction level.
- Names explain business intent.
- Database access is outside loops unless measurement and semantics justify it.
- Exceptions are handled intentionally;
OPTIONALdoes not mask an error. - Expressions are readable and types preserve required precision.
- Syntax, ATC findings and release compatibility are checked.
12. Practice exercises
- Refactor a loop that filters and appends rows using
FILTERorVALUE ... FOR. Explain which version your team finds clearer. - Replace one
READ TABLEblock with a table expression while preserving the missing-row behavior. - Extract validation and persistence from a long method into intention-revealing methods.
- Find a
SELECTinside a loop and design a set-oriented alternative. State the assumptions you must verify. - Write ABAP Unit tests around a legacy calculation before modernizing it.
13. Interview questions
What is the first step before refactoring legacy ABAP?
Understand and protect current observable behavior with tests or a documented baseline. Syntax replacement without a safety net can preserve appearance while changing results.
When should an explicit type be preferred over DATA(...)?
When the type communicates a domain contract, controls numeric semantics, crosses an API boundary or prevents an inferred technical type from becoming accidental design.
Is a table expression always better than READ TABLE?
No. It is concise, but missing-row semantics must remain explicit. Choose direct exception handling, OPTIONAL, DEFAULT where supported, or line_exists( ) according to the business rule.
Why is SELECT inside LOOP a common review finding?
It can cause repeated database round trips and mix data access with processing. A set-oriented query often scales and communicates intent better, but it must be validated for correctness.
Does fewer lines mean cleaner ABAP?
No. Clean ABAP prioritizes readability, clear responsibility, testability and predictable behavior. A small named method can be better than a dense nested expression.
Official SAP references
- Clean ABAP — SAP Styleguides
- Quick Assist Overview — SAP Help Portal
- Extracting Methods from Statements — SAP Help Portal
- ABAP Code Review Guide — SAP Styleguides
- ABAP cleaner — SAP open-source project
Compatibility note: Available refactorings and syntax depend on the ADT version, ABAP release and language version. Validate every example in your target system before production use.
0 Comments