AI 写 ABAP 的九类常见错误与约束方法
文章指出,ABAP 版本差异和 SAP 内部对象缺少公开资料,容易让编码助手生成不兼容代码或虚构对象。建议明确系统版本、校验对象存在性,将数据库访问和事务规则写入规则文件,并以 ATC 与 ABAP Unit 把关。
AI 深度解读
AI 写 ABAP,麻烦往往不在代码长得像不像,而在它能否适配你的 SAP 系统,以及那些看起来正规的对象是否真的存在。作者归纳了九类反复出现的错误:有些编译时就会暴露,有些能通过简单测试,却会在空表、业务失败或事务提交时出问题。
这份清单的价值,是把「帮我写 ABAP」变成一套可检查的约束。对接 SAP 的前端、全栈开发者也能从中学到一件事:调用没报错,不代表业务成功;代码能激活,也不代表运行安全。
先告诉 AI 你在哪个 SAP 系统上写代码
作者认为,ABAP 容易让模型失手,主要有两个原因:不同发行版的语法差异大,大量函数模块、BAPI、CDS view 和表又藏在 SAP 系统内部,模型无法靠公开代码确认它们是否存在。
同样一段代码,在 S/4HANA 2023 上能激活,放到 SAP_BASIS 7.50 的 ECC 系统就可能报语法错误。按原文列出的范围,ECC 6.0 的 EHP8 最高到 SAP_BASIS 7.50,不能使用 x += 1.,也不支持 view entity。
版本差异还细到支持包:在 7.40 SP05 至 SP07,不能把 INTO TABLE @DATA(orders) 放在示例查询的 WHERE 后面;原文写法需要 7.40 SP08。兼容 SP05 时,要先声明结果类型和内表,再把 INTO TABLE @orders 放到 WHERE 前面。
因此,开始生成前至少交代三项:语言版本是 Standard ABAP 还是 ABAP for Cloud Development,产品是 S/4HANA 还是 ECC,以及具体 release。无法确定时,作者建议按最低合理版本生成,并禁止混用不同版本的语法。
对象名称也需要单独验证。Z_GET_ORDER_STATUS、I_SomethingView、BAPI_WHATEVER 都可能只是符合命名习惯的猜测,真实函数模块的参数也可能被编造。可以通过 ADT 的 Open ABAP Development Object,或 SAP GUI 的 SE37、SE24、SE11 检查,再用 Cloudification Repository 核实发布状态。ABAP Cloud 只允许使用已发布对象,I_ 前缀不能充当证明。
能激活的代码,为什么仍会读错数据
九类错误里,最需要警惕的是那些不会立刻报错的代码。
FOR ALL ENTRIES 后面的驱动内表为空时,整个 WHERE 条件会被忽略,查询可能读出数据库表的全部记录。下面这段代码解决的就是空表保护:
IF keys IS NOT INITIAL.
SELECT vbeln, erdat FROM vbak
FOR ALL ENTRIES IN @keys
WHERE vbeln = @keys-vbeln
INTO TABLE @DATA(orders).
ENDIF.
要看的是第一行保护条件,而不只是查询条件本身。这段示例仍需满足目标版本的语法要求。此外,FOR ALL ENTRIES 会删除重复结果行,作者提醒应选出全部键字段,避免投影后丢失可区分的记录。
另一类错误来自模型对其他语言的惯性。ABAP 使用 <>,不能写 !=;没有 i++,应写 i = i + 1.,到 7.54 才能用 i += 1.。双引号会开始注释,字符串应使用反引号或单引号。也没有可以直接照搬的 null,应按对象类型使用 IS INITIAL 或 IS NOT BOUND。
RETURN value. 同样有版本门槛:它只在 7.58,也就是 ABAP Cloud 2211 起的函数式方法中存在。更早的版本要先给返回参数赋值,再执行 RETURN。
表表达式则容易让熟悉经典 READ TABLE 的模型判断错误。items[ id = key ] 不会设置 sy-subrc,找不到行会抛出 CX_SY_ITAB_LINE_NOT_FOUND。后面补一个 IF sy-subrc <> 0 接不住这个异常。可以用 line_exists( ) 判断存在性,或在 7.40 SP08+ 使用 VALUE #( ... OPTIONAL ) 返回初始值。例外是 ASSIGN items[ id = key ] TO <item>.,它会设置 sy-subrc,且不会因缺行抛出该异常。
还有类型陷阱:boolc( ) 返回 string,xsdbool( ) 返回长度为 1 的 c 类型。需要与 abap_true、abap_false 比较时,作者推荐后者。
错误处理和事务,不能套用通用模板
ABAP for Cloud Development 也不是传统 ABAP 的随意组合。原文列出的禁用项包括 sy-datum、WRITE、SUBMIT、CALL TRANSACTION、直接读取 VBAK 等 SAP 表,以及未发布的函数模块。
获取日期可以用 cl_abap_context_info=>get_system_date( ),但结果是 UTC 系统日期。用户本地日期需要结合用户时区使用 XCO library,不能默认两者相同。
RAP,即 ABAP RESTful Application Programming Model,则由框架管理逻辑工作单元(LUW)。behavior handler 和 saver 方法里不应自行执行 COMMIT WORK、ROLLBACK WORK、COMMIT ENTITIES 或 ROLLBACK ENTITIES。错误应通过 failed、reported 结构返回。会在内部提交事务的经典 BAPI,也不能直接放进 interaction phase。
调用 BAPI 时,业务错误还可能藏在 RETURN 表里,不能只看异常或 sy-subrc。原文用下面的检查拦住错误提交:
DATA return TYPE STANDARD TABLE OF bapiret2 WITH EMPTY KEY.
" ... call the BAPI, passing return ...
IF line_exists( return[ type = 'E' ] ) OR line_exists( return[ type = 'A' ] ).
" handle the error, do not commit
ENDIF.
要看的是对 E、A 两种类型的检查。独立调用 BAPI 的提交应通过 BAPI_TRANSACTION_COMMIT 处理,也不要在循环中每条记录提交一次;放进 RAP 时,仍须服从框架的事务边界。
我的判断
作者最有用的建议,是把版本、对象真实性、错误通道和事务归属放到审查清单里。它们比「请遵循最佳实践」具体得多,也更容易逐项验证。
但原文没有给出 Cursor、Claude Code、GitHub Copilot 的对照实验、错误率或修复前后数据,也没交代九类错误来自多少项目。「反复出现」是作者的经验归纳,不能据此判断哪个工具更可靠。
规则文件可以减少重复提醒。原文列出的入口包括 Cursor 的 .cursor/rules/*.mdc,Claude Code、Codex 等代理使用的 AGENTS.md,以及从 CLAUDE.md 导入规则的安排;Copilot 则使用 .github/copilot-instructions.md 和按路径生效的指令。具体工具如何加载,应以实际配置为准,原文没有展开差异。
规则负责提醒,检查工具负责拦截。
与每轮粘贴提示相比,短规则更容易维护;但作者关于长指令可靠性更差的说法,同样没有提供量化对照。最终仍应让语法检查、ATC 和 ABAP Unit 在代码进入 transport 前把关。
能拿来做什么
- 在生成 ABAP 前固定填写语言版本、产品和 SAP_BASIS release,并要求模型列出所依赖的 SAP 对象和不确定项,随后到实际系统核实。
- 审查 AI 查询代码时,优先检查
FOR ALL ENTRIES的空表保护、结果去重和表表达式缺行处理,别只验证正常输入。 - 对接 SAP 的前端或 Node 服务,应与后端约定如何把 BAPI
RETURN中的业务错误传回调用方,避免界面把「调用完成」显示成「操作成功」。 - 将九类陷阱压缩成项目规则,并把语法检查、ATC、ABAP Unit 纳入交付流程;涉及 RAP 的变更,额外核对提交动作是否越过框架边界。