SAP ABAP ABAP Core Interview Questions

In SAP ABAP rounds, abap core questions are where configuration knowledge meets day-to-day behaviour β€” what a setting does, and what breaks in a live system when it is wrong.

Master the foundation of ABAP programming: program structure, variables, system fields, selection screens, validations, messages, exceptions and clean coding basics.

This page carries 25 reviewed SAP ABAP abap core interview questions, each with a complete written answer and no sign-in required. The set breaks down into 5 foundational, 8 mid-level and 12 advanced questions, so you can start at the top for a first interview or skip ahead to the scenario-based items for a senior round.

Rehearse these out loud rather than reading them. If you can explain each answer in your own words, including one realistic way it goes wrong on a project, you are covering what a normal SAP ABAP round on abap core expects.

25 ABAP Core questions with answers

easyABAP Core

1. What are the different types of internal tables in ABAP (Standard, Sorted, Hashed) and what access performance characteristics does each have?

A Standard table is an unordered internal table where records are stored in the sequence they are inserted; reading by index is fast (direct offset), but reading with a key requires a linear scan unless a secondary index is built, giving O(n) complexity. A Sorted table maintains records in ascending or descending order of a defined key; reading by key uses binary search giving O(log n) complexity, and inserts maintain sort order automatically which has a cost. A Hashed table has no linear index access at all - it is accessed only via a unique key using a hash algorithm giving near O(1) access time regardless of table size, but it cannot be accessed by index (no LOOP ... FROM x TO y using index makes sense the same way and READ TABLE ... INDEX is not supported). In real projects, hashed tables are chosen for large master data lookups (like a lookup of customer master into memory) where key-based access is frequent and table size is large; sorted tables are chosen when data must remain ordered and frequent key reads happen; standard tables are the default for general processing, especially when index-based access or appending is the primary operation.
easyABAP Core

2. What is the difference between a Function Module and a Method of a Class? When would you choose one over the other in a new development?

A Function Module is a procedural, standalone reusable unit stored in a function group with its own global data (function group's top include) shared among the function modules of that group; it has typed IMPORTING, EXPORTING, CHANGING, TABLES and EXCEPTIONS parameters, and can be made RFC-enabled for external calls. A Method belongs to a Class or Interface, follows Object-Oriented principles (encapsulation, inheritance, polymorphism), can raise class-based exceptions, and works with instance or static data depending on whether it's an instance method or a static method. In new developments, most modern ABAP projects prefer Class-based design because it supports better encapsulation, testability (unit testing with ABAP Unit), reduced global-data side effects, and easier reuse through interfaces. Function Modules are still commonly created when RFC-enabled communication is required (e.g., BAPI-style modules called from external systems, or when integrating with legacy code/BDC), or for very simple utility routines. A senior consultant would choose classes as the default approach for new business logic and only fall back to function modules when RFC/BAPI exposure is explicitly required or for enhancement framework requirements (e.g., certain exits are FM-based).
easyABAP Core

3. What is the difference between a Data Dictionary structure, a TYPE created in ABAP program, and a database table? When would you use each in a real project?

A database table (SE11, table maintenance) is a physical object stored in the database with a technical settings, delivery class, and buffering options - it actually holds persistent data. A DDIC structure is a logical grouping of fields with no data of its own; it is used to define the shape of internal tables, function module interfaces, or work areas and can be reused across programs. A local TYPE defined inside a program using TYPES statement is only visible within that program (or include) and is used when the structure is very specific to one piece of logic and not meant to be reused elsewhere. In real projects, DDIC structures are preferred whenever a structure is used in more than one program (interfaces, BAPIs, RFC-enabled function modules) because it centralizes maintenance - if a field changes, you change it once in DDIC. Local types are used for throwaway or program-specific structures, especially in reports where reusability isn't a concern.
easyABAP Core

4. Explain the difference between SELECT SINGLE, SELECT INTO TABLE, and SELECT ... ENDSELECT. Why is looping with SELECT...ENDSELECT generally discouraged in production code?

SELECT SINGLE fetches exactly one record based on a unique or sufficiently restrictive WHERE clause, it is efficient because the database only returns one row. SELECT INTO TABLE fetches the full result set in one round trip and stores it in an internal table, which is efficient for bulk processing since it minimizes database round trips. SELECT ... ENDSELECT opens a cursor and processes rows one by one in a loop. It is usually less efficient than bulk SELECT INTO TABLE for large result sets, especially when business logic inside the loop triggers additional database access or heavy processing. In production code we avoid SELECT...ENDSELECT unless we are handling extremely large datasets where holding all data in memory is not feasible - in which case using packages (PACKAGE SIZE) is a better controlled alternative. For most business logic, SELECT INTO TABLE combined with proper WHERE clause and index usage is preferred, followed by processing the internal table in a single loop.
easyABAP Core

5. What is a Field Symbol in ABAP, how does it differ from a Data Reference, and why are field symbols used heavily in loops with large internal tables?

A Field Symbol is a symbolic pointer that acts as an alias for a memory area - it does not have its own memory but points to an existing variable or table row once assigned using ASSIGN. A Data Reference is an actual object that holds a reference (address) to a data object and is declared with TYPE REF TO; it must be dereferenced explicitly using the arrow (->*) or the dereferencing operator to access the underlying value. In LOOP AT itab INTO wa, each iteration physically copies the row into the work area, which is expensive for large tables. Using LOOP AT itab ASSIGNING <fs> avoids this copy - the field symbol simply points to the row directly in the internal table's memory, which is significantly faster for large datasets and also required when the loop needs to modify the table row directly without needing a separate MODIFY statement. This is a very common performance optimization technique for large loops in production reports and interfaces.
mediumABAP Core

6. You put a breakpoint in a custom program but it's never hit even though you're sure the code path is being executed. What are the possible reasons and how do you debug this systematically?

There are several common reasons a breakpoint doesn't trigger. First, if the program runs in a different work process/session than where the breakpoint was set - for example, a background job, an RFC call, or update task (V1/V2) runs in a separate process, so a normal session breakpoint (set via SE38 debugger) won't be hit; in that case, an external breakpoint (set via 'Debugging' menu, active across sessions/users for a limited time) or an explicit BREAK-POINT statement should be used, or the job must be run in debug mode via SM37/'Debug active job' or SE38 F8 with 'Debug' checked appropriately. Second, if the code is inside a different include/program than expected due to enhancement or macro usage, the breakpoint might be set on a line that's actually never executed due to a differing code path (e.g., wrong client, wrong condition). Third, if the transport containing the code hasn't actually been activated/generated in that system, or you're debugging in the wrong client where an old version of the program is still active. I'd systematically verify: is this the correct system/client? Is it a background/RFC/update task requiring external or explicit breakpoint? Is the exact line actually reachable given the data (check by placing a breakpoint earlier and stepping through)? Once isolated, I'd re-set the breakpoint appropriately (external breakpoint or BREAK-POINT statement in code, or use SBWP/SM37 debug for jobs) to hit the correct execution context.
mediumABAP Core

7. In a production support ticket, users report that a custom Z-program sometimes shows the wrong currency conversion for cross-company transactions, but it works fine most of the time. How would you approach troubleshooting this intermittent issue?

Intermittent issues are often related to specific data combinations rather than the code being universally wrong, so I would first collect the exact failing document numbers, date, and company codes from the user to reproduce the issue rather than guessing. I would check whether the currency conversion routine relies on a fixed exchange rate type or date logic (e.g., using the posting date vs document date vs a hardcoded rate type like 'M') - a common root cause is that the exchange rate table (TCURR) simply doesn't have a rate maintained for a particular date/currency pair combination, causing the conversion to silently return zero or fall back to a default. I would use the debugger (SE38 -> set breakpoint, or external breakpoint if a background job) to step through the actual currency conversion function module call (e.g., a standard FM to convert amounts) with the specific failing data and inspect the input parameters (source currency, target currency, exchange rate date, rate type) at runtime. If it's a background job, I'd check the job log and possibly insert a break-point condition or use SAT for that specific variant. Once the root cause data-gap or logic gap is confirmed, I would fix either the missing exchange rate maintenance (functional/master data issue) or the logic bug (e.g., wrong date being passed) and retest with the exact same failing scenario plus a regression test on previously working scenarios.
mediumABAP Core

8. You are asked to build an interface that receives 50,000 material records daily from a third-party system and update material master data. What ABAP design considerations would you keep in mind to ensure this runs reliably every day?

For a high-volume daily interface, I would design it around bulk processing rather than record-by-record calls: read the incoming data (IDoc, file, or API payload) into internal tables, and process it in manageable packages (e.g., using PACKAGE SIZE if reading from database, or chunking manually if from a file) to avoid memory overload and to allow partial commit/checkpoint recovery in case of failure mid-run. I would use standard BAPIs like BAPI_MATERIAL_SAVEDATA wrapped in an outer loop with proper COMMIT WORK handling per package rather than one giant commit at the end, so a failure doesn't force reprocessing all 50,000 records. Error handling is critical - each record's success/failure should be logged into an application log (using SLG0/BAL_LOG_CREATE) so functional team can review failures without digging through job logs, and failed records should be isolated for reprocessing rather than blocking the whole batch. I would also build in idempotency - reprocessing the same file/IDoc twice shouldn't create duplicates - typically by checking existence before create or using a control table to track processed record keys. Performance-wise, I'd avoid nested SELECTs, use FOR ALL ENTRIES for master data validation, and schedule the job with appropriate parallel processing (e.g., background job splitting by material group) if a single run risks exceeding the available batch window.
mediumABAP Core

9. You are designing a custom module pool transaction that must handle both online (dialog) entry and a mass upload of thousands of records via a background program, using the same business validation logic. How would you architect this to avoid code duplication and ensure performance in both modes?

The key architectural principle is separation of the business logic layer from the presentation/orchestration layer. I would design a reusable class (or function group) that contains pure business validation and posting logic, taking structured input (e.g., a table type representing one or more records) and returning structured results (success/failure per record with messages) with no dependency on screen elements (no direct SCREEN-*, no PBO/PAI logic inside it). The module pool (dialog program) would call this shared class one record at a time (since the user interacts with one record on screen), handling PBO/PAI, screen validations, and calling the shared validation/posting method for the single record being processed, displaying any returned messages directly on the screen. The background/mass upload program would call the exact same shared class but pass a bulk internal table, processing records either one-by-one in a loop (reusing the same method signature designed for single-record calls, if volume is moderate) or ideally with a bulk-processing variant of the method designed to handle multiple records efficiently (avoiding repeated overhead per call, e.g., bulk validation queries using FOR ALL ENTRIES internally) if volume is very large. This ensures both modes share exactly the same validation and posting rules (single source of truth, avoiding the classic problem of dialog and background logic drifting apart over time), while each entry point (dialog vs background) is optimized for its own performance profile - dialog optimized for responsiveness per single record, background optimized for bulk throughput.
mediumABAP Core

10. A new joiner on your team wrote a custom program using nested loops (LOOP inside LOOP) to match sales orders against delivery data, and it works fine in dev with 100 records but times out in production with 200,000 records. How do you explain the issue and guide them to a fix?

I would first explain that nested LOOP AT itab1 ... LOOP AT itab2 (without a key-based read) has quadratic O(n*m) complexity - for 100 records this might mean 10,000 comparisons which is instant, but for 200,000 sales orders against, say, 200,000 deliveries, it balloons to up to 40 billion comparisons, which is why it times out only in production. I'd guide them to redesign the matching logic using one of two standard approaches: either convert the inner table to a sorted or hashed table keyed on the matching field (e.g., VBELN) and use READ TABLE WITH KEY (binary search for sorted, direct for hashed) inside the outer loop instead of a nested LOOP, reducing complexity to roughly O(n log n) or O(n); or, if a simple 1:1 or 1:n merge is needed and both tables can be sorted on the join key, use a single-pass merge algorithm (advance pointers through both sorted tables together) achieving O(n+m). I would also point out that this is a great example of why performance testing should always include realistic production-like data volumes in a QA/performance testing cycle, not just dev sample data, and recommend they always ask 'what happens at 10x or 100x this data volume' whenever writing nested loops.
mediumABAP Core

11. A custom report that used to run in 2 minutes is now taking 45 minutes after go-live volume increased. The report reads material master data in a loop using SELECT SINGLE inside a loop over sales items. How would you approach diagnosing and fixing this?

This is a classic case of the 'SELECT inside LOOP' anti-pattern causing repeated database round trips as data volume grows. First, I would use ST05 (SQL trace) while executing the report for a representative dataset to confirm that the same table (e.g. MARA/MARC) is being hit repeatedly with similar WHERE clauses, and I would check SE30/SAT for runtime analysis to see where the time is actually spent - CPU-bound in ABAP or database-bound in SELECT statements. Once confirmed, the fix is to replace SELECT SINGLE inside the loop with a single bulk SELECT ... FOR ALL ENTRIES IN itab (after deduplicating and checking the driver table is not empty) into an internal table, ideally a hashed table keyed on MATNR, and then use READ TABLE WITH KEY (or direct hashed access) inside the loop instead of hitting the database again. I would also check if an index exists on the WHERE clause fields to avoid a full table scan, and consider adding one via ADBC/DB02 review if truly necessary and justified. Finally, I'd retest with ST05 to confirm the number of database round trips dropped from N (per sales item) to 1, and validate the total runtime returned to acceptable range.
mediumABAP Core

12. During debugging, you notice that a work area's value changes unexpectedly between two statements even though no explicit assignment happens between them in the code you're viewing. What could cause this and how would you find the actual culprit?

This classic symptom usually points to aliasing via field symbols/references, a PERFORM subroutine using a global/shared work area, or a CHANGING parameter being modified inside a called function/method/subroutine that you're not directly viewing at that point. I would use a Watchpoint (Debugger -> Breakpoints/Watchpoints -> set watchpoint on the specific field/variable) which pauses execution the moment that memory location's value changes, regardless of which line of code causes it - this is far more effective than manually stepping through many lines. Once the watchpoint triggers, the debugger shows exactly which statement (potentially deep inside a called subroutine, function module, or method) modified the value, revealing hidden coupling - commonly this turns out to be a global variable shared across a PERFORM/FORM without being passed explicitly, or a field symbol that was assigned to the same memory area unintentionally (e.g., ASSIGNing a field symbol to a component of the same structure elsewhere in the program). Once identified, the fix is typically to pass data explicitly via parameters instead of relying on shared global state, or correct the field symbol/reference assignment that caused unintended aliasing.
mediumABAP Core

13. You are enhancing a custom sales order creation program. The business wants an additional custom field to be validated and saved only when the sales order type is 'ZOR1'. How would you design this using existing SAP enhancement techniques rather than modifying standard code?

First I would check whether the standard transaction (VA01/VA02 or the underlying BAPI like BAPI_SALESORDER_CREATEFROMDAT2) already has a suitable BAdI or user exit available for this validation, using SE18/SE19 for BAdIs or the enhancement framework (SE80 -> enhancement spots) as well as checking for classic user exits like USEREXIT_SAVE_DOCUMENT in include MV45AFZZ for sales order processing, which is a commonly used exit point for this kind of custom validation in SD. I would implement the logic in the exit/BAdI implementation, checking VBAK-AUART = 'ZOR1' before applying validation, so unrelated order types are untouched. The custom field itself would be added via an append structure to the relevant structure (e.g. VBAK/VBAP append or a Z-table depending on whether it's header or item level, and whether it needs to be visible on the screen via screen exit/subscreen). I would avoid modifying SAP standard objects directly (no core modification, avoiding an access key requirement and upgrade conflicts), and instead keep the logic isolated in the customer include/BAdI implementation so that during upgrades the enhancement remains intact and easily identifiable in a where-used analysis.
hardABAP Core

14. You're told that a report gives correct output in the foreground (SE38, F8) but wrong output when scheduled as a background job. What categories of causes would you investigate?

This is a common and tricky category of bug because the code is identical but the execution context differs. I would investigate several categories: first, variant/selection-screen differences - background jobs run with a saved variant, and if the variant has different default values, date ranges, or a checkbox not properly set compared to what was manually entered in foreground, the output will differ; I'd check the actual variant values used in SM37 job details versus what was entered in foreground testing. Second, user/authorization context differences - background jobs typically run under a specific technical/batch user (often different from the user testing in foreground), and if the program has any authorization-dependent logic (e.g., filtering data based on SY-UNAME or authorization checks that behave differently for the batch user), results will differ. Third, date/time and system field differences - SY-DATUM, SY-UZEIT or client/language settings could differ if the job runs at a different time or in a different logon language than the foreground test. Fourth, memory/ABAP memory or SPA/GPA parameter dependencies - if the program reads memory ID's (GET PARAMETER) that were set interactively in foreground but are empty in a batch context, logic branches could differ. I would systematically compare the exact job log, variant, and execution user against the foreground test to isolate which of these factors is causing the discrepancy, often by adding diagnostic logging (e.g., writing SY-UNAME, variant values, and key dates to an application log) during the job run for direct comparison.
hardABAP Core

15. A background job processing 100,000 records dumps with a TIME_OUT or memory-related short dump after running for hours, but the same logic works fine for a smaller test file. How do you debug and fix this without being able to reproduce it interactively?

Since it's hard to interactively debug a long-running background job, I would first check ST22 for the exact short dump details (TSV_TNEW_PAGE_ALLOC_FAILED for memory issues, or TIME_OUT for exceeding the maximum allowed dialog/background runtime) including the ABAP call stack at the point of failure, which usually points directly to the problematic statement (e.g., an internal table growing too large, or a SELECT without proper restrictions returning a huge result set). I'd check SM37 job log and the runtime statistics to understand how far the job progressed before failing, and correlate it with the data volume being processed at that point. If it's a memory issue, I would look for internal tables holding the entire dataset in memory unnecessarily (instead of processing in packages/chunks) or unbounded internal table growth (e.g., appending without ever clearing processed data). I would redesign the program to process data in controlled batches with an explicit COMMIT/checkpoint and clearing of internal tables after each package is processed, freeing memory. If it's a genuine TIME_OUT, I'd look at parallelizing the job (splitting into multiple background job instances by key range) or scheduling it as a proper background job that has a much higher time limit than dialog processing, since background jobs typically don't have the same short timeout as dialog work processes unless a specific TIME_OUT profile parameter or explicit runtime limit applies. I would also add checkpoint/restart logic so that if a future failure occurs, the job can resume from the last processed package instead of starting over.
hardABAP Core

16. You are asked to review a proposed technical design where a custom real-time interface calls an external REST API synchronously from inside a core SD sales order save user exit for every order, to fetch a fraud score before allowing save. As the architect, what concerns would you raise?

This design raises several serious architectural concerns. First, calling a synchronous external HTTP/REST call from inside a core save exit means the sales order save transaction's response time is now directly dependent on the availability and latency of an external system - if that external API is slow or down, every single sales order save in the system will hang or fail, creating a hard availability dependency on a third-party system for a core business process. Second, this introduces transactional risk - if the external call succeeds but something later in the save logic fails and the document doesn't actually get saved (or the LUW rolls back), the fraud check may have already been consumed/counted externally inconsistently, and conversely if the ABAP session times out waiting for a slow external response, the user experience degrades badly. I would recommend evaluating whether the fraud check truly needs to be synchronous and blocking, or whether an asynchronous pattern is more appropriate - for example, allowing the save to proceed and triggering an asynchronous check (via a queued RFC, or output-determination/workflow-triggered follow-up) that flags orders for review if the fraud score comes back high, rather than blocking the save entirely. If a synchronous blocking check is a genuine hard business requirement, I would insist on strict timeout handling (short timeout with clear fallback behavior, e.g., defaulting to 'hold for manual review' rather than indefinite hang, if the external system doesn't respond in time), and recommend load/resilience testing to understand system behavior if the external API becomes slow or unavailable, plus proper monitoring/alerting for this dependency.
hardABAP Core

17. You're reviewing a colleague's custom ALV report that dynamically builds a field catalog based on customizing settings, and it occasionally shows incorrect column headers or wrong data types for certain configurations. How would you approach root-causing this dynamic field catalog issue?

Dynamic field catalogs built at runtime are prone to subtle bugs because the field catalog structure (usually LVC_T_FCAT or SLIS_T_FIELDCAT_ALV) is populated conditionally based on customizing, so I would first reproduce the exact customizing configuration that causes the wrong header/type, since dynamic logic bugs are usually configuration-path-dependent rather than universal. I would set a breakpoint at the point where the field catalog entries are being populated and step through with the specific failing configuration to see which condition branch is being (incorrectly) taken - a common root cause is that the code references a wrong DDIC field for REF_TABLE/REF_FIELD when setting data element/domain-based texts, causing REPTEXT/SCRTEXT columns to be mismatched, or a wrong ROLLNAME assignment causing conversion routine (CONVEXIT) or data type mismatch. Another common cause is off-by-one errors or incorrect looping when the catalog is built dynamically from a customizing table, where the column position (COL_POS) or field name doesn't correctly map to the actual dynamic internal table structure built via RTTS (CREATE DATA using cl_abap_structdescr). I would validate the actual generated field catalog content (by displaying it in debugger or writing it out) against the dynamic internal table's real structure to catch any mismatch, then fix the specific logic branch and retest across the various customizing configurations, not just the one that failed, to catch related regressions.
hardABAP Core

18. A custom interface program uses a Z-table as a staging area and inserts millions of rows daily, purging old data periodically. Over time, the table's performance has degraded significantly even for simple SELECTs by key. What would you investigate and recommend?

Continuous high-volume insert and delete on a table over time commonly leads to issues like index fragmentation, table/index statistics becoming stale, or the table growing far larger than expected if the purge job isn't running effectively or efficiently (e.g., soft-deletes via a flag instead of physical deletes, leaving dead rows). I would first check with Basis/DBA the current table size, number of rows, and whether database statistics are up to date (e.g., via DB02 or database-specific tools) since outdated statistics can cause the database optimizer to choose a poor execution plan even for simple key-based SELECTs. I would verify the purge job is actually running as scheduled and effectively removing old data (check SM37 for job history and actual row counts before/after), and confirm the appropriate index still exists and matches the actual access pattern (WHERE clause) used by both the interface program and any reporting on this table. If the table's access pattern has evolved (e.g., new reports querying by a different field not covered by existing indexes), I'd propose adding a suitable secondary index. I would also discuss with the business/functional team whether the retention period can be shortened or whether table partitioning (database-specific feature) makes sense for a very high-volume staging table, and consider archiving strategies (SARA/ILM if applicable) instead of a simple ad hoc purge job for a more robust long-term solution.
hardABAP Core

19. Your custom Z-report joins three large custom Z-tables using nested SELECTs and it's slow. The functional team says all three tables are needed together frequently across multiple reports. What performance and design options would you consider, including database-level options?

First, at the ABAP level, I would eliminate nested SELECTs (SELECT inside LOOP) in favor of either a single Open SQL statement with an explicit INNER/LEFT JOIN across the three tables (if the relationship and key fields allow a clean join), or separate bulk SELECT ... FOR ALL ENTRIES calls per table followed by combining results in ABAP using sorted/hashed internal tables, whichever performs better depends on data volume and selectivity - joins are efficient when the database can use good indexes on the join columns, but a very large join across three tables without good indexes can also be costly, so I'd test both approaches using ST05 to compare actual execution plans and cost estimates. At the design level, since this data combination is needed frequently across multiple reports (not just one), I would consider creating a CDS view (if on a suitably recent NetWeaver/HANA version) that encapsulates this join logic once at the database/semantic layer, pushing computation down to the database and allowing reuse across multiple consuming programs/reports without duplicating join logic; this also opens the door to leveraging HANA's columnar processing strengths. I would also check whether appropriate secondary indexes exist on the Z-tables for the join/filter columns, working with Basis/DBA to add one if consistently missing and justified by the access pattern, being mindful indexes add overhead to write operations so this needs a balanced decision, especially for high-insert tables.
hardABAP Core

20. A custom class-based exception is being raised deep inside a call stack (Class A calls Class B calls Class C), but the calling program only shows a generic 'exception occurred' message without the actual root cause detail. How would you debug and improve this?

First, I would set a breakpoint at the point of RAISE EXCEPTION in Class C (or use a global breakpoint on the exception class's constructor, or 'break at exception' feature in the debugger which halts execution the moment any exception of a chosen class is raised, regardless of where it's caught) to inspect the actual root cause data (e.g., the specific field values or message text) at the moment it's raised. Often the issue is that the calling program's CATCH block only catches the generic exception class (e.g., CX_ROOT or a very generic custom superclass) and just displays a static message instead of using the exception object's methods like GET_TEXT( ) or a custom GET_ERROR_DETAILS( ) method to retrieve the specific message/attributes that were set when the exception was raised. I would improve this by ensuring the custom exception class carries meaningful attributes (e.g., MV_MATNR, MV_REASON) set in its constructor, and update the CATCH block to call GET_TEXT( ) or display those specific attributes to the user/log, rather than swallowing detail. I'd also check if exceptions are being caught too broadly at an outer layer (e.g., catching CX_ROOT generically) losing the specific subclass information, and recommend catching the most specific exception class first, then broader ones, following proper exception hierarchy design.
hardABAP Core

21. A background job that processes vendor payments occasionally fails with a lock table overflow (enqueue) error, especially during month-end. How would you diagnose and resolve this?

Lock table overflow typically means the program is setting more enqueue locks than the system's lock table capacity allows (parameter enque/table_size), often because a single logical unit of work is locking too many objects at once, or locks are not being released promptly (missing DEQUEUE or long-running LUW holding locks). I would first check SM12 during a job run to see the volume and pattern of locks being held - are they piling up for a specific object type (e.g., vendor/document lock FI-related)? I'd review the program logic to see if it acquires enqueue locks (e.g., ENQUEUE_EFDOCUMENT or similar FI locking FM) per vendor per document without releasing (DEQUEUE) immediately after each unit of work, especially inside a large loop processing many vendors in one single LUW. The fix is typically to release locks as soon as each logical unit of work (e.g., one vendor payment document) completes, rather than holding locks for the entire batch duration, and to consider splitting the job to run in smaller parallel chunks (parallel processing by vendor range) rather than one massive sequential job holding many concurrent locks. If genuinely more locks are required simultaneously due to business need, I'd work with Basis to review and potentially increase enque/table_size, but that is a last resort after the application-level lock management is optimized.
hardABAP Core

22. Your team inherited a legacy custom program full of nested PERFORM subroutines with heavy use of global variables. The business now wants to add new functionality and unit test it. How would you approach refactoring this safely without breaking existing functionality?

Before touching anything, I would do a thorough impact analysis - use where-used list (SE38/SE80) to understand all callers of the program/subroutines, and check for any other programs or exits depending on the global variables or subroutine behavior. Given the risk of a 'big bang' rewrite, I'd favor an incremental strangler-pattern approach: wrap the new functionality in a new, well-encapsulated local class inside the same program (or a separate reusable class if it needs to be shared) with clear inputs/outputs instead of relying on global variables, and call this new class from the existing PERFORM structure rather than rewriting the legacy logic outright. This isolates new logic so it can be unit tested using ABAP Unit test classes independent of the legacy global state. For the existing legacy code, I would avoid modifying core logic unless absolutely necessary for the new requirement, and if I must touch it, I'd add regression test scenarios (manual test scripts or SAT/data comparisons) to validate output before and after the change on real-like data. Long term, I'd recommend a phased refactor plan to progressively move logic into testable classes rather than a risky single refactor, given legacy programs often have hidden dependencies via global memory (e.g., SPA/GPA parameters, EXPORT/IMPORT memory) that are easy to break silently.
hardABAP Core

23. Your organization is migrating a large custom ABAP landscape from a classical ECC on AnyDB setup to S/4HANA. As the architect, what ABAP-specific technical debt would you prioritize addressing before or during the migration, and why?

I would prioritize addressing custom code that relies on direct SELECTs to tables that are now replaced or restructured in S/4HANA's simplified data model (e.g., custom programs directly reading old aggregate tables that have become compatibility views in S/4HANA), since this is a functional correctness risk, not just a performance one - the SAP Custom Code Migration app / ATC checks with the S/4HANA-specific check variant should be run early to identify all such usages across the custom code base. Second, I would prioritize replacing SELECT...ENDSELECT and heavy nested-loop patterns with modern Open SQL and CDS-view-based access, since S/4HANA on HANA rewards code that is designed to push computation to the database rather than looping row-by-row in the ABAP layer - this is where the biggest performance wins from the platform migration actually come from, and leaving old code patterns unchanged means the organization pays for HANA hardware but doesn't get the performance benefit. Third, I would review custom code using obsolete or deprecated statements/table access patterns flagged specifically by the SAP-provided remediation worklist (from the readiness check/custom code migration tooling), fixing simplification-database-view related issues and any use of now-removed function modules or tables. I would sequence this work by risk and volume - fixing functional-breaking issues first (must-fix for go-live), then tackling high-volume/high-frequency programs for performance modernization as a near-term post-go-live phase, rather than attempting to modernize every single custom object at once, which is rarely feasible within a fixed project timeline.
hardABAP Core

24. As an ABAP Architect, how would you define and enforce coding and performance standards across a large team of consultants from different vendors working on a multi-year S/4HANA implementation, to prevent recurring performance and maintainability issues in production?

I would start by establishing a documented ABAP development guideline covering the recurring problem areas seen across projects - mandatory use of code inspector (SCI) checks with a defined custom check variant covering things like nested SELECT/LOOP patterns, missing FOR ALL ENTRIES safeguards, avoidance of SELECT *, proper use of hashed/sorted tables for large lookups, and naming conventions - and make passing these checks a mandatory gate before transport release, ideally integrated into the transport approval process (e.g., via ATC - ABAP Test Cockpit - checks tied to the transport of copies workflow so non-compliant code can't move beyond a certain quality gate). Beyond automated checks, I would establish a peer/architect code review process for any object touching high-risk areas (interfaces, mass-data programs, exits on core business documents), focusing especially on database access patterns and modularization/OO design consistent with the shared architecture. I would also build reusable frameworks/utility classes (e.g., a shared application logging class, a shared error-handling/exception base class, a shared parallel-processing helper) so consultants aren't reinventing infrastructure-level logic inconsistently project-wide, reducing the chance of quality drift across different vendor teams. Finally, I'd institute lightweight but consistent performance testing (using realistic production-like volumes in a dedicated performance/QA system) as a required step before go-live for any object handling significant data volume, rather than relying purely on functional testing, and set up periodic production monitoring (e.g., via ST03N/SWLT or custom dashboards) to catch performance regressions early after go-live rather than waiting for user complaints.
hardABAP Core

25. You're designing the exception handling and logging strategy for a strategic, long-lived custom framework (used by 200+ Z-programs across the enterprise) that will be maintained by multiple teams over many years. What principles would guide your design to keep it maintainable and diagnosable long-term?

For a framework with this scale and lifespan, I would design a layered exception hierarchy rooted in a common custom superclass (e.g., ZCX_FRAMEWORK_ROOT extending CX_STATIC_CHECK or CX_DYNAMIC_CHECK as appropriate) so that all specific exceptions across the 200+ consuming programs share consistent behavior (structured message handling, consistent GET_TEXT( ) implementation, common attributes like a correlation/transaction ID), while still allowing specific subclasses for specific error categories (validation errors, technical/integration errors, authorization errors) so calling code can catch at the right level of granularity. I would build a centralized logging utility (wrapping BAL_LOG_CREATE/BAL_LOG_MSG_ADD from the Application Log or a custom equivalent) that every exception's constructor or a common CATCH-handling utility automatically writes to, ensuring every failure across all 200+ programs is captured consistently with enough context (program, user, key business data, timestamp, correlation ID) for later diagnosis, without every individual developer needing to remember to log manually and inconsistently. Critically, I would version this framework carefully - since many teams depend on it, any change to the base exception class or logging utility must be backward compatible (e.g., adding new optional attributes rather than changing existing method signatures) and released with clear documentation and a deprecation policy for anything being phased out, to avoid breaking the 200+ dependent programs on a framework update. I would also build in configurability for log verbosity/severity per calling program (since not everything needs to escalate to the same severity) and establish a governance process (a framework owner/CCB - change control board) so ad hoc modifications by individual teams don't fragment the framework's consistency over time.

Related lesson

ABAP Program Structure and Runtime Flow

Related topics

Next practice step