会议室的白板上写着几个字:海外支付项目。墨水是新写的,没有干透。
九点五十八分,人陆续到齐了。刘姐最后一个进来,手里端着一杯水,另一只手拿着投影仪的遥控器。她没有坐下,直接走到幕布旁边,把遥控器对准了天花板上挂着的投影仪。幕布亮起来,蓝底白字,是一份产品需求文档的首页。
文档是英文的。标题是英文,正文是英文,满屏看过去没有一个中文。
刘姐按了一下遥控器,翻到第二页。这一页全是术语。tokenization、webhook、3DS authentication、callback URL、idempotency key。每一个词她都用激光笔在幕布上圈了一下,然后继续往下翻。
「这个项目要对接一家海外支付公司,」她说,声音不大,但在安静的会议室里很清楚,「文档是全英文的,接口规范也在里面。我们需要在三月底之前完成接入。」
会议室里安静了几秒。投影仪的散热风扇嗡嗡地转着,空调出风口吹出一阵冷风,桌面上摊开的笔记本纸页动了一下。没有人立刻接话。
老张先开口了。他靠在椅背上,两只手交叉放在肚子上,看着幕布上的英文文档,眉头拧着。「这文档我看不懂,」他说,「谁懂英文?」
小李举了手。手臂抬起来,手掌张开,像课堂上的学生。「我试试,」他说,「我英文还可以,之前看过一些英文技术文档。」他的声音比平时高了一点,带着一种想要表现又不想表现得太明显的分寸。
刘姐看了小李一眼,点了一下头,没有说行也没有说不行。
陈默坐在会议桌靠窗的角落。他面前的桌面上没有放笔记本,也没有放笔。他靠着椅背坐着,一只手搭在扶手上,视线落在幕布上。他在看那几行术语。
tokenization,他知道这个词的字面意思。但这家公司的tokenization具体指什么,文档上没有直接写。是前端敏感数据替换,还是后端账户映射,还是两者都有。webhook他知道,反向回调。但接口的payload结构、签名算法、重试机制,这些信息藏在后面的页面里,要翻到了才知道。
他把目光从幕布上移开,看了一眼窗外。阳光从落地窗照进来,在会议桌上投下一块明亮的矩形。楼下马路上车不多,行人更少。新年第一个工作日上午,城市还没有完全跑起来。他收回视线,又落回幕布上。
他在心里盘算了一下。这个项目的几个环节:先理解对方的接口规范,然后设计适配层,然后两边联调,然后上线。如果顺的话两个月,如果不顺的话三个月。他对接过五个第三方支付渠道,每一次都有不一样的坑。有的文档写得跟散文一样,读完了不知道从哪开始写。有的接口返回的数据跟文档对不上,要发邮件过去问,一来一回就是两三天。
他想起上一次对接支付渠道的场景。对方的技术支持回邮件特别慢,一个问题问出去,第二天才收到回复。回复里又带着三个新问题。来回折腾了两周,最后发现是文档版本的配置参数写错了。
「陈默,你觉得呢?」刘姐的声音把他拉回来。她看着他,一只手还拿着遥控器,另一只手撑着桌沿。
「我先看一下文档,」他说,「看完给你评估。」
刘姐没有追问。她把遥控器放到桌上,拿起水杯。「文档我发到群里了,大家先看一下,明天我们过方案。散会。」她端着水杯走出会议室,高跟鞋的声音在走廊里响了几下,远了。
椅子被拉开的声音、纸页翻动的声音、人站起来的声音混在一起。老张站起来伸了个懒腰,关节咔咔响了两声。他看了一眼幕布上的文档,又看了看陈默,张了一下嘴又闭上了,拿了自己的杯子走了。小李还坐在位子上,低头在手机上打字,大概在群里说了什么。
陈默最后一个离开会议室。白板上的字还没有干透,墨水在光线下微微反光。他回到工位,打开了邮件。附件躺在收件箱里,PDF格式,文件名是一串英文加日期。
有人站在他工位旁边。他抬头,刘姐端着水杯站在那里。「这个项目你带,有问题吗?」她说。语气很平淡,不是商量也不是命令,就是陈述一件已经定了的事,走个形式问一下。
「没问题。」陈默说。
刘姐点了一下头。「文档发你了,全是英文,你辛苦一下。」她说。那句辛苦是公式化的,但也是真实的。她知道那份文档有多厚。
「好。」陈默说。她走了。高跟鞋的声音在走廊里渐渐远了。
陈默看着屏幕上那个PDF文档。缩略图模式,左边是页码,右边是第一页。他往前翻了几页,看到了一些批注和修改痕迹。这个文档已经被几个人看过了。批注的颜色有红色有蓝色有绿色,至少三个人在上面留过言。有的在问问题,有的在提建议,有一条批注写的是this is unclear。但没有人说我来做。
他关掉文档,打开了IDE。今天还有其他活要干。
小李走过来,手里拿着手机,屏幕上是文档截图。「陈默哥,这个词我查了字典,但放在句子里还是不通。」他把手机递过来,手指指着一段英文。
陈默接过来看了一眼,是idempotency key的解释段落。他用了几句话说明了幂等键的逻辑。小李听完点头,在本子上加了几笔。
本子已经写了五六页,术语和问题混在一起。开头字迹工整,越往后越潦草,有几个单词拼错了又划掉。页边画了圈和箭头,几处打了问号。
小李走后,陈默把注意力放回IDE。先处理了手头另一个需求的代码修改,改了三个文件,提交推送。做完这一轮,他端起杯子喝水,水已经凉了。
他重新打开PDF,从接口列表开始逐行看。对方没有提供时序图,没有业务场景说明,只有一份API列表和字段表。
他在纸上画调用流程图。排到第三个接口的时候,发现两个接口之间存在循环调用的可能。他在那两行之间画了一个双向箭头,打了一个问号。
去年也有一个项目的接口存在循环依赖。开发阶段没人发现,联调时才暴露。两边改了两轮设计,改完又引入新问题。最后产品砍了一个功能才解开。
陈默看着纸上的双向箭头。同一个问题不能走两遍。
下午刘姐过来,站在工位旁边,说对方技术栈是Java,Spring Boot,数据库MySQL。陈默点了一下头。刘姐又说接口走公网,安全策略要单独评估。
「TLS版本有要求吗?」陈默问。
刘姐说记下来,回头问对方确认。她说下周要出技术方案初稿,财务等着附进合同。陈默说好,明天开始写。
刘姐走了。走廊里的日光灯嗡嗡响,隔壁工位有人在打电话。声音不高,但内容一字一字传过来。离下班还有一个小时,有人开始收拾桌面。
陈默继续看文档。在投影仪上模糊不清的小字,在屏幕上一放大就显了出来。有一处用小字号写了一个时间窗口限制,还有一处用斜体标了费用说明。
他新建了一个文本文件,把发现的问题逐条记录。写到第七条的时候,办公区的灯切换到节能模式,光线暗了一挡。他抬头看了一圈,大部分工位已经空了。
下班前去茶水间接水,碰到小李也在。饮水机烧着水,蒸汽往上冒。小李听到动静回头,说文档他晚上回去接着看。
「接口文档里时间戳格式写的是Unix毫秒,示例里给的是秒级数据,」陈默说,「你注意一下这个差异。」
小李愣了一下,翻开本子记下来。合上本子的时候,夹在里面的几页纸滑出来,散落在地上。他弯腰去捡,脸有一点红。
陈默端着水杯回到工位。他把文档关掉,关了显示器,收拾东西。鼠标垫旁边压着那张画了流程图的纸,双向箭头还在,问号还在。
他把纸折起来放进口袋。走到电梯口,小李也从茶水间那边过来,背着双肩包,低头看手机。屏幕光照在他脸上。两人前后进了电梯。
到了一楼,各往左右走了。陈默走出大楼大门,冷风迎面过来。路灯亮了,行人不多。他掏出手机看了一眼。
是刘姐发来的会议邀请。后天下午,受邀人列表包括技术部四个人和产品部一个人。他没有点接受,锁屏放回口袋,往地铁站走了。
地铁上人不多。他找了个靠门的位置站着,从口袋里掏出那张纸,展开又看了一遍。双向箭头画在两个接口之间,问题是文档里没有说清楚调用顺序。
去年的项目也是这样,两头都等着对方先表态,到了联调节点才发现。两边沟通了三轮才达成一致,费用也超了。
陈默把纸折好放回口袋。地铁到站,他下了车。冷风从站台口灌进来,他把外套拉链拉到顶,混在人群里出了站。
第二天他提前十分钟到公司。走廊里碰到老张在茶水间倒咖啡。老张看见他,端着杯子走过来。咖啡冒着热气。
「那个英文文档你看了吗?」老张问。语气不是试探,就是想知道。陈默说看了一部分。
「你觉得搞得定吗?」老张又问。
「搞得定,但时间紧。」陈默说。
老张点了一下头,喝了一口咖啡,没有继续问。他端着杯子走回工位,走了几步又回头说:「有地方要帮忙的说话。」
陈默坐到工位前。显示器亮起来,桌面上那份PDF还在。他没有急着打开,先看了一眼日历。春节在二月中旬,三月底接入,节后只有六周。去掉需求讨论和方案评审,编码时间不到四周。
手机震了一下。刘姐发来消息,说对方技术团队后天下午两点可以线上沟通。陈默回了一个好字。
他打开IDE。昨天改到一半的代码还在那里,光标停在最后一行。他先把手上这个需求的代码改完,推到测试环境。做完这件事,才重新打开了那份PDF。
走廊尽头贴着新年装饰,红色剪纸还没有揭掉。前台摆了一盆金橘,果实挂满枝头,旁边放着一叠开工利是红包,已经被人领了大半。
陈默第三次审视接口文档的时候,发现了一个问题:对方文档里回调地址的示例IP和内网IP段重叠。他截了图,在图上圈出冲突的地址范围,存到一个待讨论的文件夹里。
十点半的时候,产品部那个姓王的同事发来一条即时消息,问能不能拉个群,把技术评审的时间定下来。陈默看了一下时间,回了两个字:可以。
陈默端着水杯从茶水间走回工位。走廊尽头有人在打电话,声音隔着几排隔板传过来,听不清内容,只有低沉的嗡嗡声在空气里回荡。
他绕过转角的发财树,走向自己的位置。枯黄的叶片落了几片在花盆边缘,积了一层薄灰,看来有一阵子没人打理了。塑料花盆的边缘缺了一小块,露出里面褐色的土。
过道两边的工位上,有人盯着屏幕,有人趴在桌上休息。键盘声零散地响着,夹着鼠标点击的脆响。一个女同事端着马克杯从对面走来,侧身让过他,没有抬头,也没有说话。
桌椅之间的走道不宽,他侧着身子过了两个拐角,回到自己的位置。
工位上方的日光灯排成两列,有几根发了黑,光线比其他地方暗一些。空调的出风口在头顶上方,吹出来的风带着轻微的嗡嗡声,吹得桌上的文件一角微微掀动。
把杯子放在显示器右侧。椅子坐下去的时候,气压杆发出轻微的吱呀声。坐垫往下沉了一点,然后停住。
屏幕亮着,IDE停在他上午离开前的状态。文件树展开到src/main/java下面的某个目录,代码区的光标在一行末尾闪烁,一闪一闪,节奏稳定,等着他继续。
他能听到旁边工位的人敲键盘的节奏。有人在快速打字,有人在停顿时按删除键。这些声音混在一起,成了背景的一部分。
他没有急着动键盘。先移动鼠标,点开了邮箱。
收件箱里躺着一封未读。赵磊发来的,十四分钟前。标题上写着新项目参考文档。
点开。正文只有一行字:这是对方提供的接口文档,你先大概看一下,回头聊。句尾没有标点。
附件是一个PDF文件,文件名是一串英文字母加下划线加数字的组合,没有中文。他双击打开。
PDF阅读器打开了,界面是默认的浅灰色背景。工具栏上的按钮很小,缩放到适应宽度的视图。
加载了三四秒。文档在屏幕上展开,右侧是正文内容,左侧一列缩略图从第一页往下排,排到看不见的地方。他按住滚动条往下拖,缩略图刷刷地往下跑,连续拖了好几秒才到底。
右下角显示:共124页。
他盯着那个数字看了两秒。没什么表情。
开始翻页。第一页是封面标题和版本号,标题用了加粗的大字号,占了半页。第二页是修订历史,空白,只有一行初版字样,日期写的是上周。从第三页开始进入正文。
概览。系统架构图。接口列表。数据模型定义。分了几个章节,每章下面还有小节。他看到章节目录就有十几行,密密麻麻的缩进关系。
每个章节的标题格式统一,一级标题用大号加粗,二级标题缩进一个字符,三级标题再缩进一个字符。他一页一页地翻过去,像是在翻一本没有插图的技术手册。
每一页都是技术规格和API说明。接口名称、请求方法、URL路径、请求参数、响应格式。表格占了页面的大半面积,单元格里填着英文单词和数字,一行挨着一行,排得整整齐齐。字段说明那一列最长,有的单元格里写了两三行英文解释,单词之间用逗号隔开。
字段的命名规则看得出来是某个内部规范,驼峰式,缩写有固定的模式。写文档的人很细致,每个字段的取值范围和默认值都列得清清楚楚。
他又翻了几页。页面之间的差异很小,版式都一样,换一页跟没换似的。参数表格里的字段名密密麻麻排下来,返回值类型写在第三列,错误码枚举列了一长串,翻了三页还没列完。
手指在触摸板上停下来。他靠向椅背,抬头看了一秒天花板。日光灯管的白光有些晃眼,他眯了一下眼睛。
他停顿了一下,食指在触摸板上悬空了一秒。然后关掉了文档。
坐回来,调整了一下坐姿,移动鼠标到窗口右上角,把文档关了。邮件列表恢复原样,那封未读变成了已读。
还有其他活要干。这个文档,晚上带回去再细看。
他端起杯子喝了一口水。水已经凉了,不锈钢杯壁的温度传到指尖上,有一点冰。他把杯子握在手里转了半圈,放回桌上。杯底碰到桌面,发出轻轻的磕碰声。
不锈钢杯壁上凝结了几颗细小的水珠,在下午的光线里反着微光。他的指尖在杯壁上划过,留下一条干净的痕迹。
打开任务管理面板。今天的工作列在上面:三个bug,都是P2优先级,标注了今日待修复。
他把三个任务逐条看了一遍。第一个和第二个问题本质不复杂,他心里已经有了改动思路。第三个需要多看几行代码,确认影响范围。
第一个是条件判断的边界问题。if语句的比较操作符少写了一个等号,边界值会落进else分支,导致错误的返回值。改动很小,加一个等号就行,测试覆盖也不难。
第二个是数据格式不匹配。前端传的create_time是字符串格式,后端的DTO把这个字段定义成Integer类型,反序列化直接报错。加一个注解做自动转换就行,Jackson框架支持这种转换。
第三个复杂一些。一段业务逻辑的联动判断,涉及三个模块的数据流转。改一处不够,需要先看完整个调用链路再动手,否则容易漏掉关联的逻辑。
他看完任务描述,正准备切到IDE,余光扫到左边有人影靠过来。
他活动了一下手指关节,发出一连串细小的响声。
他偏过头。
小李站在隔板边上,手里端着一杯咖啡。白气从杯口冒出来,在空气里散得很快。杯壁上贴着茶水间的标签,标了价格:美式,15元。
「陈哥,这个项目你真的接啊。」小李说。
「不然呢。」
「听说文档一百多页。」
「看过了。」
小李沉默了两秒,低头喝了一口咖啡。
「也是。」他说完,举起咖啡杯朝陈默的方向举了一下,没再多说,转身走了。
脚步声往走廊深处去,经过两个工位后拐了个弯,被隔板挡住,听不到了。
陈默把视线转回屏幕。
手指放上键盘的时候,没有犹豫。光标移动到位,修改,保存。动作连贯。
打开IDE里的第一个文件。代码在编辑器里展开,他扫了一遍,找到出问题的if语句。在分支判断的位置停了一秒,确认自己的判断没问题。count > 0,应该写成count >= 0。
加等号。删掉行尾多余的空格。光标移到下一行,检查了else分支的返回值,确认改了之后不会反向影响已有逻辑。保存。
跑单元测试。测试条走了一小会儿,进度条从左到右慢慢填满,全部变成绿色。通过。
提PR。浏览器自动打开到新建PR的页面,他填写了标题和描述,检查了分支名,确认无误,点击创建。页面跳转到PR详情页,状态显示open。
网页上的PR模板有默认的检查清单,他勾了对应的几项,提交。
切到第二个文件。
第二个文件在接口层。前端请求体里的createTime字段是字符串格式,映射到后端的Integer字段时报类型转换异常。他找到DTO定义,在createTime字段上加了一个注解,指定了字符串到数字的自动转换策略。
改完没有马上提交。他把同一个DTO类里其他可能出问题的字段也扫了一遍,确认没有类似的类型不一致问题。只有这一个字段。
DTO的定义在另外一个包里,他顺着import语句跳转过去。字段上已经有了几个其他注解,他在它们下面加了一行。IDE的自动补全提示了选项,他选了第二个。
保存。跑测试。全部通过。
提PR。
第三个文件。他打开之后没有马上动手,先把相关的三个模块都打开,平铺在编辑器的标签栏里。从上到下读了一遍数据流,从控制器层到服务层再到数据访问层,在脑子里画了一张简略的链路图。
读代码的时候他的目光在屏幕上从左到右移动,偶尔停下来,在某个方法调用上多停留两秒。读完一个文件,切到下一个,再切回来。脑子里那条链路的形状越来越清晰。
数据从用户请求进来,经过三层处理,最终落到数据库查询。改动点在中间那一层,但会影响上下游的判断逻辑。他盯着代码看了几分钟,确认了每一处可能受影响的关联逻辑。
确认了改动范围之后开始写代码。修改了第一个文件中的判断逻辑,调整了条件顺序。在第二个文件里新增了一个校验方法,处理边界情况。第三个文件只改了一行引用,把旧方法名换成新的。
改完,跑测试。
第一次有一个用例没通过。他回头看了一下,发现少改了一个关联的枚举判断,那个枚举在另一个包里,不在他刚才看的三个文件里。
失败的用例提示信息很明确:期望值true,实际值false。他看了一眼堆栈,顺着行号找到了那个枚举。问题不大,枚举定义里少了一个对应的条目。加上去就好。
找到枚举文件,修正。重新跑测试。全部通过。
提PR。关掉任务面板。
他看了一眼右下角的时间。十七点四十一分。
下午的光线已经偏西了。百叶窗把阳光切成一道道平行的线条,斜铺在桌面上,照亮了键盘右边的区域,照亮了杯子底部干涸的水渍。光线里浮着细小的灰尘颗粒,在空气里缓缓飘动,像悬浮的碎金。桌面上的温度比中午低了一些。
角落里的盆栽被空调风吹得叶片轻微晃动。窗外的天色还没有暗下去,但阳光的颜色已经从白变黄,从黄变橙。时间在一点一点地移动。
他靠在椅背上,转了转脖子。颈椎发出一声轻响。
桌上没有别的急事了。邮件都回复了,代码都提交了,明天的晨会材料也大概浏览过了。
那个124页的文档还躺在下载文件夹里。他想了想,没有打开。现在看也看不完,而且忙了一下午,脑子已经转不动了,看了也记不住什么。等晚上回去,洗过澡,安静了再看。
他坐在椅子上没有立刻站起来。屏幕已经黑了,倒影里能看到身后的过道和过道尽头的那扇窗。窗外的天空是一片均匀的灰蓝色。
他动手收拾东西。把充电线从插座上拔下来,手指捏着线头绕了几圈,收成整齐的一束,塞进背包侧面的小袋里。笔记本电脑合上,电源灯在合盖的瞬间熄灭,他把电脑放进主隔层,卡好位置。
电脑放进去之后,背包的重量明显增加了一些。他掂了一下,拉上主隔层的拉链。
杯子里剩的水倒进角落那盆绿植里,土面很快渗下去,留下一小片深色的湿痕,沿着土壤的裂缝往四周扩散。他把杯子也装进背包,杯子碰到电脑外壳,发出咚的一声闷响。
站起来。椅子自动往后滑了半寸,轮子在地板上滚出轻微的声响。
他低头看了看自己的工位。显示器已经黑了,键盘上落了几粒灰,桌面的鼠标垫边缘有些卷翘。那盆绿植的叶片有些干尖,刚才倒进去的水应该能撑几天。
他把椅子推回桌下,拉上背包拉链。拉链头滑到尽头,咔嗒一声合上。
转身往电梯间走。路过茶水间时,里面有人在说话,声音传出来又被走廊的墙壁吞掉,听不出是谁。他没有停步。
走到半路,脑子里闪过那个数字。124页。他的脚步没有放慢。
电梯间已经站了五六个人。他走到队尾站好,拿出手机看了一眼。没有新消息。通知栏干干净净。
电梯到了。门打开,里面出来两个人,一男一女,手里都拿着手机。他跟在前面的同事后面走进去,转身,伸手按下1楼的按钮。按钮亮起橘色的光。
门关上。楼层数字开始往下跳,从17跳到16,再跳到15,一个接一个。
今天的工作结束了。文档的事,晚上再说。
客厅的灯光白得发冷。九点刚过,女儿在茶几上铺开画纸,彩笔在盒子里滚来滚去。陈默把笔记本电脑放在沙发扶手上,膝上垫了一本翻开的书。
PDF里是一份技术白皮书。光标停在第三页,一个词卡在那里,tokenization。
点开词典,查了一遍。分词,标记化。关掉窗口,继续往下读。
翻过两页,这个词又出现了。陈默盯着屏幕,看了一会儿,再查。
「爸爸。」
女儿站到沙发边上,举着一张画纸。纸上画了三个人,涂得花花绿绿。
「这个是妈妈,这个是我。」她指着中间那个火柴人。「这个是你。」
陈默接过画纸,端详了一下。「画得真好。」
「你在干嘛呀?」她踮起脚,往屏幕上看。
「爸爸在工作。」
「工作不是白天做完了吗?」
「还有一点要收尾。爸爸在看一个文档。」
「什么文档?」
「技术文档。教爸爸怎么做新项目。」
「很难吗?」
「有点难。」
「比我的画画课还难吗?」
「差不多。」
女儿歪着头,看了看屏幕,上面全是英文。
「这些字你都认识吗?」
「有些认识,有些不认识。」
「不认识怎么办?」
「查字典。」
「那好麻烦啊。」
「是有点麻烦。」
她想了想。「那你查完告诉我,我帮你记住。」
「tokenization是什么呀?」女儿指着屏幕上的一行字。
「就是把文字切成小块,让电脑能看懂。」
「就像切蛋糕一样吗?」
「差不多。」
「那我会切蛋糕。」
「WEBHOOK是什么意思?」女儿指着屏幕上另一个词。
「就是一个通知。电脑做完事情之后告诉另一个电脑。」
「就像你下班了告诉我一样吗?」
「差不多。」
「那你什么时候下班?」
「快了。」
「新项目做什么的?」女儿又问。
「做支付。就是网上买东西时付钱的东西。」
「像妈妈在淘宝上买东西那样吗?」
「对。」
「那很重要哦。」
「是挺重要的。」
「网上买东西为什么要付钱啊?」女儿继续问。
「因为东西不是免费的,要付钱才能拿走。」
「那电脑怎么知道谁付钱?」
「爸爸要做的就是让电脑知道。」
「好难啊。」
「是挺难的。」
「那你好好学。」她拍拍陈默的肩膀。
女儿指着一张画纸上的蓝色块。「这是天空。」她又指指绿色块。「这是草地。」「中间这个人是我,我在草地上跳舞。」她看着陈默。「你看到了吗?」
「看到了。」
「那你笑一下。」
陈默扯了扯嘴角。
「你笑了,好看。」女儿满意了,趴回茶几上继续画画。画笔在纸上摩擦,沙沙的声音一下接一下。
妻子从厨房走出来,看到他们挤在沙发上。
「又在加班?」
「不是加班,看点东西。」
「什么东西?」
「新项目的文档。」
「你们公司白天不给时间看,非要晚上回来啃。」
「项目刚启动,东西太多。」
「你一个人看得完吗?」
「看一点是一点。」
「这种项目以前做过吗?」
「没做过。」
「学得怎么样?」
「还行。」
「我看你挺吃力的。」妻子看了看屏幕。
「技术栈不一样,要从头学。」
「你得看到几点?」
「不知道。看完这部分。」
妻子端起他的杯子,晃了晃。「茶都凉了。」
她转身去续水。热水倒进杯子的声音从厨房传过来,闷闷的,被笔记本电脑风扇的嗡嗡声压过去。
水续回来,放在茶几上。妻子没有马上走。
「女儿今天在幼儿园画了一幅画,说要送给你。」
「我看到了。」
「她画了一个小时。说爸爸最近太累了,画个开心的给你。」
陈默看了看那张画纸。三个人,彩色,歪歪扭扭的笑脸。
「她知道你最近忙。」妻子说。
「周末带她去吧。」
「去公园?」
「嗯。放风筝。」
「你别光说,到时候又看电脑。」
「不会。」
妻子转身走进厨房。水龙头哗哗响了一会儿,然后停了。拖鞋的声音慢慢远去。
「你这个项目要做多久?」妻子从厨房走出来。
「几个月吧。」
「那这几个月都要这么晚?」
「开头会累一点。」
「后面呢?」
「后面就好了。」
「你每次都这么说。」妻子说完,在沙发上坐了一会儿。
接下来这一页,翻过去又翻回来。三维安全认证流程,3DS。发卡行,收单行。
第一遍没看懂。
往后翻几页,回头再看。发卡行验证持卡人身份,收单行等待授权,ARes,PARes,这些缩写从脑子里滑过去,一个都没抓住。
第三遍,放慢速度,一行一行地读。
拼出一个轮廓。发卡行发起认证,持卡人输入密码,收单行收到确认。流程不算复杂,但每个角色之间的消息交互必须精确。
重新看3DS那一段。发卡行和收单行之间的通信需要加密,加密需要证书。证书从哪里来?文档说由CA机构颁发。这个陈默知道,但具体怎么对接,文档没有展开。先跳过。
「还在看那个三维什么?」妻子走出来倒水,路过沙发时停了一下。
「3DS。」
「看得懂吗?」
「勉强。」
「那你看吧,我不吵你。」
妻子端着水杯走回厨房。
翻到下一页,讲回调机制。服务器收到通知之后怎么处理,状态怎么流转。流程图上的箭头从A到B,从B到C,失败就回到A。在脑子里走了一遍,发现不对,重新走。三遍之后才大致理顺。
晚上十点,挂钟的秒针嗒嗒地走。女儿的画笔掉在地上。
「妈妈,我困了。」
妻子带她去洗漱。卫生间的水声和女儿嘟囔的说话声混在一起。
「妈妈,爸爸还在工作吗?」
「嗯。」
「他什么时候睡啊?」
「等他看完。」
「那他明天还送我去幼儿园吗?」
「送。」
「说话算数?」
「算数。」
「他答应了带我去公园。」
「妈妈知道。」
女儿换了睡衣走出来,揉着眼睛。
「爸爸晚安。」
陈默抬起头,朝她摆了摆手。「晚安。」
妻子牵着女儿走进卧室。门虚掩着,暖黄色的光从门缝里漏出来。
过了几分钟,卧室里传来女儿的声音。「妈妈,爸爸还在看电脑吗?」
「嗯。」
「你跟他说晚安。」
「你自己说。」
「爸爸晚安。」声音隔着门,闷闷的。
陈默抬头。「晚安。」
客厅安静下来。电脑风扇的声音在黑暗中变得清晰。墙上挂钟的秒针在走,嗒,嗒,嗒。
陈默把靠垫换了个方向,调整坐姿。沙发坐久了腰不太舒服。
妻子走出来,手里端了另一杯水。
「女儿问我,明天你能不能送她。」
「能。」
「你明天几点起来?」
「照常。」
「那你几点睡?」
「看完这个。」
妻子站了一会儿。「你看得完吗?」
「看不完也得看。」
「那你自己掌握。」她转身走了两步,又停住。「咖啡在柜子里,别喝太多。」
「知道。」
「要不你先睡,明天再看。」妻子站在卧室门口。
「睡不着,脑子里全是这些东西。」
「那你看吧。别太晚。」
「知道。」
妻子从卧室走出来倒水。客厅的灯还亮着。
「还没看完?」
「快了。」
「你明天要喝咖啡吧?」
「嗯。」
「那我明天早一点起来煮。」
「不用,我自己来。」
「你多睡一会儿。」
陈默没再说话。妻子端着水杯站在那儿,看了看他,转身回卧室。
拖鞋在地板上啪嗒啪嗒响了几声,然后安静下来。
webhook。这个词记住了。回调,事件通知。后面几页这个词又出现,不用查。
但tokenization又冒出来。
陈默苦笑,第三次在搜索框里打进这个词。分词,标记化。写在一张便签纸上,贴在显示器边框上。
「以后再看还忘的话,就不看了。」自己对自己说了一句。
又翻回前面,看tokenization那部分。文档说分词是自然语言处理的第一步。陈默知道自然语言处理是什么,但不知道为什么支付系统要用到分词。翻到页脚看注释,注释说为了处理地址信息。明白了,但这点东西花了四十分钟。
茶杯里的水升腾起最后一丝热气,然后消失。客厅的温度不冷,但久坐之后手脚开始发凉。陈默搓了搓手指,继续翻页。
凌晨十一点,翻到文档第五节。架构设计。四个模块,接入层、处理层、转换层、分发层。每个模块下面又拆成若干组件。
读了两遍,在脑子里画图。箭头画到一半,发现逻辑接不上,翻回去重看。
模块之间画着虚线。虚线代表异步消息。文档说这样设计是为了解耦。陈默理解解耦的意思,但不明白为什么这里要用异步。翻到附录,附录没有说明。先记下,以后再说。
模块之间的依赖关系画了三条线。陈默在脑子里推演数据流向。从接入层进来,经过处理层,转到转换层,最后到分发层。每一层只跟相邻层通信。文档说这样设计是为了隔离变化。听起来有道理,实际写起来怎么组织代码,文档没讲。
又翻了一页。讲的是错误处理。超时、重试、幂等。这几个概念都不陌生,但文档里列了十几种异常场景,每种的处理方式都不一样。陈默拿笔在纸上记了几条关键的。
手机震了一下。妻子发的消息:还不睡?
看了一眼,没回。
夜更静了。窗外路灯的光照进来,在地板上画出一个长方形的亮块。风扇的嗡嗡声持续不断。
凌晨十二点,茶杯里的水冷透了。喝了一口,冰凉的液体顺着喉咙滑下去。
没有起身续水。光标在屏幕上闪动,白色方块把周围的黑暗衬得更深。
眼睛盯着屏幕太久了,视线有些模糊。陈默闭了一会儿眼。黑暗里还能看到屏幕的残影,一片浅绿色的方块。再睁开,世界重新变清晰。
颈椎比刚才更僵了。低头的时间太长,脖子后面像卡了一块铁。陈默用手掌按了按后颈,肌肉硬邦邦的。
凌晨一点,看完第一章。
四十二页,读了四个小时。
陈默靠在椅背上。眼睛干涩,进沙子的感觉。眨一下都觉得涩。抬手揉眼眶,能感觉到眼球的温度。颈椎僵硬,头往左转,脖子发出咔的一声。
合上电脑,放在茶几上。
「这种活,什么时候能交给机器。」
没有答案。
陈默站起来,走向卫生间。拧开水龙头,冷水冲出来,泼在脸上,顺着下巴滴到水池里。
镜子里的人眼眶发暗,眉心一道竖纹。按了按那道纹,按不掉。
客厅的灯还亮着。走过去关掉,站在黑暗里听了一会儿。电脑风扇慢慢停下来。
卧室门缝里的光也灭了。
黑暗中站了一会儿。凌晨的城市很安静。远处高架上偶尔有车经过,声音被风撕碎。
笔记本电脑的风扇声,屏幕光在黑暗中的白色,茶杯里从热变凉的水,画笔在纸上的摩擦声,妻子走路的脚步声,热水倒进杯子的声音。这些细节在脑子里过了一圈,慢慢沉下去。
转身回卧室,推开门。
妻子翻了个身。「看完了?」
「看完了。」
「快睡吧。」
「嗯。」
「你明天早上想吃什么?」
「随便。」
「那我煮粥。」
「好。」
「咸的?」
「嗯。」
「你明天几点起?」
「七点。」
「那我给你设闹钟。」
「好。」
躺下来,闭上眼睛。那些词还在脑子里转。tokenization。3DS。ARes。PARes。
不知道什么时候睡着的。