Skip to Content
Author's profile photo Uwe Schieferstein

Dangerous Liaisons in User-Exits and How to Avoid Them


“The R/3 enhancement concept allows you to add your own functionality to SAP’s standard business applications without having to modify the original applications.” (Introduction to the Enhancement Concept).

This blog is about a specific type of exit, namely Function Module Exits. Assuming that you are familiar with transactions CMOD and SMOD I will show you how User-Exits can be organized in order to achieve four major goals:

  1. Easy Maintenance
  2. High Transparency
  3. Process-Independent Testing
  4. Avoidance of Dangerous Liaisons 

The Problem

User-Exits are normally implemented “On-Demand”. Having found out that a specific behaviour of a standard application cannot be realized by means of customizing the next step is to look for appropriate User-Exits. An enhancement project (CMOD) is created and the required enhancements assigned. Now it is time to create the implementation of the User-Exit which occurs in a ZX-include predefined by SAP.


These ZX-includes have to be assigned to customer packages. Depending on who implements the ZX-includes and for which purposes they are implemented these User-Exit includes are likely to be scattered around many packages.

On one of our ERP systems the situation looks like this:

  • 98 implemented ZX-includes are assigned to 15 different packages

The ZX-includes of User-Exit XLTO (User Exits for TO Processing) have been assigned to four(!) different packages demonstrating the historical evolution of this User-Exit.

Considering the fact that these User-Exits are shared by three different companies residing on three different clients this is already a bad situation regarding maintenance and transparency of the User-Exits.


From Bad to Worse

Being responsible for SAP-XI & EDI support at our company I sooner or later had to modify existing User-Exits for Billing-Doc-Output (XEDF). However, I almost got a heart attack when I saw the implementation of include ZXEDFU02 (EXIT_SAPLVEDF_002) for the first time (see below). 



While it is easy to spot the two companies (Spain & Switzerland) that have used this User-Exits it is less obvious to identify and understand the manifold requirements that have been implemented.


Step 1 – Organizing the Mess

In order to organize the existing and new User-Exits in a more meaningful way I created a new package ZCMOD. This package is intended to contain all User-Exit related repository objects.

The next step was to clearly separate the company specific coding. This was achieved by a generic Five-Step approach:

  1. Create a Z-exit function group (if not already existing)
  2. Copy the Exit-Function Module to a Z-Exit Function Module
  3. Create company specific Z-Exit Function Modules
  4. Replace coding in ZX-include with function module call
  5. Reassign ZX-includes to the new package ZCMOD


The result of the first three steps is shown below:

A new function group ZXEDF was created and assigned to the package ZCMOD. The Exit-Function module EXIT_SAPLVEDF_002 was copied to ZEXIT_SAPLVEDF_002 in function group ZXEDF. This function module was again copied twice to yield ZEXIT_SAPLVEDF_002_CH and ZEXIT_SAPLVEDF_002_ES. The main point is that all Z-exit function modules have the same interface like the SAP Standard Function Module-Exit.


Implementing User-Exits: Once and for All

If we need to touch SAP standard objects we should always try to do it once and for all. In case of Function Module-Exits this can be done quite easily:

The ZX-include contains nothing but a call to the frame Z-exit function module (see below).

Using this approach we obviously never ever have to touch this ZX-include again.


The Frame Z-Exit Function Module

The frame function module ZEXIT_SAPLVEDF_002 has the function to separate the coding logic at the highest hierarchical level which in our case are the companies:


The company specific function modules contain the company specific coding:



Step 2 – Complete Structural Separation of Concerns

Even though we have now separated the company specific coding the function modules are still linked together into a single function group. Thus, if developers need to change the User-Exit for different companies at the same time they might interfere with each others. Therefore the next level of organizing User-Exits is required: the complete structural separation of concerns.

For each company a specific Z-Exit function group is created:

  • ZXMGV – Frame Z-Exit function group
  • ZXMGV_CH – specific for Lindt Switzerland
  • ZXMGV_ES – specific for Lindt Spain
  • ZXMGV_ES – specific for Lindt Poland


Within the frame function module ZEXIT_SAPMV01_002 we determine the name of the company specific function module and dynamically call it.


There are two obvious advantages of this structural organization of User-Exits:

  1. Errors accidentially introduced into a User-Exit will not affect any other company.
  2. Different companies can modify the same User-Exit at the same time without interfering with each other.



In this weblog I presented a possible strategy how to organize User-Exits (CMOD / SMOD). The major steps of this approach are:

  • Create a specific package (e.g. ZCMOD) containing all User-Exit related repository objects
  • Copy the Exit-Function module to a Z-Exit function module having the very same interface. Only the frame function module is called in the ZX-include.
  • Implement the ZX-include once and for all.
  • Create Z-Exit function groups for the highest hierarchical level that employs the User-Exit


An example for a hierarchy of Z-Exit function modules is given below:

  1. Frame: ZEXIT_SAPLVEDA_001
  2. Country specific: ZEXIT_SAPLVEDA_001_CH
  3. Country & Customer specific: ZEXIT_SAPLVEDA_001_CH_SHOP


If I need to change the User-Exit for inbound ORDERS IDocs intended for our Lindt Shops I immediately know that I have to change function module  ZEXIT_SAPLVEDA_001_CH_SHOP. Thus, based on the requirements (SD-EDI User-Exit & Lindt Shops) I can deduce the affected Z-Exit function module.


Is there another advantage of replacing the coding in the ZX-includes with the call to the frame Z-Exit function module? 

Yes, there is.

The Z-Exit function modules enable you to test the User-Exit independent of the entire process / transaction !!!

If you are already working on SAP releases >= 6.20 you can define ABAP Unit Tests for each Z-Exit function module. Having assigned all User-Exit related repository objects (ZX-includes and Z-Exit function groups) into a single package you can then use the Code Inspector (transaction SCI) to easily test all your User-Exits at once:

  • Define an object set containing this package
  • Define a check variant executing all dynamic tests (ABAP Unit)
  • Run an inspection with object set and check variant created before


Finally, if you intend to get rid of obsolete User-Exits and revert back to the SAP standard processes a package like ZCMOD will give you a headstart for your project.



Function Module-Exits (CMOD / SMOD) can be organized by simple means into a useful structure thereby achieving major goals like

  • Easy Maintenance
  • High Transparency
  • Process-Independent Testing

and at the same time avoiding dangerous liaisons by separating concerns into completely independent structural components.



The topic of this blog has been continued in:

Dangerous Liaisons in User-Exits – Revisited

Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Pablo Casamayor
      Pablo Casamayor
      your former post was fantastic and this one is excellent as well. What i do love about them is that
      you show openly how you solve problems that we encounter every day (and the way you approach them). After reading them i´ve got the impression i´ve learned something new.

      Thank you so much.

      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Pablo

      All of us who are writing blogs try to be meaningful yet at the risk of missing this target.
      Therefore receiving a response like yours is the most honourable reward I could imagine.
      Thank you.


      Author's profile photo Thorsten Franz
      Thorsten Franz
      Hi Uwe,
      I agree with Pablo. 🙂
      Thinking about the part where SYST-MANDT is evaluated and the country-specific suffix is set, thus specifying the name of the FM to be called dynamically:
      An alternative solution that comes to my mind works as follows: Create a standard client-specific customizing table and select the name of a function module or global class from the table without even explicitly mentioning the client number.
      This way, the client numbers in development, test, and production needn't be the same, and new countries can be added at any level without changes to the central "Exit-Dispatcher" FM. Also, existing country-specific implementation function modules may be reused by any existing or new country (assuming this has been agreed upon between the owner and the user of the FM, of course).
      (It's probably a matter of taste and the preferences in the organization.)
      Finally, if I had to implement this solution in a 7.0 system, I'd consider using Kernel BadIs instead of looking the names of function modules or classes up in transparent tables.
      Again, great blog!
      Author's profile photo B. Meijs
      B. Meijs

      Nice idea, but wouldn't it be a better idea to define your own BADI (multiple use) and implement these.


      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Ben

      I tried this approach but it failed for the following reason:
      Exit-Function Group XVED used variables defined in include LVEDACOM ("common part Data aus IDOC_INPUT_ORDERS).
      My ZXVED function group references this include in its TOP-include as well. And throughout the Exit-function modules you need these data definitions and variables. But how to reference them from within a BAdI?


      Author's profile photo B. Meijs
      B. Meijs
      Hello Uwe,

      A valid point. I think you could overcome this problem by defining a BADI with a large interface, i.e. define for every variable from the include LVEDACOM an importing or changing parameter in the BADI interface. You could even think of wrapping all data of LVEDACOM into one container and pass this data to the BADI as a data reference.
      Some advantages of using the BADI solution would be the Multiple Use and filter options, where you can specify at design time for which company, plant, country or whatever the implementation is valid.
      Storing the BADI-implementations in a specific package seems a good idea.
      Ben Meijs

      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Ben

      In the long-term Function Module-Exits will be superseeded by the BAdI / Enhancement Spot technology. Yet I believe that many companies still have lots of the CMOD/SMOD Exits in use and my approach will probably help to alleviate and prevent problems caused by multiple usage of these exits by simple means.


      Author's profile photo Former Member
      Former Member
      Hi Uwe Schieferstein
      really nice blog
      Author's profile photo Former Member
      Former Member
      Hi Uwe,

      This will really helpfull to those ABAPer who wants to be a ABAP Consultanr rather than just ABAP Programmer.

      Chetan Joshi

      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Chetan

      You are touching an important point:
      I would define an ABAP Consultant as a character with a strong technical background, a high affinity to the business and a sympathetic affection towards the users.


      Author's profile photo Former Member
      Former Member
      Hello Uwe,

      I have found it useful to create a package hierarchy to provide further information on development objects.  For example, underneath the ZCMOD package it may be useful to create a ZSALES package with a ZBILLING package beneath that.  This allows all custom developed code related to a particular functional area to be grouped together.  Very useful when looking for dev objects in SE80!



      Author's profile photo Naimesh Patel
      Naimesh Patel
      In this option we can successfully separate the business logic, but you can't still work in "Parallel".
      For example, two developers have to implement the two different business logic for two company code at the same time. So, First developer will change the User Exit Include and take an ownership. Another developer will create his FM and will SHARE the ownership. Somehow, One of the developer is ready with his changes to move further. But he has to wait until the other guy will finish his task because both are sharing the same INCLUDE.

      I would suggest to create Custom BADI and in the INCLUDE use the BADI handler to handle the BADI implementation. This way we will not need to change the INCLUDE again in the Future. For above example, both developers can create there separate implementation and move independently.

      Take look at the eLearning document Implementing a BAdI in an Enhancement Project (CMOD) by Gilen Spalding at

      Naimesh Patel

      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Naimesh

      Let's have a look at user-exit EXIT_SAPLMV01_002 which contains the include ZXMGVU03:

      (1) Include ZXMGVU03 contains nothing but the call to the frame exit-function module ZEXIT_SAPLMV01_002 in function group ZXMGV (see above).

      (2) The frame exit-function module contains the logic to separate for the different company codes and makes a dynamic call to the corresponding function module in a separate function group (e.g. ZEXIT_SAPLMV01_002_CH in ZXMGV_CH).

      These two steps can be prepared long time before anybody actually needs this user-exit.

      (3) Developer A implements his logic in ZEXIT_SAPLMV01_002_CH in group ZXMGV_CH.
      Developer B implements her logic in ZEXIT_SAPLMV01_002_ES in group ZXMGV_ES.

      Neither of these two developers touches the ZX-include nor the frame exit-function module.

      None of the developers will notice that there is somebody else working on the same user-exit.

      The BAdI approach described by Gilen Spalding is an option if we do not heavily depend on non-DDIC types defined within the TOP include of the exit-function group.


      Author's profile photo Naimesh Patel
      Naimesh Patel
      Thanks Uwe for the explanation, but still at some point they will get mixed up - specifically, when we don't have the set up mentioned in the first 2 points (most probably everybody will not have this setup ready).

      While re-thinking for the design, this piece of code bothers me in the FM zexit_saplmv01_002:
        CASE syst-mandt.
          WHEN '100'.  ld_suffix = lc_country_ch.  " Lindt CH
          WHEN '200'.  ld_suffix = lc_country_es.  " Lindt ES

      Let's assume that we are working in roll out team. At this point of time we don't know when all countries will be on the same server. Based on this fact, we can't implement our code for SUFFIX (i.e. ES, CH, PL). We need to keep on modifing FM zexit_saplmv01_002 when we know which company code (or client) will be assigned to specific country.

      Another point is if someone decides not to have the functionality for the specific coutnry, say ES, than he DELETEs the FM ZEXIT_SAPLMV01_002_ES but forgot to delete the SUFFIX assignment in the FM zexit_saplmv01_002. This will lead to runtime and it may lead to DEPENDENT changes if someone is already working of zexit_saplmv01_002.

      You are right that, we need to re-declare the data declaration to make it compatible with the BADI, but it will give is ROI in terms of the less maintenance. Like:
      * We can create different implementation without being worrying about making the SUFFIX assignment for the FM name.
      * Activate / deactivate any BADI implementation on the fly.

      Naimesh Patel

      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Naimesh

      First of all I would like to thank you for this interesting discussion about this topic.

      (1) The rollout team should actually consist of two parts:
      - central (or core) team
      - local rollout team

      Only the central team is responsible for implementing the frame exit-function group and modifying the ZX-include. In addition, they may create the customer/country/etc. specific exit-function groups as well or at least supervise the rollout team.

      Only the rollout team implements the requirements within the non-frame exit-function modules.

      Thus, in addition to the structural separation of concerns we have a separation of responsibilities.

      (2) If Lindt ES (Spain) decides that they no longer need the user-exit then I would recommend that they simply inactivate the functional coding. They may even delete their module ZEXIT_SAPLMV01_002_ES. Neither Lindt CH nor Lindt PL will ever notice this deletion because the frame-exit function modules ensures that this module which no longer exists will never be called (because we are working on a different client).

      (3) If the rollout team needs to implement the user-exit for a new country (e.g. Sweden) then the core team must be informed and they define the suffix and adjust the frame-exit module accordingly.

      (4) Are BAdI that much easier to activate / deactivate than function module user-exits? I have not implemented any BAdI recently but I think in both cases we need to transport the change from DEV to PRD. Obviously in case of BAdIs it is much easier to spot active and inactive implementations.

      Conclusion: In addition to good programming skills you need some degree of maturity in your development organisation.


      Author's profile photo Naimesh Patel
      Naimesh Patel
      Hello Uwe, thanks to you also to have this open discussion and I think we will go in more details as we dig more in it.

      For your point 2, there will not be a problem for the CH or PL but the ES will not work. It will give a short-dump as it will not find the FM ZEXIT_SAPLMV01_002_ES. To avoid this short dump, we need to change the FM zexit_saplmv01_002 to remove the code line:
      WHEN '200'. ld_suffix = lc_country_es. " Lindt ES
      If somebody is already holding the Onwership of the FM zexit_saplmv01_002, then we will lose the point of Independent development.

      For the point 4: Yes, BADIs are more flexible than user-exits.

      You are right, we need to have proper role identification for the Big projects i.e. Rollouts but here I am not just pointing to Big projects.

      Sometimes, we will have different "Processes" in the same User-Exit for the same company. E.g. While creating the PO we need to check: Custom Freight Condition is there or not (process 1) and save the PO data to some Ztable (Process 2). We can implement the functionality related to these processes in the user exit MM06E005 (EXIT_SAPMM06E_012).

      If we these functionality by implementing the BADI in the User Exit, than we can make both processes independent. We would have separate BADI implementation, which will give us high degree of flexibility in activation / deactivation.

      Naimesh Patel

      Author's profile photo Former Member
      Former Member
      Can we not put a check, maybe catch the exception in order to prevent the dump.

      Nice blog, by the way, Uwe.

      Author's profile photo Uwe Schieferstein
      Uwe Schieferstein
      Blog Post Author
      Hello Romuel

      Looking at our internal organisation I "represent" the core team. Thus, it is my responsibility to ensure that all non-frame exit-function modules exist that can be dynamically called.
      Furthermore it is my responsibility to provide the local teams with a documentation explaining the concept.
      Example: Lindt Poland (PL) does not yet use exit EXIT_SAPLMV01_002 yet I have already created ZEXIT_SAPLMV01_002_PL which - of course - is empty (no coding).
      When users of Lindt PL call the transaction which passes this user-exit then the calling hierarchy looks like this:
      EXIT_SAPLMV01_002 (ZX-include ZXMGVU03) -> calls ZEXIT_SAPLMV01_002 -> calls dynamically ZEXIT_SAPLMV01_002_PL (nothing is changed here)

      Conclusion: Users of Lindt PL did not notice anything when I rearranged the user-exit. As soon as they need this user-exit they have already an easy handle (ZEXIT_SAPLMV01_002_PL) and they can be ensure to be encapsulated from the other Lindt companies using the same user-exit.


      Author's profile photo Former Member
      Former Member
      This is one of the best blogs i have read.
      Hats off to Uwe, who envisages new possibilities and many ideas through this blog