去年我想算清楚一份海外工作的实际到手收入,于是动手写了个德国薪资税计算引擎。起初问题很简单:扣完税和社保,工资还剩多少?但德国模块跑通后,扩展到其他国家几乎成了必然。奥地利、瑞士、英国、爱尔兰、美国、加拿大、澳大利亚——目标是一个统一的跨国薪资对比API。

越往下做,"薪资"这个概念本身越站不住脚。

打开网易新闻 查看精彩图片

60万欧元的幻觉

我最初以为跨国薪资对比主要是数学问题:输入税前工资,套用税率,对比结果。这个假设垮得很快。

德国6万欧元、瑞士6万欧元、美国6万欧元——表面数字相同,经济实质完全不同。德国把大量社会成本嵌入工资代扣;瑞士把医保等主要成本完全放在工资体系之外;美国则把责任拆成联邦税、州税、FICA社保税,再加上雇主主导的福利系统。

API输入层看起来一模一样。语义上,它们代表的是完全不兼容的社会制度。我不再是"对比薪资",而是试图用一套稳定的API契约,暴露互不相容的社会系统。

架构被迫分层

最终架构拆成两层。第一层处理各国具体的工资执行:税档、养老金系统、社保规则、申报类别、发薪周期、扣款逻辑。第二层试图把输出归一化,变成可跨国对比的东西:实际税率、可支配收入、扣掉房租后的估算、购买力信号。

概念上这拆分很干净,工程上却制造了核心张力。内部每个国家都需要高度特定的规则和例外;外部开发者仍期待一套连贯可预测的API表面。难点不是写各国逻辑,而是防止API契约被语义不一致压垮。

最难的不是算税

意外的是,实际税务计算很少是最难的部分。更棘手的是定义输出字段到底该是什么意思。

拿看似简单的"净收入:45000"来说。"净"到底包含什么?强制医保?商业医保?养老金义务?住房?育儿?税后扣款?不同国家对"净收入"的定义边界完全不同,有些国家根本没有对应概念。

我花了大量时间不是写代码,而是写文档——解释每个字段在特定国家的实际含义,以及为什么不能直接跨国比较。API返回的数据越"干净",背后的注释越冗长。

一个未完成的观察

这个项目让我意识到,技术标准化最容易的部分是接口格式,最难的部分是语义对齐。你可以用同样的JSON结构封装任何国家的薪资数据,但封装本身不解决可比性问题。开发者拿到数据后,如果假设德国和美国的"净收入"可以直接对比,就会做出错误决策。

最终这个API更像是一个翻译层,而不是一个计算层——它翻译的是各国社会契约的不同假设,而不是简单的数字换算。