In my last blog I shared some thoughts on the ‘building blocks’ of modern ABAP development and how SAP is attempting to drag the topic into the 21st century. In that piece I focused on some practical actions like automated unit tests and automated change control but in addition to how you’re working, what code you’re writing is also really important, so in this post I’m going to look at the idea of ‘clean ABAP’.
The now classic and highly influential book Clean Code by Robert C. Martin lays down practices to write maintainable and readable code that are now widely adopted by software companies. Lately, the ABAP community also started to embrace the clean code philosophy and it’s a hot topic recently, which is also shown by the fact the SAP published their own ABAP style guide which is also entirely based on these methods.
However, the cultural shift required for a successful transition is even bigger in the ABAP world than usual. This blog presents a couple of starting points for teams wanting to get started with writing cleaner ABAP without introducing an overwhelming number of changes immediately.
The boy scout rule
Code tends to rot over time. Quick and dirty fixes are added, things are copy-pasted in haste because of deadlines and a once beautiful piece of code can gradually become unreadable and hard to understand.
This is very common and it’s one of the things that makes writing clean code hard work. But since the rot is gradual, we can stop it by adopting a similarly gradual improvement process. The boy scouts of the US have a simple rule: “Leave the campground cleaner than you found it.”. The same rule can be applied in (ABAP) programming: if you change a method or function, its code should be cleaner after the change than it was before.
This low risk, low impact approach ensures that the entire codebase becomes cleaner and cleaner over time without doing a lot of drastic changes in a short period.
Prefer small methods that do one thing
ABAP code in the past was notorious for functions, programs and methods that are sometimes thousands of lines of long, which is a recipe for disaster. In modern ABAP, developers should only use classes and methods to modularize their code, and it’s important to remember that methods should only do one thing and do it well.
A method usually does one thing if it is short (generally less than 10 statements), has very few input parameters and exactly one output parameter, and it is on a single abstraction level. For example, take the following example method:
METHOD get_user_data. IF username IS INITIAL. RAISE EXCEPTION TYPE zcx_argument_exception. ENDIF. IF username(1) = 'Z'. AUTHORITY-CHECK OBJECT 'Z_USER' ID 'USERNAME' FIELD username. IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_no_authorization. ENDIF. CALL FUNCTION 'Z_FETCH_USER' DESTINATION rfc_destination EXPORTING user_data = user_data IMPORTING username = username EXCEPTIONS OTHERS = 1. IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_rfc_error. ENDIF. ELSE. SELECT SINGLE * FROM z_users INTO user_data WHERE uname = username. user_data-additional_data = get_additional_data( username ). ENDIF. ENDMETHOD.
This method, while not excessively long, does too many things. It validates the input, determines the type of the user based on the first character, does authorization checks, handles two different main execution cases, retrieves data from the database etc. We can start by splitting it up into separate methods, starting from the highest abstraction level:
METHOD get_user_data. validate_input( username ). IF is_special_user( username ) = abap_true. user_data = get_special_user_data( username ). ELSE. user_data = get_normal_user_data( username ). ENDIF. ENDMETHOD.
Of course, the newly introduced methods here should be implemented using the same philosophy, but one abstraction level lower.
Clean up your method interfaces and calls
If you stick to the previous rule, your method interfaces will be rather simple (preferably just a few IMPORTING parameters and a RETURNING parameter). This allows you to use the functional calling style to make the code more compact, so instead of:
CALL METHOD database->get_entries EXPORTING key = my_key limit = 1000 RECEIVING result = entries.
It becomes:
entries = database->get_entries( key = my_key limit = 1000 ).
The statement CALL METHOD should generally not be used except for dynamic calls. Similarly, the superfluous keywords EXPORTING and RECEIVING should be omitted if possible. CHANGING parameters should be used sparingly and with care as they are error prone and can lead to side effects.
Public methods of a class should be defined as an interface which the class should implement (in object-oriented programming theory, each class should satisfy a contract represented by an interface). This makes the code loosely coupled and helps with writing automated unit tests.
Conditions and control branches
Conditions and branching are two of the most frequently used tools in programming so it’s important to get them right. Very complex conditions can be hard to read so it might make sense to extract them into a separate variable or method to make the intent and the meaning clear. For example, instead of writing:
IF username IS NOT INITIAL OR ( address IS NOT INITIAL AND city IS NOT INITIAL ) OR ( security_number IS NOT INITIAL ). " ... ENDIF.
You could make it more meaningful by introducing an extra variable:
sufficient_data_provided = username IS NOT INITIAL OR ( address IS NOT INITIAL AND city IS NOT INITIAL ) OR ( security_number IS NOT INITIAL ). IF sufficient_data_provided = abap_true. " ... ENDIF.
In ABAP, the two available branching statements are IF and CASE. It’s better to use CASE when there are many mutually exclusive options as it’s much easier to read than a series of IF … ELSEIF statements.
Additionally, it’s important not to create too deeply nested IFs as they can very quickly become hard to follow. If this is necessary for some reason, that usually means your method is doing too much; consider splitting it up.
Use descriptive and meaningful names
There are only two hard things in computer science: cache invalidation and naming things. — Phil Karlton
It was customary in the past to mostly use cryptic technical and abbreviated names in ABAP code. It’s mostly for historical reasons, but it also doesn’t help that ABAP has tight constraints on the length of most names so using sufficiently expressive ones can be difficult – however, this doesn’t mean we shouldn’t try.
The main thing to keep in mind is that programs are not written for computers; they are written for other developers to read and maintain (this “other developer” can easily be yourself 6 months later…). The code should read like English as much as possible, for example instead of declaring a variable called WA_T001, you should use a more meaningful name like COMPANY_DATA.
Object and interface names should be nouns, method names should be verbs. Stick to one name per concept, for example don’t use the words update, change and modify to describe the same operation. Try to avoid abbreviations too. This is sometimes difficult in ABAP because of the constraints mentioned before so if you do use abbreviations, make sure you use them consistently.
ABAP also has a tradition of using Hungarian notation where technical and contextual information is encoded as prefixes to variables. We are all familiar with names like LT_DATA, GV_COMPANY, or MST_STATIC_TABLE. This is an outdated programming concept and is now actively discouraged, however this advice might be difficult to implement as if a project already has certain set of naming conventions, they should be followed as consistency is more important to avoid confusion.
Further reading
These are only the tip of the iceberg, I tried to collect some things that are easy to adopt but of course there many more concepts. If you are interested in the topic and improving your team’s code, I highly recommend reading the original Clean Code book where many of these practices are described in a lot more detail, and take a closer look at SAP’s own style guide for more information on the ABAP specific applications of clean code concepts.