文档章节

2.1 一行代码值多少钱?

暗夜在火星
 暗夜在火星
发布于 2017/03/08 22:19
字数 3712
阅读 123
收藏 0

2.1 一行代码值多少钱?

一个问题的意义,不仅在于解决了这个问题,更在于通过这个问题引伸出更多富有意义、更具挑战性的问题。

在这里,我们将尝试探索“一行代码到底值多少钱”这一问题,进而通过这个问题,引出更多深层次的问题,最后收敛于线上故障。

2.1.1 简单的计算

最简单粗暴但明显是错误的计算,假设一位开发工程师的月薪是1W,按每月22天折算,其工价约为:1RMB/分钟。又假设,写一行代码,平均需要10分钟。

用一张逻辑推断图表示即:

这是局限于开发人员个体在单纯进行编写一行代码本身所需要的造价,这明显是不全面的,因为还没考虑开发人员在编写这行代码之前进行需求分析、领域模型设计所需要花费的时间,更不用说与其他干系人在项目开发过程中进行的沟通协作成本。

但这很可能正是很多人眼中或潜意识中错误认识的观念:写一行代码不是几分钟的事情吗?由此暗示一行代码低兼的造价。

实际上,真的是这样吗?一行代码,真的只是值10块钱吗?

2.1.2 进一步地计算

更为合理地评估一个项目的经费,应该是从完成交付的系统功能点以及开发团队为此付出的精力和时间这两方面进行衡量。如果只是单纯按代码行数来计算,则会导致开发人员本来用一行代码完成的事情分拆成N行,而不是激励程序员编写更短更美更达意的代码。

这样,进一步计算,我们可以把功能点和投入的时间所需要的成本,再平均分摊给每一行,从而也可以得出每一行代码值多少钱。

继续用一张逻辑推断图表示即:

这似乎更为合理,因为我们考虑了更多的因素。但,一行代码,真的就只值几十块钱吗?

2.1.3 未知的损失计算

一行代码到底值多少钱?我觉得,不仅要考虑已知的成本,还应涉及未知的损失。即:

损失即存在的风险,既包括了可预见的风险,还包括了不可预见的风险。而造成的损失则会根据影响的时长、涉及的用户数以及对现实世界造成的损害和为此而付出的代价而定,小则几百,大则上万。况且,还有很多损失的代价是不能直接从经济方面衡量的,甚至是不可逆的惨痛教训。

所以,一行代码值多少钱,不仅要考虑沟通、测试、开发、文档、部署、宣传、维护的成本,还要考虑由于代码问题而损失的代价。

假设某次故障导致的损失为5W,而最终导致故障发生的原因为代码问题,并且假设出错的代码只有一行,那么这行代码则处于关键的位置,并价值甚大。

在我曾任职的公司里,我就因为一行代码造成商品无尺码显示的线上故障,当时由于导致用户无法购买,评估的损失约为数十万。而这次故障正是因为一行代码而导致的!

2.1.4 浅谈损失的造成

损失,不仅有因为过错而导致的损失,还有因为没做什么而造成的损失。

该做异常处理的却没捕捉导致程序crash异常退出;

该做超时重试的却没设置导致进程无限等待最终导致服务器内存耗尽;

该做SQL注入防范的却没转换导致数据库入侵甚至泄露敏感用户数据,一如账号密码;

该做负数判断的却没检测导致在金额消费扣除时负负得正,越花越多钱。

而这些,可能需要联系各个渠道重新全量发版以修复用户app崩溃的问题;

可能需要紧急发布以修复服务器集群运行到一定时间就突然down机的奇怪现象;

可能需要统一对外公告并告知用户及时修改密码;

可能需要开发人员不仅要修复上线,还要花好几天的时间排查各个用户的日记,通过grep找出不合法的“充值”纪录然后追踪其消费流水过程,进行相应的处理甚至封号、列入黑名单。

“过错”是不小心而为的,“没做什么”中很多时候明知多做一点就能预防,却因为一时偷懒、侥幸而没做,最终在生产环境被触发,继而放大,酿成故障,实在是不应该。

2.1.4 一行代码的生命历程简史

若把每一行代码都比作是有生命的小个体,我想它终其一生也许会是这样的:

当开发人员按下Enter回车键后,一行由特定的字符排列而成的代码就从混沌中诞生了。当它的”创世者“保存后,这一行代码就来了我们的世界。

那时,一切都还很平静,它既感到好奇又觉得陌生,于是便和身边的小伙伴亲切交流了起来,以便彼此互相认识。

随后,开发人员会把它提交到版本控制系统,进入到质量测试环节。

这时,会有一些专门的测试团队会找出他们编写不合理的地方并反馈给开发进行相应调整。经过多次打磨,在投入到真正的战场前,它还要走过一段漫长的道 路,如持续集成、回归测试、灰度发布等。

最后,它们被部署到了最终真正线上的环境,一个更为复杂、严酷、激烈的环境。每时每刻,它们都在 CPU中穿梭运行,每天处理了上W次的请求,接收/输出上T级的数据,为各种各样的用户提供着有价值的服务,而这些用户中,除了有正常的用户,还有一些恶 搞的用户、非人类的用户,如:爬虫、机器人、搜索引擎等等。有些用户会尝试从这一行行的代码获取他们需要的业务功能,有些用户则会尝试获取一些更高级但非 法的功能,而有些用户则试图发现极有破坏性的代码行,然后触发它。。。

当它老时,需要退役时,应当能够做到优雅地退出历史舞台而不会给现有的系统造成其他影响,否则会变成熔浆式代码,影响后生的代码。

每一行代码所处的环境,都是已知的,但又是未知的。

已知是因为我们知道它在那个位置需要担当什么功能,实现什么业务;未知因为没办法完全了解它会面临怎样的系统平台、怎样的数据、以及怎样的用户、怎样的一个处境。

这要求我们在写每一行代码时,不仅需要知道编程语言本身的语法特性,还要了解它的运行机制、原理,还要清楚需要实现的业务功能,还要能考虑到它未来将会运行于怎样的一个场景下,最难得可贵的是,可以深入考虑到它将会面临怎样的重大问题,包括功能性的以及非功能性的。

也就是我们通常所说的,是否具备可扩展性、安全性、稳定性这些质量属性,正是由这一行行的代码聚合而成的。

2.1.5 如何写出好的代码

终其代码的一生,我们需要在一开始就为它将要面临的问题与挑战做好充分的准备。每个问题之所以会发生,都是因为我们想得还不够深入。只要我们再想多一点,就可以避免重大问题的发生。而且,往往我们觉得会有问题发生的可能时,往往都会发生。

何谓好的代码?

好的代码,一行也不能少,一行也不会多。

好的代码,应该是高度自治的,即它不会以损害其他的代码为代价来完成自己的事情或功能。而是应该本身是健康的,既无腐蚀,又无对系统的入侵威胁,反而能帮助其他的代码行一起完成重大而严谨的使命任务。并且能适应更多的情境。

反观过往的经验,问题发生的节点更多在于与外部系统的交互上,比如调用了远程的接口而没有设置超时而陷于漫长等待,进行了数据库访问而没考虑到并发量而导致了阻塞,调用了Android的系统API而没进行版本判断而导致崩溃。

出现问题的代码,通常都是脆弱、不规范、有问题的代码。

因为代码的编写原则和规范都有很多相关说明,下面再简单补充一下:

  • 遵循惯例
  • 忠于代码本身
  • 功能导向
  • 考虑上下文
  • 能够适应更多的环境

下面分别说之。

遵循惯例

各个语言都有语法上微妙的容易犯的错误,一如:

  • 10 Most Common PHP Mistakes
  • The 9 Most Common Mistakes That Ionic Developers Make

忠于代码本身

在编写一行代码时,我们应当理性地进行编写,而不应感性地编写。

也就是说,我们应该遵循编码的开发规范、满足项目面临的约束、保持系统架构的风格和原则。

再直白一点,这一行代码的结果需要加强判断的则加强判断,这一行代码需要考虑容灾的则添加容灾,这一行代码需要向前兼容的则向前兼容。

而不能因为我本来也知道需要进一步完善的,但因今天心情不好、或因需求频繁变动而有抵触情绪,或抱着侥幸的可能不会发生的心理,使得应当加强的没加强,应当容灾的没容灾,应当兼容的没兼容,最终导致了线上重大的故障发生。则属于是感性编程,而非始终忠于代码本身功能的专业体现。

存在即合理。但我们也可以反过来理解:代码因为合理而存在。不合理的代码,即有缺陷的有bug的代码,最终还是会被修复优化、甚至被移除的。既然,一开始就可以做好的事情,为什么要付出代价后才肯回来弥补呢?

功能导向

在我刚毕业时,曾就职于一家游戏公司。当时我很自然地敲出以下操作命令:

$ php ./all_in_one.php >> run.log 2>&1

而站在一旁的一位资深架构师(也是我颇为敬仰的大神)突然问我:你知道这行命令的意思吗?

当时,我愣住了。仿佛回到了当年校招面试时被问到了一个完全不懂的问题,一脸不知所措。更为尴尬的是,我一直在使用这样的命令,它每个参数的作用我竟然不知!

所以在写每一行代码时,都需要知道它确切的意思,各个参数的作用,内部的原理,与其他类似接口的微妙关系与区别。

需要多想想,多查查,这一行代码是否能很好地胜任我们将要赋予给它的任务?我们需要用它来做什么,我们又不需要它在背后做什么?

考虑上下文

一切不考虑场景上下文的代码,都是耍流氓,是不负责任的。

在一次性能优化中,我们发现有一行代码被调用了17K次!几乎占用了接口响应时间的10%!

绝大多数的情况下,以下这样的写法都是没有问题的:

$bufLength = TStringFuncFactory::create()->strlen($this->buf);

而且我们也确实看不出有什么问题。这是一个很好的封装,也使用了工厂方法模式,可以说这是一行很合格且写法清晰的代码。

再来看下TStringFuncFactory::create()内部的实现,也是使用了单例模式,没有特殊之外。

但令人颇为惊讶的是,在xhprof的报告中,我们发现此函数竟然被调用了17K次!

回顾TStringFuncFactory::create()被调用的场景,我们从图中,可以看出TFramedTransport::read()竟然调用了11,700次!

最终优化后,调用次数从17K降到了只有100多次,三个级别的差距!为了有一个更形象的对比,用米作为单位,试这样粗算对比前后优化的效果:

函数调用次数 等效的长度(单位:米)
优化前 17K次
优化后 100多次

需要注意的是,实际的优化效果与接口返回的报文长度成正比。报文长度越大,差距越明显。

能够适应更多的环境

软件开发发展至此,已大概有30多年。我们所编写的代码,最终会运行于不同的平台、不同的系统、不同的硬件、还需要不同的配套外部系统或依赖系统,以及各编程语言的支持,而这些平台、系统、硬件、外部系统、编程语言又会分为不同的版本。

这就有点像,一行弱小的代码,被扔进了一个错综复杂的世界,不仅需要满足各个霸主苛刻的要求,还要面对不可预计的未来 – 因为它不知道它将会接收处理怎样的数据。

此时,我们应当考虑到代码的健壮性,以便它能适应更残酷的环境。

2.1.6 论质量提升的重要性

任何问题,最终其实都是属于团队问题,而不是个人问题。

正如《清单革命》说道的那样:团队犯错的概率会更低。所以代码虽小,影响甚大

© 著作权归作者所有

暗夜在火星

暗夜在火星

粉丝 168
博文 176
码字总数 326355
作品 1
广州
程序员
私信 提问
一行代码值多少钱?

一行代码值多少钱? 一个问题的意义,不仅在于解决了这个问题,更在于通过这个问题引伸出更多富有意义、更具挑战性的问题。 在这里,我们将尝试探索“一行代码到底值多少钱”这一问题,进而通...

暗夜在火星
2016/11/12
17
0
[转]C/C++编码规范总结

一、文件排版方面 1.包含头文件 1.1 先系统头文件,后用户头文件。 1.2 系统头文件,稳定的目录结构,应采用包含子路径方式。 1.3 自定义头文件,不稳定目录结构,应在dsp中指定包含路径。 ...

四彩
2016/02/04
101
0
3.04-Miniui总结(一)

1、datagrid 行变色: 2、 树形分页表格PagerTree pagerTree会根据返回值追加属性伪装成树,展开或收缩即为一次查询 2.1、数据源形式(pid不可少,若数据库中无pid,可根据实际情况在Control...

静以修身2025
03/11
0
0
今年加薪了没?

问:.net java 前端 工作经验: 一年值多少钱? 二年值多少钱? 三年值多少钱? 四年值多少钱? 五年值多少钱? 小组负责人值多少钱? 项目负责人值多少钱? 部门经理值多少钱? 产品经理值多...

小小的夏
2015/03/17
6.2K
38
C Primer Plus 第2章 C语言概述

2.1一个简单的实例 程序清单2.1 first.c程序 ------ #include <stdio.h> int main() { int num; num=1; printf("I am a simple"); printf("computer.n"); printf("My favorite number is %d......

idreamo
2016/05/07
37
0

没有更多内容

加载失败,请刷新页面

加载更多

android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

本文将基于android6.0的源码,对Camera API2.0下Camera的preview的流程进行分析。在文章android6.0源码分析之Camera API2.0下的初始化流程分析中,已经对Camera2内置应用的Open即初始化流程进...

天王盖地虎626
10分钟前
0
0
java 序列化和反序列化

1. 概述 序列恢复为Java对象的过程。 对象的序列化主要有两 首先我们介绍下序列化和反序列化的概念: 序列化:把Java对象转换为字节序列的过程。 反序列化:把字节序列恢复为Java对象的过程。...

edison_kwok
21分钟前
0
0
分布式数据一致性

狼王黄师傅
今天
1
0
经验

相信每位开发者在自己开发的过程中,都会反思一些问题,比如怎样提高编程能力、如何保持心态不砍产品经理、996 之后怎样恢复精力……最近开发者 Tomasz Łakomy 将他 7 年的开发生涯中学习到...

WinkJie
今天
4
0
从源码的角度来看SpringMVC

SpringMVC核心流程图 简单总结 首先请求进入DispatcherServlet 由DispatcherServlet 从HandlerMappings中提取对应的Handler 此时只是获取到了对应的Handle,然后得去寻找对应的适配器,即:H...

骚年锦时
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部