You are on page 1of 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.2 PUBLIC https://github.com/SAP/clean-abap/blob/master/cheat-sheet/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.2 PUBLIC https://github.com/SAP/clean-abap/blob/master/cheat-sheet/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 our_products_static_check
INTERFACES the_interface. Prefer RETURNING over EXPORTING INHERITING FROM cx_static_check
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
METHODS a RETURNING b TYPE TABLE
Methods: Method body METHODS a EXPORTING b TYPE TABLE
Use sub-classes to enable callers to
distinguish error situations
Do one thing, do it well, do it only Use either RETURNING or EXPORTING METHODS do_it RAISING EXCEPTION r
CLASS a INHERITING FROM r
or CHANGING, but not a combination CLASS b INHERITING FROM r
Focus on the happy path or error METHODS do_it
EXPORTING a Throw CX_STATIC_CHECK for
handling, but not both CHANGING b
TRY. manageable situations
“ focus here Use CHANGING sparingly, where suited RAISE EXCEPTION no_customizing
CATCH. METHODS IMPORTING … RETURNING …
“ do somewhere else METHODS CHANGING Throw CX_NO_CHECK for usually
ENDTRY.
unrecoverable situations
Split method instead of Boolean input RAISE EXCEPTION db_unavailable
Descend one level of abstraction
do_something_high_level ( ). parameter
DATA(low_level_op) = |a { b }|. METHODS do_it_without_saving Consider CX_DYNAMIC_CHECK for
METHODS do_it_and_save avoidable exceptions
Keep methods small METHODS do_it IMPORTING and_save
RAISE EXCEPTION division_by_zero
3-5 statements, one page, 1000 lines
Dump for totally unrecoverable
situations
Methods: Parameter names RAISE EXCEPTION out_of_memory
Methods: Control flow Prefer RAISE EXCEPTION NEW to RAISE
Consider calling the RETURNING
Fail fast parameter RESULT EXCEPTION TYPE
METHOD do_it. METHODS sum RETURNING result RAISE EXCEPTION NEW a( ).
“ some more actions METHODS sum RETURNING sum RAISE EXCEPTION TYPE a.
CHECK input IS NOT INITIAL.

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

Clean ABAP Cheat Sheet v1.0.2 PUBLIC https://github.com/SAP/clean-abap/blob/master/cheat-sheet/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( ). CONSTANTS some_nonsense_key …

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

METHOD map_xml_to_itab. “When” is exactly one call


result = cut->map_xml_to_itab( … ). given_some_data( ).
ENDMETHOD. do_the_good_thing( ).
and_another_good_thing( ).
Allows tests to focus on the parameters that are assert_that_it_worked( ).
really needed:
Don’t add a TEARDOWN unless you
METHOD some_test.
map_xml_to_itab( `<xml></xml>` ).
really need it
" recreated in setup anyway
ENDMETHOD.
METHOD teardown.
CLEAR stub_db_reader
ENDMETHOD.

Clean ABAP Cheat Sheet v1.0.2 PUBLIC https://github.com/SAP/clean-abap/blob/master/cheat-sheet/CheatSheet.md

You might also like