Skip to Content
Technical Articles

ABAP 7.5 : Are Subroutines Obsolete?

I recently stumbled upon ABAP 7.5 documentation. I was interested to see what is new. But rather I got to know what is old. SAP now says that subroutines are obsolete.

Shocked? Well, I was. I have been using performs and have always preferred them over methods. A habit that I am trying to change but not very successful at it so far.

So when I saw the statement ‘Performs are Obsolete’. I was bit worried – how would I cope with this?

As per the guidelines available at this link, whenever you are developing new code, do not create new Subroutines(Performs) or Function Modules, use objects/methods instead.

The rules say, and there are couple of them –

Rule 1 : Do not implement in function modules and subroutines

Use FMs and Performs only if they are a must. Use Methods instead.

Rule 2 : Use ABAP Objects

Use ABAP objects wherever possible for new and further developments. Classic processing blocks should only be created in exceptional cases.

And as per every rule, these rules also have exceptions. Not everything can be done in classes. Well, not yet. So the exception is long. But over the time SAP will reduce the exceptions. Some of the exceptions are as below –

  1. RFC function modules – RMI or Remote Method Invocation is not yet available
  2. Update function modules – These are still to be used
  3. CALL SCREEN and CALL SELECTION-SCREEN – Object-oriented handling of classic dynpros is still not available. This is a feature that I personally would love. 
  4. PERFORM ON COMMIT|ROLLBACK – We anyways do not use these generally, but if you have to – go ahead.
  5. GENERATE SUBROUTINE POOL – I had to use this only once in 13 years. So not really looking forward.

So, our new ABAP reports should still be created using a program, have selection screen and have events, but instead of performs we should have methods. Here is an example of how it should look.

*&--------------------------------------------------------*
*& Report YABAP_750_REPORT
*&--------------------------------------------------------*
*&
*&--------------------------------------------------------*
REPORT yabap_750_report.

CLASS lcl_alv DEFINITION FINAL.

  PUBLIC SECTION.
    CONSTANTS :
      information TYPE sy-msgty VALUE 'I'.

    TYPES :
      tt_flights TYPE STANDARD TABLE OF sflight
        WITH EMPTY KEY.
    "... add more types here

    METHODS :
      constructor
        IMPORTING
          i_carrier    TYPE sflight-carrid
          i_connection TYPE sflight-connid,
      start_report_process
        RETURNING
          VALUE(r_error) TYPE char1.

    "... add more public methods here

  PROTECTED SECTION.
    "Add protected data/methods if needed

  PRIVATE SECTION.
    DATA :
      carrier    TYPE sflight-carrid,
      conn_id    TYPE sflight-connid,
      flights    TYPE tt_flights.
    "... add more data declarations here

    METHODS :
      get_flights 
        RETURNING 
          VALUE(rt_flights) TYPE tt_flights,
      display_flights 
        RETURNING 
          VALUE(r_error) TYPE char1.
    "... add more private methods here

ENDCLASS.

CLASS lcl_alv IMPLEMENTATION.
  METHOD constructor.
    "Set variables based on called parameters
    carrier    = i_carrier.
    conn_id    = i_connection.
    "Initialize attributes
    CLEAR flights.
  ENDMETHOD.

  METHOD start_report_process.
    CLEAR r_error.
    flights = get_flights( ).
    IF flights IS INITIAL.
      MESSAGE e001(00) WITH 'No data found'(002)
        INTO DATA(lv_error).
      r_error = abap_true.
    ELSE.
      r_error = display_flights( ).
    ENDIF.
  ENDMETHOD.

  METHOD get_flights.
    CLEAR rt_flights.
    SELECT * FROM sflight INTO TABLE @rt_flights
      WHERE carrid = @carrier
      AND   connid = @conn_id.
  ENDMETHOD.

  METHOD display_flights.
    CLEAR r_error.
    TRY.
        "Create ALV table object for the output data table
        cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_table)
                                CHANGING  t_table      = flights ).
        lo_table->get_functions( )->set_all( ).
        lo_table->get_columns( )->set_optimize( ).
        lo_table->display( ).
      CATCH cx_salv_msg.  "ALV can not be displayed, issue error
        MESSAGE e001(00) WITH 'Error in ALV creation'(003) INTO DATA(lv_error).
        r_error = abap_true.
    ENDTRY.
  ENDMETHOD.

  "... add more methods implementation here
ENDCLASS.

"... build selection screen here or in another include
SELECTION-SCREEN BEGIN OF BLOCK main_block WITH FRAME TITLE TEXT-001.
  PARAMETERS : carrid TYPE sflight-carrid,
               connid TYPE sflight-connid.
SELECTION-SCREEN END OF BLOCK main_block.

START-OF-SELECTION.
  "Create object for the class
  DATA(o_alv) = NEW lcl_alv( i_carrier    = carrid
                             i_connection = connid ).

  "Below code starts the main process and issues error
  "It can be coded as per requirement 
  IF o_alv->start_report_process( ) IS NOT INITIAL.
    MESSAGE ID sy-msgid TYPE lcl_alv=>information NUMBER sy-msgno
      WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

 

Conclusion:

  • If you have not switched to using methods over performs, this is the time to do so.
  • Using this or a similar template, reports can be created very easily.
  • This style can also be applied to other programs, like file/proxy based interface program.

In short, the message is, even if we are used to a way of coding, which is also comfortable, we should change with the ABAP advancements.

And this, is my first step.

This is also my first blog, so your comments and suggestions are welcome. Please use the comment section below or you can also post your questions here.

 

– Jagdish Patil

29 Comments
You must be Logged on to comment or reply to a post.
  • Welcome and keep on learning on that path. In my opinion it it totally worth it and overdue.

    Just another hint: some of your Exceptions are only partially exceptions: at least for the use cases 1 and 2 you can use the function Module as pure wrapper. They can contains only one Method call.

    • Hi Michael

      Overdue !! So true.

      On the exceptions - yes, I agree that this code sample does not use exceptions well. Something to improve for me. Will definitely work on it.

    • Hi Jörgen

      Thanks for the link. I feel a little embarrassed now that I never knew this. 7.3 has been around for so long, and I am using subroutines as well for so long that I never thought this could be the case.

      As I said, I just happened to come across this by mistake. And I am glad that I did, because for past so many years, I kept avoiding using objects.

      • Well, the information has in fact been out there, but nobody MAKES us read it... Between that and the fact that things are backwards compatible and that obsolete things still actually work (for a good reason), we either need to find it or be told it. And the more the better!

        • Yes, the information needs to be shared.

          I heard a story today -

          Developer used Inline Declarations and a SME in a external(outside project) SME review asked to move all the data declaration to TOP include. 🙂

  • When you have a local class with a constructor, extended check may warn you that you have a global variable.

    To prevent that, I tend to make all methods and variables of "lcl_alv" static.

  • As you say, "there are exceptions", but "exception classes" are not exceptions and I would use them so that to write the starting code differently, but it's a matter of preference I guess 😉

    TRY.
        o_alv->start_report_process( ).
      CATCH cx_root INTO DATA(x_root).
        MESSAGE x_root TYPE 'I' DISPLAY LIKE 'E'.
    ENDTRY.

    NB: some people prefer to catch only what is expected to be raised, I prefer to catch everything at the lowest level.

    /
    😉
    • Thanks Sandra. Its been a long time since we interacted.

      Yes, I have this confusion, which one is the correct way. I also prefer the cx_root, but not because of any particular reason but its simpler as I don't have to check which ones to handle.

    • Having initially felt that, I now use class based exceptions exclusively. They're so much more flexible. As you find the first time you have to pass the return code up to a higher level.

      Furthermore, it might be an indication that you've not really layered your development properly.

      Fwiw, my model classes always throw exceptions - which are then handled by the controller class.

  • In some sense subroutines became obsolete when ABAP Objects was introduced in SAP 4.6 back in the year 2000, even if they were only "officially" obsolete in 2009.

    However I would imagine that of all the new ABAP code that has been written all around the world on this very day, 75%+ would be done in FORM routines.

    21 years and counting and still very little uptake.

    So I would disagree with one of the statements in the above blog.

    • If you have not switched to using methods over performs, this is the time to do so.

    Should be replaced by

    • If you have not switched to using methods over performs, the year 2000 was the time to start doing so, so you are a bit late to the party

    Whilst I am asking for the impossible, how about people investigating ABAP Unit and TDD as well?

    Cheersy Cheers

    Paul

     

    • Hi Paul

      Late to the party indeed.

      Its too bad that all the new ABAPers that get trained on the job or through the Fresher's training or whatever the term different organization use - all use this old style.

      I got trained in 2007 in ABAP, and the 21 days training did not cover CREATE OBJECT at all. OOABAP was treated as a different skillset than ABAP.

      Only if I could go back in time and get started on this in 2007.

       

  • Little addition: One of the very rare use cases for subroutines in these days is message control. You need a subroutine (form) in a processing program (report) to process a message.

  • Hi Jardish,

     

    You do not need the PERFORM ON COMMIT|ROLLBACK functionality. You can create handler methods for event TRANSACTION_FINISHED of class CL_SYSTEM_TRANSACTION_STATE. The KIND parameter will tell you whether you are in COMMIT or in ROLLBACK.

    • Hi Arseni

      Thanks for this tip, I will definitely try this.

      We don't get such use case too often for development, but I will try to check this as I am curious to see how this works.

  • I started with ABAP in 2008 on 7.02. And form routines were considered obsolete already. Whether you like them or not, or you prefer them to OO artefacts, is a matter of own taste, i guess.

    What really irritates me from the programming language point of view, is that the syntaxes are not identical - who the FORM signature looks like(syntax) and the METHOD signature(syntax).

    Meaning, that there are multiple ways to get the same task done, which i personally don't like.

    Also, they drive you to think differently( slightly different flow, because of the different syntactic possibilities ).

    I would say choose of them, and stick to the approach  - personally try to do everything which is technically possible with the OO syntax, use FMs when needed, and form routines only in exceptional cases.

    Developers using other technologies would probably ask the question 'From when on will the obsolete stuff will become really not supported' 🙂

    I would guess, the way i know SAP, that the language features will be really restricted only on special platforms, like ABAP for Cloud Development(which dont have a huge codebase, yet... ) or NGAP, etc.. So we will be seeing form routines for many years to come 🙂

  • Get rid of r_error and use the exceptions all the way.

    The exception handling is one of the best things about using classes/methods. It used to be a pain in the back to do something like that in the routines (you'd have to declare a bunch of r_error's in every one of them just to display an error message somewhere at the top). Once you realize the advantage of exceptions, there is no going back to the subroutines. Makes the code more readable too.

    Thanks for sharing!

    • Thanks Jelena. Yes, this is absolutely valid suggestion and I am adopting the use for exceptions in the actual programs. In this blog post I avoided its use because lot of people who are not used to classes and methods will see the exceptions and think its more complicated and stick to performs.