Course Purpose
To give an overview of main aspects for creation of good quality code.
Target Audience
SAP development course students, self-learners, and anyone interested in coding for development needs.
Prerequisites
Students should know the basics of ABAP Core.
Course Objectives
On completion of the Clean code course, the learners will be able to:
- What is a code quality and why it is important,
- Main points to pay attention to for creation of a good quality code in ABAP
Agenda
Lesson 2: Common recommendations
Lesson 4: Constants and Variables
Lesson 7: Method body and error handling
Lesson 8: Comments and code formatting
Lesson 1: Code quality
Code quality |
Good quality code should satisfy certain characteristics (they are in the picture). |
Code quality: why is it important? |
As you know, no project ends in a day. Modifying, reviewing, and testing the code are the activities of any software project and product.
Take a scenario where you need to fix an issue or modify existing code. First, you need to understand code, then modify or write additional code. This chart shows time spent on related activities. |
Code quality and Software cost |
Reading someone’s code is like understanding his mind. Writing code is a mind game. Moreover, code depends on the skill and maturity of the person writing it. The same requirement can be executed very differently by different people. Therefore, it is important to write code in an obvious way, using minimal effort.
If you minimize the time needed to understand the code, it will bring a reduction in the overall time and effort. Poor code quality and clarity, on opposite side, may lead to: — functional defects, — more cost and time to make changes, — poor application performance, — extension is problematic, — increase in technical debt.
|
Lesson 2: Common recommendations
Important! Subsequent information is a well-grounded recommendation but not a dogma. It may change with time.
So, when you take part in a real project then follow the development guidelines used on the project. Mind the performance If you implement high performance components, use the advice from this guide with care. Some of approaches described below may make performance slower (more method calls) or demand more memory (more objects). ABAP has some specific features that may increase this (for example it compares data types during a method call). So, splitting a large method into a set of sub-methods may make the code slower. However, it is strongly recommended to not avoid the code optimization based on obscure fears. Most rules (e.g., naming, commenting) have no negative impact at all. Try to implement things in a clean, object-oriented way. If you suppose that something runs too slow then make a performance measurement. Only after that should you take a fact-based decision to refuse specific rules.
|
Lesson 3: Naming
Class naming Methods naming Boolean methods Function module |
For names of classes use nouns and for names of methods use verbsStart Boolean methods names with verbs like “is_” or “has_”.
It is better to name Function modules like methods. |
Good Bad |
Use one word per concept
Use the same term for a concept; don’t mix it with other synonyms. Synonyms may force the reader to waste time on finding a difference that’s not there. Use pattern names only if really implement themUsage of software design patterns names for classes and interfaces is acceptable only if you really implement them (for example, you may name your class form_factory if it really implements the factory design pattern). The most used patterns include singleton, factory, decorator, facade, observer, iterator, etc. |
Lesson 4: Constants and Variables
Good Bad |
Use constants instead of magic numbers
|
Inline declaration Up-front declaration |
Prefer inline in comparison with up-front declarations but avoid inline declaration in optional branches |
is preferable then |
Do not chain up-front declarationsChaining suggests that variables are related on a logical level. Also, chaining makes code reformatting and refactoring a little bit more difficult (changing of variables list requires meddling with colons, dots, and commas, etc.). |
Prefer INSERT INTO TABLE instead of APPEND TOINSERT INTO TABLE can be used with all table and key types. So, the table’s type and key definitions can be refactored easier if you need to change it due to performance requirements. APPEND TO is good only for a STANDARD table if you use it in an array-like fashion (entry is added to the table as the last row). |
Instead of
It is better to use or |
Avoid DEFAULT KEYOften the only aim of adding Default keys is to get some new functional statements working. Default keys ignore numeric data types. So, some obscure mistakes can happen. It is better to specify the key components explicitly or use EMPTY KEY if key is not necessary at all. |
expresses the intent better than |
Prefer LINE_EXISTS instead of READ TABLE to check existence of the row |
Is better then Also, you may use |
Prefer XSDBOOL to set Boolean variablesThe name xsdbool is a little bit misleading («xsd» prefix suggests that we deal with stuff like the «XML Schema Definition). But this function is good for our aim, because it produces a char1, which is compatible with boolean type abap_bool best. Also, instead of xsdbool you may use the COND ternary form. Its syntax is a little longer. |
Lesson 5: Conditions
is preferable then |
Try to make conditions positiveIf you create Boolean variable as negative statement it is difficult to understand what you mean sometimes. Also try to avoid the creation of empty if\else blocks. |
Or Is preferable then |
Decompose complex conditions or create for them methods of their own
|
Use
Instead of |
Try to avoid empty IF\ELSE branches. |
Good Bad |
When you have multiple alternative conditions – prefer CASE to ELSE IFCASE allows us to easily create a set of alternatives that exclude each other. The statement has simpler syntax, and its performance is better than a series of IFs. CASE even prevents some errors that can occur during creation of series of the IF-ELSEIFs. |
Bad |
Keep conditions nesting depth lowIt is difficult to understand Nested IFs quickly, an exponential number of test cases is needed for complete coverage. Decision trees can usually be simplified by using sub-methods or introducing Boolean helper variables. |
Lesson 6: Classes
Prefer instance to static classesStatic classes lose almost all advantages provided by object-oriented programming. During Unit tests creation it is almost impossible to replace productive dependencies (implemented by/inside static classes) with test doubles. So, when there is a question whether to create a class\method as static, the answer will usually be: no. One accepted exception to this rule is plain type utils classes. Their methods are completely stateless and basic (as a result they look like ABAP statements or built-in functions). Prefer composition over inheritanceIt is not recommended to build hierarchies of classes with inheritance. It is better to favor composition. It is difficult to design clean inheritance because you have to follow rules like the Liskov substitution principle. It is also more difficult to understand the hierarchy (you need to understand guiding principles behind it). Inheritance decreases reuse because methods tend to be implemented to be used only by sub-classes. It also complicates refactoring (to change one method you may need to change the whole hierarchy tree). Composition supposes that you create small, independent objects, each for one specific purpose. It is possible to combine these objects into more complex ones by simple delegation and facade patterns. Composition forces us to create more classes but has no further disadvantages. But do not let this advice discourage you from usage of inheritance in the right places. Do not mix stateful and stateless in the same classIf methods provide the same result no matter when and in what order they are run, this approach is considered as Stateless. Otherwise, the internal state of objects is changed through their methods (stateful programming), meaning this is full of side effects. Both approaches are acceptable and have their applications. But if you mix these approaches in the same object it makes the code hard to understand. Moreover, code becomes prone to obscure carry-over errors and synchronicity problems. So, it a bad idea to do that.
|
Is better than Example of dynamic typing is below: |
Use functional instead of procedural callsBut remember about exceptions: dynamic typing forbids functional calls. So, resort to the procedural style. |
Good Bad |
Omit RECEIVING |
Is better than |
Tend to use few IMPORTING parameters, at best less than threeIf many parameters really make sense, consider combining them into objects or structures. |
Is better than
|
Use exactly one type of parameters (RETURNING, EXPORTING, or CHANGING)A good method does one thing. As a result, it also should return exactly one thing (the exception is when the output parameters of your method form a logical entity). Otherwise, your method does multiple things, and it is better to split it. |
Instead of |
Prefer RETURNING over EXPORTINGSyntax is shorter. |
Use CHANGING sparingly, where suitedCHANGING should be used for cases when an existing variable that was earlier filled is partially updated. Use EXPORTING to initially fill a previously empty variable. |
Clear or overwrite EXPORTING reference parametersReference parameters refer to existing memory areas. They may be filled beforehand. So, clear or overwrite EXPORTING variables to provide reliable data. |
Lesson 7: Method body and error handling
Do only one thing, do it wellOnly one thing should be done in method. It should be done in the best way possible. It is likely that a method does one thing if:
|
Instead of
It better to decompose into |
Concentrate on the happy path or error handling (do not combine both)It better to implement inside the method a happy-path method is built for, or the error-handling-behavior. Combining both is a bad idea.
|
Small method |
Keep methods smallMethods should be small (usually not more than 3 to 5 statements). Of course, sometimes it does not make sense to reduce a large method further. This is usually okay if the method remains focused on one thing. |
Is better then |
Fail fastValidate and fail as early as possible. It is harder to spot and understand later validations. You may waste resources to get there. |
Use
or or |
You may use CHECK or RETURN at method start for validation of inputThere is no common opinion on whether you should use RETURN or CHECK statements to exit a method if the input does not meet expectations. CHECK has shorter syntax but traditional form with IF … ELSE… may be understood by people better. Optimally, methods should provide a result (a filled return parameter, or an exception). Returning nothing should be avoided.
Do not use CHECK in other positions |
Use
Instead of |
Use exceptions instead of return codesExceptions poses multiple advantages in comparison with return codes:
|
Instead of |
Prefer class-based exceptions instead of outdated non-class-based exceptions
It is better to use your own super classes for exceptions.It is recommended to create (abstract) super classes for each exception type of your application (instead of sub-classing the foundation classes directly). It will allow you to CATCH all your exceptions and provide the possibility to add common functionality to exceptions (for example, special text handling, etc.). |
Throw CX_STATIC_CHECK for manageable exceptionsIf the logic flow supposes an exception to occur (failing user input validation, etc.) and be reasonably handled by the receiver, it is better to throw a checked exception inherited from CX_STATIC_CHECK. Such exception type must be mentioned in method’s signature and have to be caught or forwarded to prevent syntax errors. It is therefore the consumer will need to take care of handling the error situation. |
Force dump for unrecoverable situationsIf an error is so critical that you are sure the receiver is not able to manage it (failure to acquire memory, etc.), dump instead of throwing an exception.
|
Is shorter than |
Use RAISE EXCEPTION NEW instead of RAISE EXCEPTION TYPE
|
Good Bad |
Wrap foreign exceptions.Be independent from the foreign code (catch foreign exceptions and wrap them in your own exception type). |
Lesson 8: Comments and code formatting
Instead of |
Express yourself in code (not in comments)
|
Good Bad |
Comments do not excuse you for bad names |
Instead of |
For code segmentation use methods instead of comments |
Instead of |
Comments should explain the ‘why’ but not the ‘what’ |
Instead of |
Prefer for comments » instead of * |
Or Instead of |
Put comments before the code statements they relate to |
Design should be described in the design documents, not in the code |
Instead of commenting code – delete it |
Instead of |
Optimize for reading, not for writingMost of the time developers read code. Writing code takes up a way smaller part of the day. So, your code formatting should be optimized for reading but not for writing. Use the Pretty Printer before code activation |
Instead of |
Condense your code |
Instead of |
Do not add more than one single blank line to separate things |
You may highlight that things somehow belong together by aligning assignments:
But leave statements ragged if they have nothing to do with each other: |
Align assignments when statements belong to the same object, but not to different ones
|
Instead of |
Close brackets at line end |
Instead of |
Place single parameter calls on one line |
Instead of |
Use line-breaks for multiple parametersOtherwise, it is hard to define where one parameter ends and the next one starts |
Instead of |
Align parametersRagged margins make it harder to see where the parameter name ends, and its value begins |
Lesson 9: Testing
Instead of |
Mainly test interfaces, not classesInterfaces are usually more stable than classes . |
Good names Bad names |
Reflect in test method names what was given and expectedAppropriate names describe the given and then of the test while bad names describe the when, provide meaningless facts, or are cryptic. It would be good to add an explanatory comment when the name is too short to provide enough meaning because ABAP allows only 30 characters in method names. The comment may be placed into ABAP Doc or at the beginning of the test method. If you have many test methods with long names, it can be an indicator that you should divide your test class into several ones. Differences in the givens may be expressed in the class’s names. Use pattern given-when-then Implement your test code following the given-when-then paradigm: 1. initialize objects\variables in a given section («given»), 2. call the actual tested piece of code («when»), 3. validate the outcome («then»). If the given or then sections are very long so that you cannot visually separate the mentioned sections anymore, it would be good to extract sub-methods. |
You may use constants to description of purpose and importance of test data |
Good Bad |
Use few, focused assertionsTend to assert only exactly what the test method is for. Do this with a small number of assertions. If you use too many assertions, it indicates that the method has no clear purpose. The productive and test code are interrelated in too many places: changing a small piece of productive code will demand rewriting a large number of tests even if they are not really connected to the changed feature. A large variety of assertions confuses the reader, it is difficult to find the most important, distinguishing assertions among them. |
Good Bad |
Use the proper assert typeAsserts often do more than their name supposes, for example assert_equals include type comparison and provides precise descriptions if values differ. Usage of the wrong, too-common asserts will force the reader to go into the debugger immediately instead of allowing to make suggestion of what is wrong directly from the error message. |
If you check for expected exceptions, then use FAIL
|
Writing custom asserts allows shorten code and avoid duplication |
Read more:
“Clean Code A Handbook Of Agile Software Craftsmanship”, Robert C. Martin Series |
Lesson 10: Final Test
Final Test Reference: Test