Centralised Transport Naming Standards
The first in a series of blog posts about Centralised Transport Naming Standards.
See also blog posts:
|Centralised Transport Naming Standards – SCP ABAP Environment Migration|
Centralised Transport Naming Standards – Branch By Abstraction
|Centralised Transport Naming Standards – Service Now Integration
The company I work for has a sizeable and complex SAP estate with dozens of production SAP systems and therefore an equally complex development environment. Although development standards have been in place for a number of years, it is always a challenge to ‘encourage’ developers to adhere to them.
In the last couple of years, we have implemented a Central ATC system which has made a huge difference in being able to effectively control the quality, performance and security etc. of the code being delivered to QA systems (thanks to Olga Dolinskaja and her excellent blog post series plus Bärbel Winkler for her experience in her Central ATC implementation blog post series. Their blogs and continued help is very much appreciated).
As well as development standards, the Transport Naming Standards are also important – especially for one particular business unit where they have developments moving to multiple QA systems and therefore knowing what goes where is paramount to ensure safe and reliable operations. Although reports had been built to provide statistics and details of transport adherence, this was all reactive and transports were routinely being released without ticket details and/or release schedule details. For audit purposes, ticket details are mandatory and for the Release Management team, the schedule details help to identify the scope for each release. The burden of correcting the transport name generally fell to a few build leads and due to the new DevOps structure and increased frequency of the releases, this was starting to consume up to 25% of their day. Not exactly an efficient use of time.
The main benefit therefore was to move this burden onto individual developers and therefore the ‘pain’ was then felt at the source. This then relieved the build leads of this bottle neck and time drain, and (as it turned out) enabled them to concentrate of building more automation as a result of this implementation. The benefits can be summarised as:
- Significant time saved to audit transports
- Easier mechanism to validate all transports for the weekly release are included
- Futher development based on change reference number now possible to generate the scope list automatically
Initially I considered whether it was appropriate to built a custom ATC check to house all the development in the Central ATC, but this was not the recommended approach (see this blog post on the subject) and therefore I set about building my own solution.
The solution came in two parts:
Provide links and information to all the development standards and transport naming standards details from within SE80. See another excellent blog post by Bärbel Winkler on this subject. This was to ensure that all the standards were in easy reach of all the developers, providing them will all the information required.
Transports were to be blocked at both creation and release via the CTS_REQUEST_CHECK BAdI to force the correct format – which is:
- <Programme/Region> = EPB, MOW, MDG, FSCM etc.
- <Project/Release/Sprint> = R5, 6, QR1, OOR, WR, QR01, ELS etc.
- <Change_Ref> = Service Now, Azure DevOps, SolMan or HPALM Ref Number
- <Description> = Self-explanatory
- <Delta No> = For subsequent transports for the same change ref 1, 2, 3, 4 etc.
A single remote enabled function module within the Central ATC system was required. This was just a wrapper for a local class that was split into 3 sections. See below:
DATA: lv_ok TYPE abap_bool, lo_transport_checks TYPE REF TO lcl_transport_checks. " Use the local class CREATE OBJECT lo_transport_checks exporting request = request type = type sysid = sysid text = text owner = owner. IF lo_transport_checks IS BOUND. * Read the config, mapped from the system id lv_ok = lo_transport_checks->get_config( ). IF lv_ok = abap_false. log = lo_transport_checks->get_log( ). lo_transport_checks->update_statistics( 001 ). RETURN. ENDIF. * Check the prefix against the config lv_ok = lo_transport_checks->check_prefix( ). IF lv_ok = abap_false. lo_transport_checks->log_valid_prefixes( ). log = lo_transport_checks->get_log( ). lo_transport_checks->update_statistics( 002 ). RETURN. ENDIF. * Check the transport title using regular expressions lv_ok = lo_transport_checks->check_title( ). IF lv_ok = abap_false. lo_transport_checks->log_follow_structure( ). log = lo_transport_checks->get_log( ). lo_transport_checks->update_statistics( 003 ). RETURN. ENDIF. ELSE. log = lo_transport_checks->log_binding_error( ). ENDIF. * Also log the success lo_transport_checks->update_statistics( 000 ).
The first section deals with ensuring the correct configuration was in place. Configuration tables are necessary to firstly ensure the linkage between the production systems to the respective development systems is in place.
* Read the config, mapped from the system id lv_ok = lo_transport_checks->get_config( ). IF lv_ok = abap_false. log = lo_transport_checks->get_log( ). lo_transport_checks->update_statistics( 001 ). RETURN. ENDIF.
Secondly, each production system can then have its own set of prefixes aligned to either a single or multiple development system scenario (in the case of dual track or more landscapes). If a valid prefix is not entered, the full set of valid prefixes is then sent back to the user, advising them of what they must do to proceed.
* Check the prefix against the config lv_ok = lo_transport_checks->check_prefix( ). IF lv_ok = abap_false. lo_transport_checks->log_valid_prefixes( ). log = lo_transport_checks->get_log( ). lo_transport_checks->update_statistics( 002 ). RETURN. ENDIF.
Lastly, the rest of the title is then checked using regular expressions for conformity to the correct structure.
* Check the transport title using regular expressions lv_ok = lo_transport_checks->check_title( ). IF lv_ok = abap_false. lo_transport_checks->log_follow_structure( ). log = lo_transport_checks->get_log( ). lo_transport_checks->update_statistics( 003 ). RETURN. ENDIF. ELSE. log = lo_transport_checks->log_binding_error( ). ENDIF.
At each section, any errors were logged and then sent back to the user in the satellite system via the local POPUP_WITH_TABLE_DISPLAY function module and a statistics table is updated for later use.
On the satellite system side, the CTS_REQUEST_CHECK BAdI has a simple stub of code as below, again with a local class to house the call to the Central ATC system.
METHOD if_ex_cts_request_check~check_before_creation. DATA: lv_result TYPE sysubrc, lt_log TYPE tchar255, lv_startpos_row TYPE sytabix, lv_startpos_col TYPE sytabix, lv_endpos_row TYPE sytabix, lv_endpos_col TYPE sytabix, lv_width TYPE sytabix, lv_line TYPE sytabix. FIELD-SYMBOLS: <lv_log> TYPE char255. me->remote_naming_check( EXPORTING type = type owner = sy-uname IMPORTING log = lt_log CHANGING text = text ). * Initial top left corner of dialog box lv_startpos_row = 5. lv_startpos_col = 28. IF lt_log IS NOT INITIAL. * Set the necessary bottom right corner lv_endpos_row = lines( lt_log ) + 3. LOOP AT lt_log ASSIGNING <lv_log>. lv_width = strlen( <lv_log> ). IF lv_width > lv_endpos_col. lv_endpos_col = lv_width. ENDIF. ENDLOOP. lv_endpos_col = lv_endpos_col + lv_startpos_col. CALL FUNCTION 'POPUP_WITH_TABLE_DISPLAY' EXPORTING endpos_col = lv_endpos_col endpos_row = lv_endpos_row startpos_col = lv_startpos_col startpos_row = lv_startpos_row titletext = 'Error Information' TABLES valuetab = lt_log EXCEPTIONS break_off = 1 OTHERS = 2. RAISE cancel. ENDIF. ENDMETHOD.
It should also be noted that since the main logic is housed centrally and within a development system, it is always ‘live’ and therefore any changes made must be done with the greatest of care. For that reason, ABAP Unit has been employed to ensure that a test harness is in place to provide assurance that when adding new functionality, the old functionality is not broken.
After being live for several weeks, further improvements were identified and have been implemented:
- Delta numbers were made mandatory and hence a colon number check was required
- Refence number type check (allowed Service Now or ADO document types)
- System specific additional requirements
Regardng point 3 above, catering for multiple production system requirements is not easy and although these are central standards, some systems require further checks for their own unique circumstances. In order to cater for these while keeping the core standards clean and not introducing a series of system specific IF’s or CASE statements into the central code, the multiple use functionality of the CTS_REQUEST_CHECK BAdI was taken advantage of, as well as ensuring the correct calling sequence i.e. central standards followed by local standards via the sort order option of the BAdI.
To illustrate this, the logical architecture is shown below
With the sequence of events as follows:
So far we have, five systems (2 landscapes) attached with another 4 systems imminent. As more systems come on board, feedback is positive and improvements suggested.
Any suggestions that are believed to be ‘good for all’ will be built into the core logic in the Central ATC system which why we were keen to centralise this in the first place. System specific developments that ‘don’t make the grade’ will be developed and supported by the respectrive system teams.
Current improvements on the backlog are:
- Provide a better UX than the POPUP_WITH_TABLE_DISPLAY FM. It is simple and functional but a better formatted HTML based UI would be more favourable
- API calls to both Service Now and Azure DevOps to validate the change reference number added to the title
- Configuration tables to be accessed via transactional CDS views/Fiori Elements rather than SM30 (nice to have but good excuse to develop experience in this area).
Also, I will be exploring whether it makes sense to move the functionality from the Central ATC to the SCP ABAP Environment (aka Steampunk). I’ll provide a blog on the experience when I get round to it.
Naturally I’d be very interested to hear if anyone else has implemeted anything similar and has suggestions for improvement. Equally, I’d be interested if you just find the blog post useful 🙂
Wow, quite the setup you've implemented there, Ian!
And, thanks for the mentions of two of my blogs.
Here is another one in which - even though the title doesn't mention it - I include a description of what I set up via a function module to check content of transport titles:
Getting adventurous – planning to switch a function module from using forms to using methods
As we didn't yet have our central ATC-system up and running when I was creating this FM, putting it into the central system never really occured to me. On the other hand - and although we do have some rather special processes in an interesting landscape - it's not quite as complex as yours. The biggest difference is that, anything originating in one development system will always end up in all of the test- and prod-systems on its transport path.
Thanks for the feedback and blog post link. I've just had a look through it which reminded me that one of my goals (that I didn't mention) was to code all of this within ADT and to use classes as much as possible plus any other new keywords etc. that were available in the original 7.52 environment. I don't tend to code much nowadays and felt the need to catchup to the corrent ways of working so I can update the development standards more effectively. Originally the code was developed on our 'old' NW 7.52 system but I have now moved this to a new S/4 HANA 1909 system (not used exclusively for the Central ATC btw but still a technical system). I also used it as an opportunity to introduce some newer techniques to a colleague who helped write some sections - ABAP Unit specifically.
BTW I've just started looking at porting it to Steampunk, witth the initial run of the SAP_CP_READINESS_CHECK being executed this morning. Lot's of findings to go through but look out for a blog post on this in the near future.
An interesting solution.
Thanks for sharing.
Thanks for the points raised. Indeed the CR attributes would be useful but of course you have to drill down into the transport to view these. Keeping them in the title means you can view the transports in a list and know where they belong.
For point 2, perhaps I am missing the point. Can you expand on the advantage of having the bespoke BAdI definition?
Regarding point 2, I just mentioned the option to create your own Z BAdI definition.
This way, instead of duplicating/inheriting all custom logic, you can focus just on the validation check itself. e.g. in your case, you may call a new BAdI method ZTRANSPORT_CHECK->NAMING_CHECK in method remote_naming_check. For every local system (with local standards) you'll have to implement just this part.
In the 2nd instance of the BAdI the only duplication of the code is the display of the log data since the validation check is local to the system anyway and does not go near the central system.
Once I clear up a couple of ATC checks and 'refine' some of the code, I'll put it on Github and add a link to it. Bit easier to see the logic and apply it to the diagrams above then.
hm, so it is not very clear for me which check logic is local and which is done on remote central ATC system, I thought config tables with global naming rules are hosted globally, i.e. this method
and the whole local class wrapped into the FM is hosted on remote ATC. Global rules in central ATC and local system rules in local tables. No?
If it is not the case, can you please describe logic separation between remote and local system?
Generally all the check logic and config tables are held in the Central ATC system. In 2 specific situations (for 2 specific systems) they have requirements beyond that of the global standards. Therefore, rather than 'pollute' the global standards in the Central ATC with system specific code, they implement their own 2nd tier BAdI with local logic (and a config table for 1 system).
As it happens, for one of these systems, I see value in their 2nd tier logic for the global standards and will therefore move the logic to the Central ATC system. That will then remove the need for the 2nd tier BAdI in this case.
Does that help?
Ok, so 1st tier Badi implementation call central ATC class wrapper and the 2nd implementation modifies it with local custom logic.
how do you see it? you see it in the 2nd tier Badi implementatoin when they call your class and pass their resulting value to central ATC for validation re-run?
The order in which the badi implementations are called is not clear.
I see vaue due to tthe extra checks they are performing. Specifically they validate Service Now ticket types which the central solution does not yet. Therefore, I would like to migrate this code to the central solution to be used by all systems.
The 2nd tier BAdIs in both instances are not tied to the central solution at all. They are completely independant.
The order is set so that the central BAdI implementation (1st tier) is always called first and the 2nd tier BAdI is called second. This is achieved by adding a layer vaue (Utilities->Sort) at the CTS_REQUEST_CHECK definition level. Due to SAP standard and 3rd party BAdI implemetations being present my implementations are set to be -2 and -1.
Thanks, now it is clear to me.