GitHub Copilot for ABAP: Boost Productivity with AI

Category
Tools
Published
Author
Johannes

GitHub Copilot is the AI assistant from GitHub/Microsoft that massively boosts ABAP developer productivity starting in 2025. It understands ABAP syntax, knows RAP patterns and can write, explain, test and refactor code – directly in VS Code or Eclipse ADT.

What is GitHub Copilot?

Copilot = Your AI Pair-Programmer

  • Based on OpenAI GPT-4 (trained on billions of lines of code)
  • Writes code while you type (auto-completion on steroids)
  • Answers questions in chat (code explanations, debugging)
  • Refactors legacy code to modern ABAP Cloud
  • Generates unit tests automatically
  • Understands ABAP syntax, RAP, CDS Views, OData

Setup: Activating Copilot

Prerequisites

  • GitHub Account with Copilot license
    • Individual: $10/month
    • Business: $19/user/month
    • Free for Students/Open Source Maintainers
  • VS Code or Eclipse ADT (from 2025)

Installation in VS Code

Terminal window
# 1. Open VS Code
# 2. Extensions → Search "GitHub Copilot"
# 3. Install: "GitHub Copilot" + "GitHub Copilot Chat"
# 4. Sign in with GitHub Account
# 5. Copilot activates automatically

Check status:

Bottom-right in VS Code: Copilot icon
- Green = Active
- Red = Error (check sign-in)

Activating ABAP Support

settings.json
{
"github.copilot.enable": {
"*": true,
"abap": true, // ← Explicitly enable ABAP
"plaintext": false
},
"github.copilot.inlineSuggest.enable": true,
"editor.inlineSuggest.enabled": true
}

Code Generation: Copilot as Co-Pilot

Example 1: RAP Behavior Implementation

Your comment:

" Implement validation to check if customer exists in I_Customer
METHOD validateCustomer FOR VALIDATE ON SAVE
IMPORTING keys FOR Travel~validateCustomer.

Copilot suggests: (press Tab to accept)

" Read travel data
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( CustomerId )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
" Get all customer IDs
DATA(lt_customer_ids) = VALUE string_table(
FOR travel IN lt_travel ( travel-CustomerId )
).
" Check if customers exist
SELECT Customer
FROM I_Customer
FOR ALL ENTRIES IN @lt_customer_ids
WHERE Customer = @lt_customer_ids-table_line
INTO TABLE @DATA(lt_existing_customers).
" Report errors for non-existing customers
LOOP AT lt_travel INTO DATA(ls_travel).
IF NOT line_exists( lt_existing_customers[ table_line = ls_travel-CustomerId ] ).
APPEND VALUE #(
%tky = ls_travel-%tky
%element-CustomerId = if_abap_behv=>mk-on
) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%element-CustomerId = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |Customer { ls_travel-CustomerId } does not exist|
)
) TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.

→ 30 lines of code generated in seconds!

Example 2: Creating a CDS View

Your prompt:

-- CDS View for Sales Orders with customer name and total amount
-- Include association to customer

Copilot generates:

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Orders with Customer Data'
define view entity ZI_SalesOrderWithCustomer
as select from I_SalesOrder as Order
association [0..1] to I_Customer as _Customer
on Order.SoldToParty = _Customer.Customer
{
key Order.SalesOrder,
Order.SalesOrderType,
Order.SalesOrganization,
Order.SoldToParty,
Order.CreationDate,
@Semantics.amount.currencyCode: 'TransactionCurrency'
Order.TotalNetAmount,
@Semantics.currencyCode: true
Order.TransactionCurrency,
Order.OverallSDProcessStatus,
// Customer data via association
_Customer.CustomerName,
_Customer.CityName,
_Customer.Country,
// Expose association
_Customer
}

Example 3: Writing a Unit Test

Your comment:

" Unit test for validateCustomer method
" Test case: Customer does not exist → should fail

Copilot generates:

CLASS ltc_validate_customer DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_env TYPE REF TO if_cds_test_environment.
METHODS:
setup,
teardown,
test_customer_not_exists FOR TESTING,
test_customer_exists FOR TESTING.
ENDCLASS.
CLASS ltc_validate_customer IMPLEMENTATION.
METHOD setup.
mo_env = cl_cds_test_environment=>create_for_multiple_cds(
i_for_entities = VALUE #(
( i_for_entity = 'ZI_Travel' )
( i_for_entity = 'I_Customer' )
)
).
" Insert test customer
mo_env->insert_test_data(
i_data = VALUE i_customer(
( Customer = '000042' CustomerName = 'Test Customer' )
)
).
ENDMETHOD.
METHOD test_customer_not_exists.
" Arrange: Travel with non-existing customer
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( CustomerId )
WITH VALUE #( ( %cid = 'T1' CustomerId = '999999' ) )
FAILED DATA(failed).
" Act: Commit triggers validation
COMMIT ENTITIES
RESPONSE OF zi_travel
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" Assert: Should fail
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-travel
msg = 'Validation should fail for non-existing customer'
).
cl_abap_unit_assert=>assert_bound(
act = commit_reported-travel[ 1 ]-%msg
msg = 'Error message should be present'
).
ENDMETHOD.
METHOD test_customer_exists.
" Arrange: Travel with existing customer
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( CustomerId )
WITH VALUE #( ( %cid = 'T1' CustomerId = '000042' ) )
FAILED DATA(failed).
" Act
COMMIT ENTITIES
RESPONSE OF zi_travel
FAILED DATA(commit_failed).
" Assert: Should succeed
cl_abap_unit_assert=>assert_initial(
act = commit_failed-travel
msg = 'Validation should pass for existing customer'
).
ENDMETHOD.
METHOD teardown.
ROLLBACK ENTITIES.
mo_env->destroy( ).
ENDMETHOD.
ENDCLASS.

Copilot Chat: Your Interactive Assistant

Opening Chat

VS Code:
- Ctrl+Shift+I (Windows/Linux)
- Cmd+Shift+I (Mac)
- Or: Sidebar → Chat icon

Example Dialogs

Explaining Code

You:

Explain this ABAP code:
[Select and send code]
DATA(lt_result) = FILTER #( lt_data WHERE status = 'A' ).

Copilot:

This line uses ABAP's inline FILTER expression to create a filtered
internal table. It:
1. Takes source table lt_data
2. Filters rows where status field equals 'A'
3. Assigns result to lt_result (type inferred automatically)
This is equivalent to:
LOOP AT lt_data INTO DATA(ls_row) WHERE status = 'A'.
APPEND ls_row TO lt_result.
ENDLOOP.
But more concise and performant (single-pass operation).

Refactoring

You:

Refactor this to use modern ABAP syntax:
LOOP AT lt_orders INTO ls_order.
IF ls_order-status = 'O'.
ls_order-priority = 'HIGH'.
MODIFY lt_orders FROM ls_order.
ENDIF.
ENDLOOP.

Copilot:

" Modern ABAP with inline declaration and modify expression:
MODIFY lt_orders FROM VALUE #(
FOR order IN lt_orders WHERE ( status = 'O' )
( CORRESPONDING #( order ) priority = 'HIGH' )
).
" Or even more concise with COND:
lt_orders = VALUE #(
FOR order IN lt_orders
( CORRESPONDING #( order )
priority = COND #( WHEN order-status = 'O' THEN 'HIGH'
ELSE order-priority ) )
).

Debugging Help

You:

Why does this fail?
SELECT * FROM i_salesorder
WHERE TotalNetAmount > @lv_threshold
INTO TABLE @lt_orders.
Error: "Field TotalNetAmount unknown"

Copilot:

The error occurs because field names in CDS views are case-sensitive
in the ABAP SQL syntax.
Fix: Check the actual field name in I_SalesOrder CDS view.
It's likely "TotalNetAmount" in the view definition, but you need
to use it exactly as defined.
Try:
SELECT * FROM I_SalesOrder
FIELDS SalesOrder, TotalNetAmount, TransactionCurrency
WHERE TotalNetAmount > @lv_threshold
INTO TABLE @lt_orders.
Also note: Use explicit FIELDS clause for clarity and performance.

Prompt Engineering for ABAP

Good Prompts

" Specific + Context
" Create a RAP determination that calculates TotalPrice
" as BasePrice * Quantity * (1 - Discount/100)
" Trigger: on modify when Quantity or BasePrice changes
" With example
" Convert this SELECT to use path expressions:
" SELECT * FROM vbak
" INNER JOIN kna1 ON vbak~kunnr = kna1~kunnr
" WHERE vbak~vbeln = @lv_order.
" Name constraints
" Write a validation that checks:
" 1. BeginDate not in past
" 2. EndDate after BeginDate
" 3. Max duration 365 days
" Use message class ZTRAVEL
" Specify technology
" Implement OData V4 action 'approve' for Travel entity
" Input: ApprovalNote (string)
" Output: Updated Travel with Status='A'
" Use RAP Business Object pattern

Bad Prompts

" Too vague
" Write some validation
" Too complex (in one prompt)
" Build a complete RAP application with Travel, Booking, Customer,
" including all CRUD, validations, determinations, actions, OData service,
" Fiori UI, and deployment to BTP
" Without context
" Fix this code:
SELECT * FROM table WHERE field = value.
" → Copilot doesn't know: Which table? What error?
" Unclear requirement
" Make this better
" → What does "better" mean? Performance? Readability?

Prompt Templates

Template: Validation

" Validation for [ENTITY]: [VALIDATION_NAME]
" Business rules:
" 1. [RULE_1]
" 2. [RULE_2]
" Trigger: on save, fields [FIELD_LIST]
" Error message: [MESSAGE_TEXT]
METHOD [methodName] FOR VALIDATE ON SAVE
IMPORTING keys FOR [Entity]~[validationName].

Template: CDS View

-- CDS View: [VIEW_NAME]
-- Purpose: [DESCRIPTION]
-- Data source: [SOURCE_TABLE/VIEW]
-- Include:
-- - Fields: [FIELD_LIST]
-- - Association to: [RELATED_ENTITIES]
-- - Filter: [WHERE_CONDITION if any]

Template: Unit Test

" Unit test for [METHOD_NAME]
" Test case: [SCENARIO]
" Arrange: [SETUP_DESCRIPTION]
" Act: [ACTION_DESCRIPTION]
" Assert: [EXPECTED_RESULT]

Practical Use Cases

Use Case 1: Migrating Legacy Code

Before: Classic ABAP

" Legacy code:
REPORT z_sales_report.
TABLES: vbak.
SELECT-OPTIONS: s_vbeln FOR vbak-vbeln.
START-OF-SELECTION.
SELECT vbak~vbeln vbak~erdat vbak~kunnr kna1~name1 vbak~netwr
FROM vbak
INNER JOIN kna1 ON vbak~kunnr = kna1~kunnr
WHERE vbak~vbeln IN @s_vbeln
INTO TABLE @DATA(lt_orders).
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = lt_orders ).
lo_alv->display( ).

Prompt to Copilot:

Convert this classic ABAP report to:
1. CDS View with UI annotations
2. RAP Query (read-only)
3. OData V4 service
4. Fiori Elements UI
Target: ABAP Cloud compatible

Copilot generates:

-- 1. CDS View
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Report'
@Metadata.allowExtensions: true
@UI: {
headerInfo: {
typeName: 'Sales Order',
typeNamePlural: 'Sales Orders'
}
}
define view entity ZC_SalesReport
as select from I_SalesOrder as Order
association [0..1] to I_Customer as _Customer
on Order.SoldToParty = _Customer.Customer
{
@UI.lineItem: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
key Order.SalesOrder,
@UI.lineItem: [{ position: 20 }]
Order.CreationDate,
@UI.lineItem: [{ position: 30 }]
Order.SoldToParty,
@UI.lineItem: [{ position: 40 }]
_Customer.CustomerName,
@UI.lineItem: [{ position: 50 }]
@Semantics.amount.currencyCode: 'TransactionCurrency'
Order.TotalNetAmount,
Order.TransactionCurrency,
_Customer
}
-- 2. Service Definition
@EndUserText.label: 'Sales Report Service'
define service ZUI_SalesReport {
expose ZC_SalesReport as SalesOrders;
}
3. Service Binding: OData V4 - UI
4. Publish → Fiori Preview
→ Done! From 50-line report to cloud-ready Fiori app

Use Case 2: Complex EML Queries

Prompt:

" Read Travel with all Bookings where BookingDate is within last 30 days
" Also load Customer data via association
" Sort by BookingDate descending

Copilot:

" Read Travel with recent Bookings and Customer data
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH VALUE #( ( TravelId = lv_travel_id ) )
RESULT DATA(lt_travel)
" Navigate to Bookings
ENTITY Travel BY \_Bookings
ALL FIELDS
WITH VALUE #( ( TravelId = lv_travel_id ) )
RESULT DATA(lt_all_bookings)
" Navigate to Customer
ENTITY Travel BY \_Customer
ALL FIELDS
WITH VALUE #( ( TravelId = lv_travel_id ) )
RESULT DATA(lt_customer).
" Filter Bookings (last 30 days)
DATA(lv_cutoff_date) = cl_abap_context_info=>get_system_date( ) - 30.
DATA(lt_recent_bookings) = FILTER #( lt_all_bookings
WHERE BookingDate >= lv_cutoff_date ).
" Sort by BookingDate descending
SORT lt_recent_bookings BY BookingDate DESCENDING.
" Output
WRITE: / |Travel: { lt_travel[ 1 ]-TravelId }|.
WRITE: / |Customer: { lt_customer[ 1 ]-CustomerName }|.
WRITE: / |Recent Bookings: { lines( lt_recent_bookings ) }|.
LOOP AT lt_recent_bookings INTO DATA(ls_booking).
WRITE: / | - { ls_booking-BookingDate DATE = USER }: | &&
|{ ls_booking-CarrierId } { ls_booking-FlightPrice }|.
ENDLOOP.

Use Case 3: Performance Optimization

Prompt:

Optimize this code for performance:
[Insert slow code]
LOOP AT lt_orders INTO DATA(ls_order).
SELECT SINGLE name1 FROM kna1
WHERE kunnr = ls_order-kunnr
INTO @DATA(lv_name).
ls_order-customer_name = lv_name.
MODIFY lt_orders FROM ls_order.
ENDLOOP.

Copilot:

" Optimized: Single SELECT with FOR ALL ENTRIES
" Avoids N+1 query problem (was: 1 SELECT per order)
" 1. Collect unique customer IDs
DATA(lt_customer_ids) = VALUE kunnr_t(
FOR order IN lt_orders ( order-kunnr ) ).
DELETE ADJACENT DUPLICATES FROM lt_customer_ids.
" 2. Bulk-read customer names
SELECT kunnr, name1
FROM kna1
FOR ALL ENTRIES IN @lt_customer_ids
WHERE kunnr = @lt_customer_ids-table_line
INTO TABLE @DATA(lt_customers).
" 3. Update orders with CORRESPONDING
lt_orders = VALUE #(
FOR order IN lt_orders
LET customer_name = VALUE #( lt_customers[ kunnr = order-kunnr ]-name1 OPTIONAL )
IN ( CORRESPONDING #( order ) customer_name = customer_name )
).
" Performance improvement: O(N) instead of O(N²)
" Example: 1000 orders
" Before: 1000 SELECTs (~10 seconds)
" After: 2 SELECTs (~0.1 seconds)

Copilot Shortcuts

VS Code Shortcuts:
Tab Accept suggestion
Esc Dismiss suggestion
Alt+] Next suggestion
Alt+[ Previous suggestion
Ctrl+Enter Show all suggestions (panel)
Chat:
Ctrl+Shift+I Open chat
/explain Explain selected code
/fix Suggest fix for error
/tests Generate unit tests
/doc Generate documentation
Inline Chat (in editor):
Ctrl+I Inline chat at cursor

Limitations of Copilot

What Copilot CANNOT Do

" Cannot know company-specific logic
" Copilot does NOT know:
" - Your internal number ranges
" - Custom Z-table structures
" - Your company's business rules
" → Must be adjusted manually
" Cannot plan complex architectures
" Copilot can generate code, but NOT:
" - Design microservice architecture
" - Decide: Managed vs Unmanaged RAP
" - Analyze performance bottlenecks (without profiling data)
" Cannot do security audits
" Copilot code may contain:
" - SQL Injection (if not reviewed)
" - Hardcoded Credentials (from training data)
" - Missing Authorization Checks
" → Code Review is MANDATORY!
" Cannot be 100% correct
" Copilot makes mistakes:
" - Syntax errors (rare, but possible)
" - Logic errors (business rules wrong)
" - Suggest deprecated APIs
" → Always test!

Best Practices

" Copilot as assistant, not replacement
" - Use suggestions as starting point
" - Review EVERY generated code
" - Test thoroughly
" Work iteratively
" - Prompt → Generate → Review → Refine → Repeat
" Be security conscious
" - Remove Copilot-generated secrets
" - Check authorization checks
" - Follow OWASP Top 10
" Code Review
" - Treat generated code like external code
" - Pair programming with Copilot
" Learning
" - Copilot shows best practices
" - Understand the generated code (don't copy blindly!)

Cost-Benefit Analysis

Investment

  • License: $10-19/month
  • Learning curve: 1-2 weeks for effective usage
  • Setup: 15 minutes

ROI (Return on Investment)

Time savings (conservative):

TaskWithout CopilotWith CopilotSavings
Write simple method15 min5 min67%
Unit tests30 min10 min67%
Code refactoring60 min20 min67%
Debugging/code explanation45 min15 min67%
Create CDS View20 min8 min60%

Average productivity increase: 30-40%

Break-Even:

Hourly rate: €60
License: €15/month
Savings: 5 hours/month (at 30% productivity)
→ €300/month saved
→ ROI: 2000% (!)

Important Notes / Best Practice

  • Review is mandatory: Every Copilot code must be reviewed
  • Prompts are crucial: Good prompts = good code
  • Use iteratively: Generate → Review → Refine → Repeat
  • Mind security: Don’t commit secrets, check authorization
  • Learning: Understand the code, don’t copy blindly
  • Testing: Test Copilot code the same as your own code
  • Company context: Copilot doesn’t know your internal standards
  • Offline mode: Copilot needs internet connection
  • Data privacy: Company data is sent to GitHub/Microsoft (Business license for Enterprise)
  • Learn shortcuts: Tab, Alt+], Ctrl+I for maximum efficiency
  • Use chat: Not just auto-completion – chat is very powerful
  • VS Code recommended: Better Copilot integration than Eclipse ADT (2025)

Further Resources