0% found this document useful (0 votes)
290 views

General + Comments + Formatting: Clean ABAP

This document provides guidelines for writing clean and readable ABAP code. It discusses best practices for naming variables and constants, adding comments, formatting code, using statements and classes, and other aspects of writing well-structured ABAP code. The guidelines emphasize readability, maintainability, and following object-oriented principles in ABAP.

Uploaded by

Felipe Graeff
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
290 views

General + Comments + Formatting: Clean ABAP

This document provides guidelines for writing clean and readable ABAP code. It discusses best practices for naming variables and constants, adding comments, formatting code, using statements and classes, and other aspects of writing well-structured ABAP code. The guidelines emphasize readability, maintainability, and following object-oriented principles in ABAP.

Uploaded by

Felipe Graeff
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

Clean ABAP General + Comments + Formatting

Names Comments Formatting


Use descriptive names Express yourself in code, not in Optimize for reading, not for writing
max_wait_time_in_seconds, iso3166tab. comments DATA: a
, b.
assert_is_valid( input )
Prefer solution domain and problem “checks whether user input is valid
check( x ) Use the Pretty Printer before activating
domain terms Always!
Business layers: acount, ledger
Comments are no excuse for bad
Technical layers: queue, tree Use your Pretty Printer team settings
names Always!
Use plural DATA total_sum
" the total sum
E.g. countries instead country
DATA s
No more than one statement per line
don’t( ). do_this( ).
Use pronounceable names Use methods instead of comments to
detection_object, dobj Stick to a reasonable line length
segment your code <= 120 characters
Avoid abbreviations do_a( ).
do_b( ).
customizing, cust
" do a
Condense your code
a = b + 1. No whitespace in weird places
Use same abbreviations everywhere " do b
dobjt, dot, dotype x = a / 10. Add a single blank line to separate
Use nouns for classes and verbs for things, but not more
Write comments to explain the why, No whitespace in weird places
methods not the what
account, withdraw, is_empty " can be missing if … Don’t obsess with separating blank
" reads the itab
Avoid noise words READ TABLE itab
lines
No whitespace in weird places
data, controller, object
Design goes into the design
Pick one word per concept Align assignments to the same object,
documents, not the code
read, retrieve, query " some general observations on this but not to different ones
structure-type = 'A'.
Use pattern names only if you mean Comment with ", not with * structure-id = '4711'.
" inlines nicely
them * aligns to weird places Close brackets at line end
E.g. factory, façade, composite updater->update( this
Put comments before the statement ).
Avoid encodings, esp. Hungarian
they relate to Keep single parameter calls on one line
notation and prefixes " right here just_like( that )
result = a + b do_it( ). " not there
rv_result = iv_a + iv_b " nor there
Keep parameters behind the call
break_only(
Delete code instead of commenting it if_the_line_gets_too_long ).
" READ TABLE
Language Use FIXME, TODO, and XXX and add
If you break, indent parameters under
the call
Mind the legacy your ID DATA(sum) = add_two_numbers(
" FIXME FH check sy-subrc! value_1 = 5
Try new syntax before applying
value_2 = 6 ).
Mind the performance Don’t add method signature and end-
Measure potentially slower patterns of comments Line-break multiple parameters
ENDIF. " IF a = 0. add_two_numbers( a = 5 b = 6 ).
Prefer object orientation over Align parameters
Don’t duplicate message texts as
imperative programming
I.e. classes over functions and reports
comments
" Business document not found
MESSAGE e100. Break the call to a new line if the line
Prefer functional over procedural gets too long
language constructs ABAP Doc only for public APIs DATA(result) =
E.g. index += 1 instead ADD 1 to index PRIVATE SECTION. some_object->some_interface~a_method(
"! Reads something a = 1
Use design patterns wisely METHODS read_something b = 2 ).
I.e. where appropriate
Indent and snap to tab
Don’t force people to add single spaces

Indent in-line declarations like method


calls
merge( a = VALUE #( b = 'X'
c = 'A' ) ).

Don’t align type clauses


DATA name TYPE seoclsname.
DATA reader TYPE REF TO /clean/reader.

Clean ABAP Cheat Sheet v1.0.0 https://github.wdf.sap.corp/CleanCode/ABAPCleanCode/blob/master/CheatSheet.md


Clean ABAP Variables + Statements + Classes
Constants Booleans Classes: Object orientation
Use constants instead of magic Use ABAP_BOOL for Booleans Prefer objects to static classes
numbers DATA has_entries TYPE abap_bool or
E.g. typekind_date instead 'D' BOOLE_D where DDIC type needed
Prefer composition over inheritance
Prefer enumeration classes over Use ABAP_TRUE and ABAP_FALSE for DATA delegate TYPE REF TO
CLASS a DEFINITION INHERITING FROM
constants interfaces comparisons
E.g. class message_severity over interface Instead 'X' , space, and IS INITIAL Don’t mix stateful and stateless in the
common_constants same class
Use XSDBOOL to set Boolean variables
If you don’t use enumeration classes, empty = xsdbool( itab IS INITIAL )
group your constants
Don’t mix unrelated constants in same structure

Conditions Classes: Scope


Try to make conditions positive Global by default, local only in
Variables IF has_entries = abap_true. exceptional cases
CLASS lcl_some_helper
Prefer inline over up-front declarations Consider decomposing complex
DATA(name) = 'something' FINAL if not designed for inheritance
DATA: name TYPE char30 conditions
DATA(example_provided) = xsdbool(…) CLASS a DEFINITION FINAL
Don’t declare inline in optional IF example_provided = abap_true AND
one_example_fits = abap_true. Members PRIVATE by default,
branches PROTECTED only if needed
IF has_entries = abap_true. Consider extracting complex conditions PRIVATE SECTION.
DATA(value) = 1. IF is_provided( example ). DATA attribute
Do not chain up-front declarations Consider using immutable instead of
DATA name TYPE seoclsname.
DATA reader TYPE REF TO something. getter
Ifs CLASS data_container
Prefer REF TO over FIELD-SYMBOL DATA a TYPE i READ-ONLY
LOOP AT itab REFERENCE INTO … No empty IF branches
IF has_entries = abap_true. Use READ-ONLY sparingly
ELSE. READ-ONLY

Tables Prefer CASE to ELSE IF for multiple


alternative conditions
Use the right table type CASE type. Classes: Constructors
WHEN this.
HASHED: large, filled at once, never modified, WHEN OTHERS.
read often ENDCASE.
Prefer NEW over CREATE OBJECT
SORTED: large, always sorted, filled over time or DATA(a) = NEW b( ).
modified, read often Keep the nesting depth low CREATE OBJECT a TYPE b
STANDARD: small, array-like ELSE.
IF <other>. If your global class is CREATE PRIVATE,
Avoid DEFAULT_KEY ELSE. leave the CONSTRUCTOR public
IF <something>. CLASS a DEFINITION CREATE PRIVATE.
DATA itab TYPE … WITH EMPTY KEY
DATA itab TYPE … WITH DEFAULT KEY PUBLIC SECTION.
METHODS constructor
Prefer INSERT INTO TABLE over
APPEND TO Regular expressions Prefer multiple static factory methods
Except to express that row must be last over optional parameters
Prefer simpler methods to regular METHODS constructor
Prefer LINE_EXISTS over READ TABLE IMPORTING
expressions a OPTIONAL
IF line_exists( itab[ key = 'A' ] ) IF input IS NOT INITIAL. b OPTIONAL
IF matches( … regex = '.+' ).
Use descriptive names for multiple
Prefer basis checks to regular constructor methods
Strings expressions METHODS create_from_sample
CALL FUNCTION 'SEO_CLIF_CHECK_NAME' METHODS create_from_definition
Use ` to define literals pattern = '[A-Z][A-Z0-9_]{0,29}'
CONSTANTS a TYPE string VALUE `abc` Make singletons only where multiple
Consider assembling complex regular instances don’t make sense
Use | to assemble text expressions DATA singleton
text = |Received { http_code }| CONSTANTS classes …
CONSTANTS interfaces …
… = |{ classes }|{ interfaces }|.

Clean ABAP Cheat Sheet v1.0.0 https://github.wdf.sap.corp/CleanCode/ABAPCleanCode/blob/master/CheatSheet.md


Clean ABAP Methods + Exceptions
Methods: Calls Methods: Parameter number Error handling: Return codes
Prefer functional over procedural calls Aim for few IMPORTING parameters, at Prefer exceptions to return codes
do_it( ). best less than three METHODS check RAISING EXCEPTION
CALL METHOD do_it. METHODS check RETURNING result
METHODS a IMPORTING b c d e
Omit RECEIVING Split methods instead of adding Don’t let failures slip through
DATA(a) = do_it( ). DATA(result) = check( input )
do_it( RECEIVING result = a ). OPTIONAL parameters IF result = abap_false.
METHODS a IMPORTING b
Omit the optional keyword EXPORTING METHODS c IMPORTING d
do_it( a = b ). METHODS x
do_it( EXPORTING a = b ). IMPORTING b
d Error handling: Exceptions
Omit the parameter name in single
Use PREFERRED parameter sparingly Exceptions are for errors, not for
parameter calls METHODS do_it
do_it( b ). IMPORTING a PREFERRED regular cases
do_it( a = b ). B TYPE i RAISE EXCEPTION db_read_failure
RAISE EXCEPTION not_enough_money
RETURN, EXPORT, or CHANGE exactly
Use class-based exceptions
one parameter METHODS do_it RAISING EXCEPTION
Methods: Object orientation METHODS do_it METHODS do_it EXCEPTIONS
EXPORTING a
Prefer instance to static methods CHANGING b
METHODS a
CLASS-METHODS a
Error handling: Throwing
Public instance methods should be part Methods: Parameter types
of an interface Use own super classes
CLASS e INHERITING FROM our_main_e
INTERFACES the_interface. Prefer RETURNING over EXPORTING
METHODS a METHODS a RETURNING b
METHODS a EXPORTING b
Throw one type of exception
METHODS a RAISING EXCEPTION b c d
RETURNING large tables is usually okay Use sub-classes to enable callers to
METHODS a RETURNING b TYPE TABLE
Methods: Method body METHODS a EXPORTING b TYPE TABLE distinguish error situations
METHODS do_it RAISING EXCEPTION r
Do one thing, do it well, do it only Use either RETURNING or EXPORTING CLASS a INHERITING FROM r
CLASS b INHERITING FROM r
or CHANGING, but not a combination
Focus on the happy path or error METHODS do_it Throw CX_STATIC_CHECK for
EXPORTING a
handling, but not both CHANGING b manageable situations
TRY. RAISE EXCEPTION no_customizing
“ focus here Use CHANGING sparingly, where suited
CATCH. METHODS IMPORTING … RETURNING … Throw CX_NO_CHECK for usually
“ do somewhere else METHODS CHANGING
ENDTRY. unrecoverable situations
RAISE EXCEPTION db_unavailable
Split method instead of Boolean input
Descend one level of abstraction
do_something_high_level ( ). parameter Consider CX_DYNAMIC_CHECK for
DATA(low_level_op) = |a { b }|. METHODS do_it_without_saving avoidable exceptions
METHODS do_it_and_save
RAISE EXCEPTION division_by_zero
Keep methods small METHODS do_it IMPORTING and_save
3-5 statements, one page, 1000 lines Dump for totally unrecoverable
situations
RAISE EXCEPTION out_of_memory
Methods: Parameter names
Methods: Control flow Prefer RAISE EXCEPTION NEW to RAISE
Consider calling the RETURNING EXCEPTION TYPE
Fail fast parameter RESULT RAISE EXCEPTION NEW a( ).
METHOD do_it. METHODS sum RETURNING result RAISE EXCEPTION TYPE a.
“ some more actions METHODS sum RETURNING sum
CHECK input IS NOT INITIAL.

CHECK or RETURN Error handling: Catching


METHOD do_it.
CHECK input IS NOT INITIAL.
Methods: Parameter initialization
Wrap foreign exceptions instead of
Avoid CHECK in other positions Clear or overwrite EXPORTING letting them invade your code
LOOP AT itab INTO DATA(row). reference parameters CATCH foreign INTO DATA(error).
CHECK row IS NOT INITIAL. CLEAR et_result. RAISE EXCEPTION NEW my( error ).
RAISE EXCEPTION error.
Don’t clear VALUE parameters
CLEAR rv_result.

Clean ABAP Cheat Sheet v1.0.0 https://github.wdf.sap.corp/CleanCode/ABAPCleanCode/blob/master/CheatSheet.md


Clean ABAP Testing
Principles Injection Test Data
Write testable code Use dependency inversion to inject test Make it easy to spot meaning
There are no tricks to writing tests, there are only doubles METHODS accepts_emtpy_user_input
tricks to writing testable code. (Google) METHODS test_1
cut = NEW( stub_db_reader )
cut->set_db_reader( stub_db_reader )
Enable others to mock you cut->db_reader = stub_db_reader Make it easy to spot differences
CLASS my_super_object DEFINITION. given_some_data( ).
INTERFACES you_can_mock_this. Use CL_ABAP_TESTDOUBLE do_the_good_thing( ).
assert_that_it_worked( ).
before writing custom stubs and mocks
Readability rules
Use constants to describe purpose and
given_some_data( ). Exploit the test tools
do_the_good_thing( ).
CL_OSQL_REPLACE, CDS Test Framework, Avalon importance of test data
and_assert_that_it_worked( ). " recreated in setup anyway
METHOD teardown.
Don’t make copies or write test reports Use test seams as temporary CLEAR stub_db_reader
REPORT zmy_copy. workaround ENDMETHOD.
" for playing around They are not a permanent solution!

Test publics, not private internals Use LOCAL FRIENDS to access the
CLASS unit_tests DEFINITION LOCAL FRIENDS
dependency-inverting constructor Assertions
Don’t obsess about coverage if it’s hidden away
60% -> all done! Few, focused assertions
Don’t misuse LOCAL FRIENDS to invade assert_not_initial( itab ).
the tested code assert_equals( act = itab exp = exp ).
CLASS unit_tests LOCAL FRIENDS cut.
cut->db_reader = stub_db_reader Use the right assert type
Test classes assert_equals( act = itab exp = exp ).
Don’t change the productive code to assert_true( itab = exp ).
Call local test classes by their purpose make the code testable
CLASS unit_tests Assert content, not quantity
IF in_test_mode = abap_true. assert_contains_message( key )
CLASS tests_for_the_class_under_test
assert_equals( act = lines( messages )
Don’t sub-class to mock methods exp = 3 ).
Put tests in local classes
Use test seams or OSQL_REPLACE or extract the
REPORT some_tests_for_this Assert quality, not content
methods to own class
assert_all_lines_shorter_than( … )
Don’t mock stuff that’s not needed
DATA unused_dependency Use FAIL to check for expected
Code under test exceptions
Don’t build test frameworks METHOD throws_on_empty_input.
Name the code under test setup( test_case_id = '4711' ) TRY.
" when
meaningfully, or default to CUT cut->do_something( '' ).
DATA switch cl_abap_unit_assert=>fail( ).
DATA cut CATCH /clean/some_exception.
Test Methods " then
Test interfaces, not classes ENDTRY.
DATA cut TYPE REF TO some_interface Test methods names: reflect what’s ENDMETHOD.
DATA cut TYPE REF TO some_class
given and expected Forward unexpected exceptions
Extract the call to the code under test METHODS accepts_emtpy_user_input
instead of catching and failing
METHODS test_1
to its own method METHODS throws RAISING EXCEPTION bad
to default unneeded parameters Use given-when-then
given_some_data( ). Write custom asserts to shorten code
do_the_good_thing( ). and avoid duplication
assert_that_it_worked( ). assert_table_contains( row )
READ TABLE itab
“When” is exactly one call assert_subrc( )
given_some_data( ).
do_the_good_thing( ).
and_another_good_thing( ).
assert_that_it_worked( ).

Don’t add a TEARDOWN unless you


really need it
" recreated in setup anyway
METHOD teardown.
CLEAR stub_db_reader
ENDMETHOD.

Clean ABAP Cheat Sheet v1.0.0 https://github.wdf.sap.corp/CleanCode/ABAPCleanCode/blob/master/CheatSheet.md

You might also like