Get Started
- CodeAnt AI
- Control Center
- Pull Request Review
- IDE
- Compliance
- Anti-Patterns
- Code Governance
- Infrastructure Security Database
- Application Security Database
Abap
A BREAK-POINT
statement is used when debugging an application with help of the ABAP Debugger. But such debugging statements could make an application vulnerable to attackers, and should not be left in the source code.
Jump statements, such as CHECK and CONTINUE
let you change the default flow of program execution, but jump statements that direct the control flow to the original direction are just a waste of keystrokes.
Calling DELETE ADJACENT DUPLICATES won’t reliably do any good if the table hasn’t first been sorted to put duplicates side by side, since the ADJACENT
part of the command looks for multiple rows side-by-side with the same content.
Procedural development in general, and FORM… ENDFORM, and PERFORM
specifically, have been been classified as obsolete by SAP and should be avoided. Classes and methods should be used for all new development.
Using `LOOP…INTO with MODIFY statements will put the required record in a work area, process it and put it back in the internal table. It is more efficient to modify the internal table directly by using LOOP…ASSIGNING and field-symbols.
This rule raises an issue when a LOOP…INTO contains one or more MODIFY` statements.
The `READ TABLE … WITH KEY … statement performs a linear search of STANDARD tables, which is very inefficient in most cases.
This rule raises an issue when a READ TABLE … WITH KEY … statement does not finish with BINARY SEARCH. No issue will be raised for HASHED and SORTED` tables.
Modifying SAP standard tables via an Open SQL statement can result in data corruption and unexpected behavior. This is because a direct modification bypasses all validation mechanisms.
Instead, the use of standard functions or SAP Business Add-Ins (BAdIs) is recommended.
This rule raises an issue on Open SQL statements INSERT, UPDATE, MODIFY, or SELECT … FOR UPDATE
which target an SAP table, i.e. a table with a name starting with a character between “A” and “X”, case insensitive.
The ABAP documentation is pretty clear on this subject :
The complexity of an expression is defined by the number of AND, OR, XOR and EQUIV
operators it contains.
A single expression’s complexity should not become too high to keep the code readable.
`JOIN bypasses the SAP table buffer. Buffered tables should be accessed with the simplest SELECT statements possible so as not to risk bypassing the buffer.
If one of the tables in a JOIN is buffered, it would be an advantage to first import the required entries using a SELECT into an internal table itab, and then for example, using the statement SELECT … FOR ALL ENTRIES IN itab` to access further tables.
For readability, SAP recommends that asterisks (*) only be used to comment out header lines and code. Commentary should be commented using a double quote (“
)
In SELECT … FOR ALL ENTRIES
queries there are two internal tables: the driver internal table, and the target internal table. For each entry in the driver table some data from a database table is stored in the target internal table.
Most of the time, starting by sorting the content of the driver internal table offers improved performance. Indeed in such cases, from one request to another, the data read from the database tends to be much the same, and so for instance the use of database caching is maximised.
This BYPASSING BUFFER clause explicitly switches off SAP table buffering, so the SELECT
reads data directly from the database.
By definition, using this clause can lead to performance issues, which is why its use must be strongly indicated.
Restricting the number of `CONTINUE, EXIT and CHECK statements in a loop is done in the interest of good structured programming.
One CONTINUE, EXIT and CHECK` statement is acceptable in a loop, since it facilitates optimal coding. If there is more than one, the code should be refactored to increase readability.
SELECT INTO TABLE is much more efficient than SELECT … ENDSELECT. SELECT INTO TABLE
needs more memory to hold the result set, but in normal situations, this is not a concern. When memory is a concern, the result set can be divided into smaller sets.
Leaving a CATCH
block empty means that the exception in question is neither handled nor passed forward to callers for handling at a higher level. Suppressing errors rather than handling them could lead to unpredictable system behavior and should be avoided.
A loop with at most one iteration is equivalent to the use of an `IF statement to conditionally execute one piece of code. No developer expects to find such usage of a loop statement. If the initial intention of the author was really to conditionally execute one piece of code, an IF statement should be used in place.
At worst that was not the initial intention of the author and so the body of the loop should be fixed to use the nested STOP, RETURN or EXIT` statements in a more appropriate way.
SELECT with JOIN
always performs better than nested selects.
Removing duplicate entries from driver tables enables OPEN SQL
to generate fewer queries for getting the same data, giving a performance boost.
Using EXIT and CHECK in SELECT statements to stop the execution of SELECT loop is an expensive and ineffective way to filter data. Filtering should be part of the SELECT loop themselves. Most of the time conditions located in a CHECK statement should be moved to the WHERE clause, and the EXIT statement should typically be replaced by an UP TO 1 ROW
clause.
Internal tables can quickly become a source of performance problems if not accessed correctly, SORTED and HASHED
tables should always be accessed with the appropriate key or partial key.
Internal tables can be sorted without specifying the specific fields on which to sort. However, doing so is inefficient because when a sort key is not specified, the entire row is used in the sort, which can be markedly inefficient.
Every subroutine(FORM
) should be commented to explain its goal and how it works. This comment can be located just before or after the form definition.
Checking logged users’ permissions by comparing their name to a hardcoded string can create security vulnerabilities. It prevents system administrators from changing users’ permissions when needed (example: when their account has been compromised). Thus system fields `SY-UNAME and SYST-UNAME should not be compared to hardcoded strings. Use instead AUTHORITY-CHECK to check users’ permissions.
This rule raises an issue when either of the system fields SY-UNAME or SYST-UNAME are compared to a hardcoded value in a CASE statement or using one of the following operators: =, EQ, <>, NE`.
When several lines must be inserted/updated into an internal table, instead of doing those changes line by line, mass operations should be used because they offer better performance by design.
This rule raises an issue when a single line operation like APPEND, CONCATENATE, and INSERT
is performed on an internal table in a loop.
`CASE can contain a WHEN OTHERS clause for various reasons: to handle unexpected values, to show that all the cases were properly considered, etc.
For readability purposes, to help a developer quickly spot the default behavior of a CASE statement, it is recommended to put the WHEN OTHERS clause at the end of the CASE statement.
This rule raises an issue if the WHEN OTHERS clause is not the last one of the CASE’s cases.
According to the SAP documentation:
So calling system C functions using a CALL
statement should be avoided.
Using the = operator to copy the content of an internal table is more efficient than using LOOP + APPEND
statements.
Every AUTHORITY-CHECK statement sets the fields SY-SUBRC (also accessible as SYST-SUBRC) to the authorization check result. Thus SY-SUBRC value should be checked just after every AUTHORITY-CHECK
statement.
Because CX_ROOT is the base exception type, catching it directly probably casts a wider net than you intended. Catching CX_ROOT could mask far more serious system errors that your CATCH
logic was intended to deal with.
Some smaller, more specific exception type should be caught instead.
Passing parameters by reference instead of by value avoids the overhead of making a copy. Passing arguments via copy should only be done when it is technically mandated, as it is for example with RFC function modules.
Shared naming conventions allow teams to collaborate efficiently. This rule checks that all method parameters follow a naming convention.
Whenever more than one line needs to be read, inserted or deleted from a database table, it is more efficient to work with an internal table than to read, insert or delete the lines one by one inside a loop.
`CASE statements are useful when there are many different cases depending on the value of the same expression.
For just one or two cases however, the code will be more readable with IF` statements.
This rule applies whenever an `IF statement is followed by one or
more ELSEIF statements; the final ELSEIF should be followed by an ELSE statement.
The requirement for a final ELSE statement is defensive programming.
The ELSE statement should either take appropriate action or contain
a suitable comment as to why no action is taken. This is consistent with the
requirement to have a final OTHERS clause in a CASE`
statement.
DATA BEGIN OF … OCCURS
has been deprecated and will eventually be removed. All usages should be replaced.
Shared naming conventions allow teams to collaborate efficiently.
This rule raises an issue when a method name does not match a provided regular expression.
For example, with the default provided regular expression ^([A-Z0-9_]|[a-z0-9_])$, the method:
Nested control flow statements IF, CASE, DO, LOOP, SELECT, WHILE and PROVIDE
are often key ingredients in creating
what’s known as “Spaghetti code”. This code smell can make your program difficult to understand and maintain.
When numerous control structures are placed inside one another, the code becomes a tangled, complex web. This significantly reduces the code’s readability and maintainability, and it also complicates the testing process.
Every function should be commented to explain its goal and how it works. This non-empty comment must be located before the function definition.
The system field `SY-SUBRC must be tested immediately after any statement setting this variable. Reading this variable informs on previous operation success or errors. Such errors should be handled properly so that the program continues in a consistent state.
This rule raises an issue when the field SY-SUBRC is not checked just after performing one of the following operations:
Calling a function or method which can throw exceptions.
Calling one of the file access operation OPEN DATASET, READ DATASET or DELETE DATASET.
SY-SUBRC check must be done either with the CASE, IF or CHECK` statement.
This statement deletes all rows of an internal table itab. This REFRESH
statement is deprecated and usage should be avoided.
Naming conventions are an important tool in efficient team collaboration. This rule checks that all form names match a regular expression naming convention.
UPDATE dbtab SET … without a WHERE condition changes all the entries of the table. Check whether dataset to be changed can be limited by a suitable WHERE
condition.
Using keywords as variable names may yield incomprehensible code, and should be avoided.
Using an empty driver table in a SELECT/FOR ALL ENTRIES table has a very important side effect: the complete WHERE clause is not taken into account because a NO WHERE
condition is generated. Thus a full table scan is unexpectedly executed.
Every macro should be commented to explain its goal and how it works. This comment can be located just before or after the macro definition.
ABAP provides the ability to manipulate programs dynamically during execution for instance with statements like `INSERT REPORT and GENERATE SUBROUTINE POOL. Most of those statements are for internal use within SAP Technology Development and incompatible changes are possible at any time without prior warning or notice.
This rule raises an issue when any of the following source code processing statements is used: INSERT REPORT, READ REPORT, DELETE REPORT, EDITOR-CALL FOR REPORT, SYNTAX-CHECK FOR itab, GENERATE REPORT/SUBROUTINE POOL, LOAD REPORT, SCAN, INSERT TEXTPOOL, READ TEXTPOOL, DELETE TEXTPOOL, EXPORT DYNPRO, IMPORT DYNPRO, DELETE DYNPRO, SYNTAX-CHECK FOR DYNPRO, and GENERATE DYNPRO`.
DELETE FROM dbtab without a WHERE
condition deletes all the entries of the table. Check whether dataset to be deleted can be limited by a suitable WHERE condition.
” statements without an authority check is security sensitive. Its access should be restricted to specific users.
This rule raises when a `CALL TRANSACTION has no explicit authorization check, i.e. when:
the CALL TRANSACTION statement is not followed by WITH AUTHORITY-CHECK.
the CALL TRANSACTION statement is not following an AUTHORITY-CHECK statement.
the CALL TRANSACTION statement is not following a call to the AUTHORITY_CHECK_TCODE` function.
This variant of the `REFRESH statement is deprecated and should be avoided.
This REFRESH statement initializes the internal table itab, reads several rows from the database table dbtab, and adds their contents to the internal table itab. A SELECT` statement should be used instead.
SQL COUNT(..), MIN(..), MAX(..), SUM(..), AVG(..)
aggregate functions cause the SAP table buffer to be bypassed, so the use of these functions can lead to performance issues.
Having all branches of a CASE or IF chain with the same implementation indicates a problem.
In the following code:
When there is only one statement in a chain, the chain syntax can be omitted, which simplifies the code.
The `EXEC SQL … END-EXEC statement can be used to embed Native SQL statically in ABAP programs.
According to the SAP documentation:
ABAP hints can be used to override the default behavior of the SAP Cost Based Optimizer (CBO). When the execution plan provided by the CBO is not optimal, it is possible to “drive” the CBO by providing the main index to be used to filter rows.
Such optimizations are not portable from one database to another, such as when migrating from Oracle to DB2. Therefore hard coding an optimization should be done only when it is strongly indicated.
The main reason for using chained statements is to increase readability, but when used with operational statements, chaining can have the opposite effect. Even worse, it can lead to unexpected program behavior.
An Open SQL SELECT statement without an explicit ORDER BY clause will retrieve rows in an unpredictable order. On pool/cluster tables, the current implementation of Open SQL SELECT returns the result set in the primary key order, but that’s not the case for transparent tables. That’s why it’s safer to always use an ORDER BY
clause.
SAP recommends to keep the result set of any request as small as possible for performance reasons.
A `SELECT…ENDSELECT request will retrieve multiple records at the same time. Stopping immediately such a request with a CHECK, RETURN or EXIT statement will not reduce the number of retrieved records. If the goal is to retrieve a single record, or check that at least one record exists, it is recommended to add UP TO 1 ROWS or to use SELECT SINGLE.
Even if the request already retrieved only one record, adding UP TO 1 ROWS or to using SELECT SINGLE will make the code easier to read.
This rule raises an issue when a SELECT…ENDSELECT which has neither SINGLE nor UP TO 1 ROWS and:
contains only one statement and it is a CHECK, RETURN or EXIT`
or when it is completely empty.
The requirement for an OTHERS
clause is defensive programming. The clause should either take appropriate action, or contain a suitable comment as to why no action is taken.
DISTINCT operator causes the SELECT
statement to avoid the SAP buffering and to read directly from the database and not from the buffer on the application server.
Putting multiple statements on a single line lowers the code readability and makes debugging the code more complex.
Unresolved directive in <stdin> - include::{noncompliant}[]
Write one statement per line to improve readability.
Unresolved directive in <stdin> - include::{compliant}[]
When handling a caught exception, the original exception’s message and stack trace should be logged or passed forward.
Magic numbers make the code more complex to understand as it requires the reader to have knowledge about the global context to understand the number itself. Their usage may seem obvious when writing the code, but it may not be the case for another developer or later once the context faded away. -1, 0, and 1 are not considered magic numbers.
SQL queries that use EXISTS subqueries are inefficient because the subquery is re-run for every row in the outer query’s table. There are more efficient ways to write most queries, ways that do not use the EXISTS
condition.
Nested code - blocks of code inside blocks of code - is eventually necessary, but increases complexity. This is why keeping the code as flat as possible, by avoiding unnecessary nesting, is considered a good practice.
Merging if statements when possible will decrease the nesting of the code and improve its readability.
Sharing some naming conventions is a key point to make it possible for a team to efficiently collaborate. This rule allows to check that all macro names match a provided regular expression.
The use of parentheses, even those not required to enforce a desired order of operations, can clarify the intent behind a piece of code. However, redundant pairs of parentheses could be misleading and should be removed.
Arbitrary OS command injection vulnerabilities are more likely when a shell is spawned rather than a new process, indeed shell meta-chars can be used (when parameters are user-controlled for instance) to inject OS commands.
A chain of if/else if statements is evaluated from top to bottom. At most, only one branch will be executed: the first one with a condition that evaluates to true.
Therefore, duplicating a condition automatically leads to dead code. Usually, this is due to a copy/paste error. At best, it’s simply dead code and at worst, it’s a bug that is likely to induce further bugs as the code is maintained, and obviously it could lead to unexpected behavior.
Shared naming conventions allow teams to collaborate efficiently.
This rule raises an issue when a class name does not match a provided regular expression.
Sharing some naming conventions is a key point to make it possible for a team to efficiently collaborate. This rule allows to check that all variable names match a provided regular expression.
Duplicated string literals make the process of refactoring complex and error-prone, as any change would need to be propagated on all occurrences.
The switch statement should be used only to clearly define some new branches in the control flow. As soon as a case clause contains too many statements this highly decreases the readability of the overall control flow statement. In such case, the content of the case
clause should be extracted into a dedicated method.
Shared coding conventions allow teams to collaborate efficiently. This rule checks that all function names match a provided regular expression.
Hardcoding IP addresses is security-sensitive. It has led in the past to the following vulnerabilities:
Today’s services have an ever-changing architecture due to their scaling and redundancy needs. It is a mistake to think that a service will always have the same IP address. When it does change, the hardcoded IP will have to be modified too. This will have an impact on the product development, delivery, and deployment:
The developers will have to do a rapid fix every time this happens, instead of having an operation team change a configuration file.
It misleads to use the same address in every environment (dev, sys, qa, prod).
Last but not least it has an effect on application security. Attackers might be able to decompile the code and thereby discover a potentially sensitive address. They can perform a Denial of Service attack on the service, try to get access to the system, or try to spoof the IP address to bypass security checks. Such attacks can always be possible, but in the case of a hardcoded IP address solving the issue will take more time, which will increase an attack’s impact.
Sharing some naming conventions is a key point to make it possible for a team to efficiently collaborate. This rule allows to check that all interface names match a provided regular expression.
An unused local variable is a variable that has been declared but is not used anywhere in the block of code where it is defined. It is dead code, contributing to unnecessary complexity and leading to confusion when reading the code. Therefore, it should be removed from your code to maintain clarity and efficiency.
Consistent indentation is a simple and effective way to improve the code’s readability. It reduces the differences that are committed to source control systems, making code reviews easier.
This rule raises an issue when the indentation does not match the configured value. Only the first line of a badly indented section is reported.
Having two WHEN in a CASE statement or two branches in an IF chain with the same implementation is at best duplicate code, and at worst a coding error.