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 –
- RFC function modules – RMI or Remote Method Invocation is not yet available
- Update function modules – These are still to be used
- 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.
- PERFORM ON COMMIT|ROLLBACK – We anyways do not use these generally, but if you have to – go ahead.
- 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.
- 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
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.
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.
Yes, you have the right approach! Let's go!
(And to be fair, PERFORM have been obsolete since long before 7.50 🙂)
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.
If the documentations before 7.31 had not been removed, you could find out that PERFORM is obsolete since 7.02 (2009) 😀
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.
Thanks Kerem for this tip. Its definitely useful.
I used to create the object inside a subroutine, but then, you know. 🙂
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
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.
Should be replaced by
Whilst I am asking for the impossible, how about people investigating ABAP Unit and TDD as well?
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.
Can you share more information on this? A code snippet or any link may be?
Here's some documentation.
Thanks Michael. I got it now.
I believe we would find similar application of subroutines in table maintenance generator events or a substitution routine. But these would be a must have subroutines till the time there is replacement.
FMs would also be used in scenarios like screen enhancement for transactions like BP(Business Partner) using the BDT.
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.
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.
Well, i am not quite sure what you mean. If you want to control the order of execution of the routines you have to use the PERFORM ON COMMIT.
I don't think there is an alternate solution available yet.
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 🙂
ABAP is such a large language, that personally I cannot remember what is preferred/obsolete, so every time I encounter something, I implement tooling to remind me, this so far has resulted in
which both have multiple rules to find obsolete syntax,
Thanks Lars for these links. Its very interesting stuff. I will try to use this and share the result.
Interesting. I also found out recently (this week) that subroutines are already obsolete. Good read!
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.
This may be a stupid question, but here goes:
I recently had to apply changes to a typical userexit's Z-Include:
The logic had thus far been added directly in ZXV50U03 and I wanted to a) create my new (small) logic in its own routine and b) move the existing code from the include into it's own routine. As I was working in the function module context I went the easy route and simply created new form routines which ended up in their own (new) include ZXV50F01 as is usually the case for these user exits. They now get called from within IF-statements:
Could I have easily avoided these PERFORMs and if so how? I just can't picture how working with classes and methods would look like in this particular context and how much "overhead" it would generate to set up. Especially given that I did this as part of a fairly quick code change and not some general clean-up task.
Thanks much and cheers
Note: I created a stand-alone question for this now over in Q&A to give it more visibility.