首页 > 其他分享 >开发人员,千万不要去碰那该死的业务参数,无论什么时候!

开发人员,千万不要去碰那该死的业务参数,无论什么时候!

时间:2024-11-11 20:07:53浏览次数:4  
标签:这个 该死 开发人员 流程 业务 参数 日利率 SQL 数据

你好呀,我是歪歪。

前几天发了一个牢骚:

本来只是单纯的吐槽一下,但是好多人对其中的细节比较感兴趣。

大家都是搞技术的嘛,对于“踩 BUG”这种喜闻乐见的事情,有兴趣是很正常的。

其实我这个 BUG,其实严格意义上不能叫做 BUG,因为和程序无关,甚至和技术的关系都不算大。从标题上你也能猜出来,是和一个业务参数相关。

但是在这个过程中,因为我是整个事件全程的亲历者,所以现在回看这个事情,我还是有一些思考在里面的。

我觉得这是一个程序员会遇到的“典型事件”。

那就用这篇文章一起复盘一下吧。

背景

要说明这个问题的背景,甚至不需要一个具体的业务场景,只需要围绕着以下这个非常常见的利息计算公式,就可以说明问题的起因:

利息=计息金额*日利率。
日利率=年利率/360

由于日利率的计算,涉及到除法,在对应需求第一次开发时,业务的要求是日利率保存 7 位小数。

在程序中,年利率和日利率是两个字段分别保存的,日利率在初始化的时候就算好落库了,后续程序直接取这个算好的日利率就行了。

系统上线,相安无事。

跑了一段时间后,业务又提来一个需求:当前的精度不够,需要调整到 11 为小数。

你不用好奇歪师傅这边到底是什么业务场景,反正我去看了业务数据,需求是合理的,那就把需求接过来干就行了。

保存 7 位小数和 11 位小数,大家都是搞开发的,肯定也知道这个就是一个小改动,很快就能搞定。

事实也是如此,虽然之前的需求对应的代码不是我写的,但是我看过代码,清楚的知道改动点在哪,所以很快就开发完成。

前面说了,这个需求之前在线上按照 7 位小数跑了一段时间,所以存在一些存量配置。

针对这些存量数据,在需求评审会议上的时候,我提了一句:存量配置怎么处理呢?

业务答复:这次需求上线的时候,你按照 11 位小数重新算好,然后写 SQL 更新一下就行。

我心里一盘算:计算公式明确,年利率我也有,算一把,没啥问题。

就答应了。

然后,不出意外的出意外了。

假设年利率是 2.5%,除以 360 之后,保留 11 位小数,应该是 0.00006944444。

而我不知道当时为什么手抖了,在 SQL 里面写成了 0.00069444444。

我给你对比一下:

0.00006944444
0.00069444444

相当于我写出来的日利率被扩大了十倍。

然后再回头看看这个公式:

利息=计息金额*日利率

日利率被扩大十倍,那么对应的计提金额也会被扩大十倍。

这就是问题的背景。

一个单纯的人为失误,和程序没有任何关系,所以严格意义上不属于程序 BUG。

但是这个问题确实是足够低级。

为什么没被发现?

那么这个错误的 SQL 是怎么通过代码评审、测试验证这两道关卡被带到生产环节的呢?

首先,这一次提交的代码,压根就没有评审环节。

我有代码提交权限,也有代码审核权限。所以我自己提交,自己就审核通过了。

其实这个需求应该是组里面另外一个小伙伴来做,但是当时他被调到其他组了。

他还在我们组的时候,我们的合作模式是他提交代码,我进行审核。

如果有这个环节,我觉得我有 50% 的几率发现问题。

为什么是 50% 呢?

因为这取决于我审核代码时是否有正在处理其他的事情,如果有其他事情处理,我可能会形式主义的看上几眼。如果没有其他事情,而这次提交的代码量又不大的话,我基本上都会认真的过一下提交的内容。

通过代码评审之后,接下来就应该是测试环节。

测试主要关注的是精度从 7 位变成 11 位之后,最终计算出来的利息是否符合预期。

他测试时是走了整个业务的全流程。

在“全流程”中,这个 11 位精度的日利率,是在页面配置年利率的时候通过程序自动计算出来的,不会错的。

而他在验证 SQL 语句的时候,测试环境又没有生产环境的配置,所以他拿着我提供的 SQL,只能保证写的语法没问题,能正常执行,并不能确保里面数据的正确性。

而我也记得很清楚,我当时给他说过:你执行一下 SQL 不报错就行,值的正确性,我来保证。

而且戏剧性的是,测试同事很仔细的去看了值,他去数了确实是 11 位小数。但是可惜,站在他的视角,他发现不了值被扩大了十倍。

所以,测试环节也没有发现这个问题:

0.00006944444
0.00069444444

就带着上生产了。

一个问题正常来说不应该被带上生产,但是我们确实不能保证测试环节一定能把所有问题都测出来,所以新项目、新迭代的生产验证也是非常有必要的。

这个我们也做了。

按理来说,生产上的数据已经是错误的了,而且是一个“利息金额扩大十倍”的明显的错误,如果主动去做了数据验证,应该能被发现才对。

那为什么做了生产验证,却没有发现问题呢?

因为当时存量配置有三条,我提供了 3 个 SQL,其中有一个是算对了的。

每一条存量配置都对应着大量的利息数据,而算对了的这个对应的数据更多,在比例上超过 60%。

我进行生产验证的时候,在大量的利息计提数据中随机抽选了两条,选中的这两条,恰好都是正确的 SQL 对应的数据。

所以我发现符合预期,得出了生产验证通过的结论。

站在这个节点,回顾整个事件,这个时候应该是最有可能发现问题的时候。

但是没发现。

根本原因是验证方案不严谨,玄学原因是运气不站在我这边。

怎么暴露的?

你想想,这种业务参数配置错误的问题你能通过什么监控规则监控到吗?

其实很难的。

我们一般来说做技术层面的监控,都是监控程序是否按照预期正常运行。比如在计算的过程中出现异常,那我们是可以监控到的。

但是在这种只是参与计算的值不对,但是能正常计算出一个值的情况,并不会报错。

这种问题通过技术手段很难监控到。如果硬要去做监控,肯定是能做的,比如从异常浮动的维度、横向数据对比的维度,但是配套的开发成本又上去了。

我是怎么发现这个问题的呢?

也是纯粹的运气。

是一个周五的晚上,我做另外的一个和本问题毫无关系的场景下的数据验证的时候,偶然间看到了一笔数据的金额和前几天比,明显大了很多。

这是不符合业务规律的。

然后进一步跟踪,最终定位到了前面的问题 SQL。这个时候距离这个 SQL 上线,已经过去了三天,已经产生了一批错误数据了。

如果我没有偶然间看到这个问题数据,那么这个问题会在什么环节暴露呢?

就是在业务使用这个数据做核对的时候。

那个时候整个问题的性质就变了。不仅是处理时间来不来得及的问题了,而是这个问题是由“开发自主发现”还是由“外部反馈发现”这两个完全不同的性质了。

一般来说,不管是什么问题,先抛开严重程度,只要是开发自主发现的,都能一定程度上让事情变得不那么难堪。

所以我们才一度强调“可监控”的重要性。

随后,我联系了业务,反馈了这个情况。他表示在他下次使用这批数据之前,把数据修复好就行。大概一个月后,他会用到这批数据。

这样,我有接近一个月的时间来处理这个问题,防止问题扩大化。

时间非常充足,站在这个角度,我运气还不错的。

问题已经暴露出来了,随后就是制定针对这批错误数据的修复方案了。

修复方案就和业务场景相关了,属于多个业务场景叠加在一起,所以修复方案其实是比较复杂的,涉及到“修数”和“补数”,没有展开描述的必要了。

只是想简单提一句,这个修复方案是我利用周末的时间想出来的,很多细节问题我都需要考虑到,甚至在心里写了一遍伪代码。

确实是浪费了周末的时间,但是这是为自己的错误买单,半点不怨别人,就是活该。

而对于参与后续方案讨论的同事来说,在这件事情上付出的时间,才是属于无妄之灾。

这就是整个事情的过程,一个小数点引发的血案。

再回首

现在整个事情的全貌都在你眼前了,你得到了什么经验教训?

因为手抖了,写错了一位小数,这确实是直接原因,所以是想着下次再处理这种数据的时候,更加小心一点吗?

我觉得不是这样的。

我得到的经验教训就是我的标题:开发人员,千万不要去碰那该死的业务参数!

如果在最开始需求评审会,我们讨论到存量数据的时候。

业务说:这次需求上线的时候,你按照 11 位小数重新算好,然后写 SQL 更新一下就行。

我说:不行,这个属于是业务参数,我不能去动。上线完成后,就具备这个功能了,你可以通过页面配置去修改。

我知道他们修改业务参数的流程,很长很复杂。

首先业务需要发起一个参数变更的 OA 流程,然后走到他的部门负责人审批。

业务部门负责人审批完成后,会到具体负责业务参数配置的人员手里,还需要该人员对应的部门负责人审核。

审核完成后有权限的人员才会去修改这个业务参数,而这个参数的修改,在对应的系统功能上还有两级甚至三级审核。

整个完成之后发起 OA 的人员还需要进行变更确认,看看页面上是否是自己想要的配置。

这一套流程走下来,你觉得还会出错吗?

很难出错了。

你可以批判这个流程过于臃肿,但是你最终总是会认识到,这个流程其实是在保护打工人。

我知道他流程比较复杂,而我写个 SQL 几乎是没有成本的,但是这是在 SQL 正确的前提下。

如果当时不答应通过 SQL 的方式帮他处理存量数据,他其实有更加正规的流程去处理这些数据,而且不会出错。

事后我们复盘的时候,也有同事私下向我提出了这个的问题:为什么不走 OA 流程去调整这个参数?

另外,关于流程,我给你举一个程序员方面的例子。

一个核心开发人员拥有线上数据库的操作权限,我们先假设这个人绝对忠诚、绝对可以信赖、绝对恪尽职守、绝对不会删库跑路。

某一天,他收到一个预警信息,经过排查发现需要去修改数据库里面某个数据的状态,他直接就去修改了。

这个操作非常常见,特别是在小公司或者在一些在快速发展阶段的公司。

后来这个公司成长起来了,开始更加注重操作风险了,回收了所有人员的数据库权限,以前的事儿既往不咎,以后想要修改数据库数据,必须要发起一个审批流程,经过层层审批之后才能执行。

这个流程和“直接去修改”这个动作比起来,就重了无数倍了。

站在程序员的角度,前几年都是可以直接操作生产数据,突然这个制度出来了,极大的影响了之前的开发惯性。所以刚刚开始执行的时候,你可能会骂一句:xxx。

但是长远来看,这个流程其实是在保护你。

当你有数据库权限的时候,操作对了,没有人会夸你。操作错了,你就是罪魁祸首。

有了一个审批流程,在加重了操作成本的同时,也降低了错误成本。

处理问题的时长可能增加了,对于问题处理的敏捷度可能降低了,但是站在公司的角度,随着公司的发展“稳定”才是永恒的主旋律,在稳定面前,敏捷度反而是可以牺牲的。

歪师傅在第一家公司业务野蛮发展的时代,曾经就有这样的权限,那个时候刚刚参加工作两年多的时间,觉得事情就应该是这样的,这样才是正确的,可以足够敏捷,足够迅速的处理问题。

后来权限回收了,当时我也在私底下骂骂咧咧了几句。

再回来,随着经验和在职场上见过得事儿越来越多,才渐渐认识到:蛮荒时代确实出英雄,但是我没有把握好机会成为英雄。蛮荒时代之后的流程规范,规章制度其实是在保护那批没有成为英雄的人,其中就有我。

最后,给你,也给我自己一个忠告:开发人员,你最好要知道你数据库里面每一个业务参数背后的业务含义,但是千万不要去碰那该死的业务参数。也轮不到你碰,该碰的人会在正确的流程下去碰。

无论什么时候,心中都要绷着这根弦。

标签:这个,该死,开发人员,流程,业务,参数,日利率,SQL,数据
From: https://www.cnblogs.com/thisiswhy/p/18540475

相关文章

  • 常见 setup.exe 参数 有关 Setup 命令行参数的其他信息,请参阅 Setup Help 文件。有
    Windows安装程序安装或升级Windows。Setup.exe[/debughelp][/auto<upgrade;dataonly;clean>][/quiet][/installdrivers<driver_folder_path>][/noreboot][/installangpacks<language_packfolder_path>][/showoobe<none;full>][/unattend:<ans......
  • 内核参数pci=realloc
    内核参数pci=realloc在Linux系统中,pci=realloc是一个内核启动参数,用于控制PCI设备所需的内存基地址寄存器(BaseAddressRegisters,BARs)的重新分配。这个参数对于解决一些PCI设备在启动时由BIOS分配的内存地址不正确、不兼容或者无法满足特定需求的问题非常有用。PCI设备的BAR......
  • 计算引擎中使用系统自动计算参数:@time_calc
    直接上代码: ///<summary>///auto_do_for_time_calc按照秒计算///@time_calc-86400返回则是当前时刻往前减去86400秒(也就是一天),可用:@time_calc-200,@time_calc+500,@time_calc300///select*fromtablewherecreate_time>'@time_calc-18......
  • 带参数的 Python 装饰器让你的代码更优雅
    引言在上一篇文章中,我们介绍了Python装饰器的基本概念及其简单用法。前面讲到的装饰器都是不带参数的装饰器,在需要对装饰器做一些针对性的处理的时候就不太适用了,这个时候需要对装饰器传入一些参数,根据传入的参数进行不同的处理。带参数装饰器在实际开发中能够灵活地调整函数......
  • 【优化参数】粒子群算法PSO求解三轴稳定航天器姿态控制PD参数优化问题【含Matlab源码
    ......
  • 分享一个超强的网页自动化工具!写得快,跑得快,开发人员狂喜(带私活)
       「今天分享一个开源项目:可控制浏览器,也可收发数据包,可模拟键盘和鼠标的操作」背景做数据采集的同学应该知道,当我们采集要登录的网站时,不仅要分析数据包、JS源码,构造复杂的请求,还要应付验证码、JS混淆、签名参数等反爬手段,门槛较高,开发效率不高。然后使用浏览器,可以......
  • OpenGL 和 GLSL 在顶点着色器中动态调整裁剪平面参数的简单代码示例
    以下是一个使用OpenGL和GLSL在顶点着色器中动态调整裁剪平面参数的简单代码示例://OpenGL初始化代码#include<GL/glew.h>#include<GLFW/glfw3.h>#include<iostream>GLFWwindow*window;//初始化GLFWvoidinitGLFW(){if(!glfwInit()){std::cer......
  • 如何进行数据库连接池的参数优化?
    以下是进行数据库连接池参数优化的一些方法:一、确定合适的初始连接数:考虑因素:数据库的规模、应用程序的启动需求以及预期的初始负载。如果数据库规模较小且应用程序启动时对数据库的即时访问需求不高,可以将初始连接数设置得较少,比如3到5个;如果数据库较大或应用启动后很快......
  • CST参数扫描设置细节
    cst参数扫描的细节 点开参数扫描对话框,新建扫描参数,例如参数a进行扫描1-2,0.5的步长,这样最后会出现3个参数的仿真结果。 这时如果增加参数b的扫描,在同一sequence下,同样1-2,0.5的步长,这样最后会出现9个结果,结果是交叉进行的。  如果增加一个sequence,同样1-2,0.5的步长......
  • 常用的 jvm 调优的参数都有哪些
    ​  堆内存设置-Xms:设置JVM堆的初始大小。例如:​​-Xms2g​​表示初始堆大小为2GB。-Xmx:设置JVM堆的最大大小。例如:​​-Xmx2g​​表示最大堆大小为2GB。-XX:NewRatio:设置年轻代和老年代的比例。例如:​​-XX:NewRatio=4​​表示年轻代和老年代的比例为1......