《AI时代的程序员修养》前面几篇,从程序、数据结构、执行模型讲到并发。接下来要看一个更贴近日常开发的问题:模块之间怎么说话。这个问题看起来很普通。一个模块要调用另一个模块,不就是写个函数、发个 HTTP 请求、丢条消息,或者查一下数据库吗?但真实系统里,模块通信方式会决定耦合度、事务边界、失败传播、延迟、吞吐、可观测性和后续演进空间。AI 很擅长补出调用代码,却经常默认选择最顺手的通信方式。你不明确告诉它边界,它就可能把本该异步的事写成同步调用,把本该有契约的接口写成随手传 dict,把本该隔离的模块通过共享数据库绑死。通信方式就是系统边界模块通信不是“怎么把数据传过去”这么简单。每一种通信方式都在回答几组问题:调用方需不需要等结果?被调用方失败时,调用方怎么办?两边是不是必须同时在线?数据是否必须在同一个事务里生效?接口变更时,谁会被影响?调用链能不能被观测和追踪?流量突然变大时,压力落在哪一侧?常见通信方式可以先放进一张表里:通信方式典型场景优点主要风险函数调用同进程模块、领域服务、工具函数简单、快速、类型和测试容易控制模块边界容易被写穿,隐式依赖变多HTTP / REST跨服务业务接口、开放 API通用、易调试、生态成熟延迟、超时、版本兼容、错误码设计RPC / gRPC内部高频服务调用、强 schema 场景契约清晰、性能好、代码生成方便调试成本高,服务治理要求更高消息队列异步任务、削峰、解耦、最终一致调用方不必等,能缓冲流量幂等、重复消费、顺序、死信复杂事件流审计、数据同步、多个消费者读取可追溯、易扩展消费者schema 演进、重放、offset 管理复杂共享数据库小系统、历史系统、报表读取实现快,少一层接口强耦合,绕过业务规则,难拆分文件 / 对象存储大文件交换、批量导入导出适合大对象,成本低生命周期、权限、清理、原子性要设计这张表不是让人背,而是提醒自己:每次让 AI 写“调用另一个模块”之前,都应该先选通信方式。选错了,代码越多越麻烦。函数调用:最简单,也最容易把边界写没同一个进程里,函数调用是最自然的通信方式。它快、直观、容易测试,也最容易被 AI 写出来。比如一个订单服务里,AI 可能会生成这样的代码:def create_order(user_id: str, items: list[OrderItem]) - Order: user = user_service.get_user(user_id) price = pricing_service.calculate(items, user.level) inventory_service.reserve(items) order = order_repository.create(user_id=user_id, items=items, price=price) notification_service.send_order_created(user.email, order.id) return order这段代码乍看很清楚,但里面至少混了三类操作:必须在主流程里完成的校验和计算。需要和订单创建保持一致的库存预占。可以异步执行的通知。函数调用的问题不在函数本身,而在边界太容易消失。所有模块都在同一个进程里,调用成本低,AI 就会很自然地把一切串起来。最后一个业务动作变成一条长链,任何一步慢或失败,都拖住整个请求。更稳的拆法,是先区分同步核心路径和异步副作用:def create_order(command