(转)软件公司可重用代码库的建设

  这个想法由来已久,也是因为自己在现在效力的公司中能看到一些不良的现象,但自己却不是管理层,没有任何的执行力去改变它。
  软件公司管理职位越往上,越不熟悉技术方面,加上此举和重构一样,很可能是吃力不讨好的做法,要得到执行则是想当困难的。客户没有这些要求,我们为什么要去做呢?如果员工有这个念头,我基本上可以看出他对自己的公司是没有想法的,工作如快餐式开发,做完拉倒的迹象很明显。
  软件公司越往大规模发展就越要注意可重用代码的利用,最好就是建立自己的代码库。
  我在公司做了一阵子开发后发现,开发团队中根本没有这个意识。当然开发团队没这个意识不是团队的错,而是带团队的人的错误。就是上层管理不重视,所以下层也就不重视了。为什么说团队中没有可重用代码的意识呢?很显然,公司没有建立自己的可重用代码库,员工在开发时也没有这个意识,当碰到问题不会先去问一下公司里是不是已经写过类似的算法了,这导致信息的极度不共享。员工可能会在不同的项目中反反复复地写那些简单的小函数。稍微能有点意识的人可能自己会把代码收集起来,自己用时拷贝粘贴即可。但是个人的东西一般是不会拿出来秀的,所以团队的水平仍然是大众的平均水平。个别案例,甚至为了一个简单的函数而研究很多工时,那可真是让人哭笑不得了。而且研究的时候,管理层还不一定知道他把时间全花在了那个小函数上,而是以为开发确实可能需要那些时间。这一定层次上也影响到管理层的管理和进度把握。
  那么我们怎么去组建公司自己的可重用代码库呢?
  一、做好精神领袖。
  有想法是好事,但一定要去做,不做的想法再好也没有用。我们首先就要给员工灌输这个意识,不仅仅是程序员,做设计的、搞测试的也要有这个意识。在工作中随时寻找可以抽象成复用的那一部分。要把这种意识从新人培训开始,就要深入的植入。此外也要培养员工对公司的家庭归属感。
  二、建立专门组织和电子化管理
  员工有了意识,那么接下来,我觉得公司应该有专门的人员管理这个库,建立起相关的组织来维护它。对外可以采用接口的形式,即一般不提供源码,只提供访问接口和库。
  首先要建立一个查询系统,提供库内已收入的方法,函数等等详细信息的查询,这个查询可以不设权限,和项目有关的人员都能自由使用。这一步很重要,因为写程序的人大多不善交际,如果你说谁有好的方法要抽象出来的,和项目经理反应,我估计就没人出来反应,这样这个想法就得不到很好的互动了。
  三、惰性增量式与后台研发式创建库。
  对于小公司可能没有太多人力资源,很多重要的东西只能在实战中积累,那么就是所谓的惰性增量式。即遇到一个问题实现一个问题,最后入库,不做单独的可重用库开发。有实力的公司可以同时由专人来负责其它可能的需求(当前并没有遇到),把它开发成重用库即为研发式创建。
  四、制定重用的准则。
  首先关于什么代码应该重用?一方面是通用的,每个产品都能用到的代码,肯定要入库;另一方面是具有专业性的算法,比如图形算法等等,这种代码的通用性仅限于那个领域,不是所有产品都能用上的,但也要建立,因为他的技术含量高,避免二次反复开发。
  对于客户而言,我们是一家人,是软件的开发和提供方,所以公司的代码一般不分服务与客户,代码大家都能看到,如果任其自由复制修改应用,那么虽然也叫重用但没办法统一,也就无法管理了。所以我主张,公司的重用库一般情况下最好实现封装,普通项目开发人员一般也得不到它的代码。他们要做的就是上报可重用的代码、查询库中已有的代码、直接调用。即使测试过程中发现是重用库的Bug,也和开发人员没有关系,由管理重用代码库的组织来解决,找到代码的作者,由其修改,新库一发布,就行了。所以说公司内部建立这种请求--服务的关系的话,各自的职责便明确化了。
  开发人员一般比较偷懒,有了请求--服务的模式后,请求的人就会增多,这对于加深开发人员可重用意识也很有积极的意义。一般我们也懒得去动脑筋写算法,有这个查询机会何乐而不为呢?系统上一查,没有,于是可以请求。可重用管理组的人在评定通过后,可以由他们来开发把这个算法添加到库中,我们在得到信息后可以直接调用。重用管理组背后的技术支持可能是牛人们,这有什么问题呢,我们是要开发产品,不是去比试哪个大学毕业生牛,大家各自有各自位置!有现成的就可以复用,有更强大的开发技术资源的就可以用,不要什么东西都自己去做。这一点很重要。
  当然,如果开发组的需求不是很通用,和库中的接口不协调的话,也可以变通,可以向管理组请求提供库的代码,在代码的基础上修改后直接应用,这样一来这些代码就不应该算是调用重用库了,一般是不推荐这样做的。或者项目组可以考虑应用适配器模式,适配接口,尽量用上库。实在需要改代码内部算法的也就只能提供源码让其修改后使用,总之尽量不要重新开发。
  另外,如果开发人员上报的可重用代码抽象地不理想,换言之,抽象地不通用,那么管理组应该要与项目组协调,必须把接口设计成通用性高的,由项目这边妥协修改调用形式;当然,如果因某些原因(比如接口是客户规定的),无法修改的,就不要收入库中了(可以收入改正后的版本单独入库),因为很明显这段代码是为了项目而生存的,没必要重用化。
  五、维护。
  这个就不用多说了,既然决定一试,就不是创建出来后可以袖手不管。诸如库的更新升级或者修改Bug的事务可能也不会很轻松,但是不能因此而荒废。可重用库的价值很大程度上就要看维护了。
  修改库的Bug原则上谁写谁改,如果是开发人员提供的,应该由开发人员修正,管理组审核。
  再一个就是文档,文档一定要详细,一定要实时更新。程序即时编辑提示文档、外部API手册、不同的版本、函数的不同用法、使用场合等等一定要详细,说白了,内部算法就算是爱因斯坦再世写的,放出的接口和文档,也要让傻瓜用的起来。我非常注重这点,因为我也有和这类开发人员合作过,算法的调用相当复杂(就是要你调用的人也要懂这个懂那个,否则用不来)。这个我很奇怪,但也怪不着他们,因为他们自己懂就以会天下人都懂,而没有这方面的意识了。哪天,微软和你说想用Windows?把这个二极管焊在主板的X位置,把那个三极管焊在Y位置,不要焊反,否则电脑烧了,你就别用Windows了。反正我们提供你Windows了,是你不会用,和我们无关。技术人员是有牛人,但人外有人,对于用Windows再牛的程序员,微软如果这样做,他也牛不起来了,除非去用Linux。所以做技术的一定要考虑怎样让不懂技术的人用得爽,这绝对是一个功力。诚然,他们的解释是合情合理的:算法就应该是纯净的,任何与算法无关的事前准备之类的,不属于算法内部。解释得很好,我也赞成,那么请麻烦一下,算法都写出来了,你就再写个适配接口吧,就把这部分当成应用,而不是你的算法不就行了吗。
  六、单元测试
  这是极其重要的一环,如果有专职的维护和技术人员的话,一定要建立对已有库的单元测试用例和相关测试程序。Bug修正后,首先要做的是,把原测试用例测试通过,保证修正后不破坏其它功能。然后新添加测试用例测试。
  可重用库,必须以严谨的方式管理,否则基础的东西出了问题,上层的构建将会直接面临崩溃的风险。千万不要等新库一发布,客户的新系统,老程序,产品A,产品B都崩溃了。
  以上谈了些个人对公司建设可重用代码库的一些想法,最后总结一下好处和风险:
  好处:
  1、这是公司的无形资产,是公司的发展日志。员工是公司的,员工创造的思想也是公司的,既然是公司的,公司不去注意收集、管理这些无形的东西,将来造成技术流失,能怪谁呢(只见管理层抱着一堆产品的源码,感叹地说,我们是有技术的,全在这里面,但是却没人用的起来)?
  2、降低以后的项目开发难度和缩短开发速度,提高软件质量。
  3、对公司的产品的架构形式统一管理。也就是说这些可重用的代码不要嵌在各个软件产品中,而是独立成通用的包或库。需要时只要对外发布更新库而不用更新客户程序。
  4、可重用代码库相当于公司的笔记,公司不怕核心员工跳槽,因为他曾努力写下的算法以经整理成可重用的库了。注意,一定要整理成规范易用的接口(算法再复杂也不要影响使用者的使用心态),不整理的随心所欲的源代码禁止入库。
  5、加强团队成员间的交流与和作。因为加强了这方面的意识,开发人员随时会注意这些问题而与周边组员,甚至重用库的管理人员进行沟通。
  风险:
  1、具有高风险,执行前需要一个深远的考虑和计划。否则不易执行及运行长久。
  2、维护库需要花费不少时间和精力,否则一旦发布新库,很有可能,所有产品受影响。不过可以考虑只针对部分产品发布库。但是如果你能确定现有库有一个Bug,而目前所有产品都使用这个库,那么从职业道德上来说,应该通知所有产品更新库,而不能等到客户Bug再现后再做处理。或者我们可以在公司网站上发布库,让客户自由决定是否下载最新的库。
  3、正因为这些库是基础的基础,所以在评定审查以及讨论良好通用的接口是十分重要的,一旦决定将无法随意变更,公司要有这个实力安排这些专业人员,从而增加了成本。只有做好了,才能收回成本,应用于以后的所有开发,做不好就真的是吃力不讨好了。
  4、产品系列具有严重依赖于这些库的特性,所以没有很好的管理,一旦出问题往往不是一个产品出事情,所有产品都会出问题。如果公司没有这种管理能力付不起这种风险,宁可一个产品一个产品分开开发,也不愿做一劳永逸的事,请不要使用这个方案。
  5、这个风险是采用这个方案的一个很大风险。就是人力资源上容易出问题。大学刚毕业的有些人还是很有牛劲的,总希望做些厉害的开发,如果他总是用公司的基础库,甚至代码都看不到,对他来说可能会不爽,觉得学不到东西,造成人才流失。这个只有进行教育了,实际上,在业务逻辑上也是很有用武之地的,不可能练不到人,或者往设计方向走,怎么可能练不到人,不要一味追求写底层的东西,公司能提供给你也是开发人员的福气啊,要学会站在巨人的肩膀上。所以一定要让开发人员理解公司的这种做法。
  说到这里,我又在想自己现在效力的公司,完全不注重“整理”。可重用库的建设我们就不去谈了,这种花成本没收益的事公司决计不做,那也应该注意下最简单的代码整理吧,至少在开发一个很有难度的项目时,管理层不懂得整理资源。以至于其它组开发类似的东西时,往往会听到管理层说这样的话,“好像某某在N年前做过这个东西,去问问他吧!”然后就看到开发人员从一个部跑到另一个部去取经。就算别人N年前做过,别人可不是闲人啊,专门给你解决这问题吗。就算愿意,他自己估计也忘了差不多了吧,还得再重新捡起来。对两个部来说都是降低效率的做法。如果别人已经离职了呢?那就把当年的项目源码翻出来吧,一起啃一起学,先把项目理解了,再找到你要的东西。于是加班开始了,公司还振振有辞,说加班是没有办法的,竞争激烈啊。要知道,良好的管理是不会加班的,加班绝对是哪个环节出问题了。
  自己亲自体会,越发觉得建立可重用库的重要性。不是说公司没有重用的思想,而是觉得不到位。公司的重用思想,就是粘贴拷贝,修修补补,完全没有管理。如果把重用意识上升到管理层次,那么对公司的将来,至少在技术上,我是非常有信心的。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Algorithms   本次README修订为算法仓Algorithms的第100次commit,首先我们庆祝自2016年8月4日本仓建立以来Dev-XYS在算法学习方面取得的显著进步!   这里有各种算法的C++代码,任何人可以在自己的任何程序中使用,欢迎大家指出代码中的错误以及有待改进的地方。   本仓内所有代码的授权方式为Unlicense,大家如果使用我的代码开发自己的软件挣了大钱,或是参考我的代码在NOI中得了金牌,我都会很高兴的。使用这里的代码之后,你可以自主选择是否公开源代码。总而言之,你可以把这里的代码当作你自己写的一样,无论怎样使用都是被允许的。但是,我不对本仓代码的正确性负责。大家要是使用我的代码开发软件而导致程序崩溃,或是参考我的代码在考试时出错,请不要向我抱怨。如果你愿意,遇到问题可以在Issues中提出来,我们共同解决。我们不赞成Pull Request,因为本仓主要储存作者已经学习的算法,全部代码均由作者本人负责维护与更新。   以下索引提供了本仓内算法的中文名,方便大家查找。更新可能有很长时间的延迟,不保证所有算法的名称都在列表中出现。 Index --------------------------Contents-------------------------- --------------------------FileName-------------------------- AC自动机 Aho-Corasick-Automation 单源最短路径(SPFA) Bellman-Ford(Queue-Optimised) 单源最短路径(Bellman-Ford) Bellman-Ford 使用Edmonds-Karp进行二分图匹配 Bigrpah-Matching(Edmonds-Karp) 普通的二叉搜索树 Binary-Search-Tree 广度优先搜索 Breadth-First-Search 冒泡排序 Bubble-Sort 桶排序 Bucket-Sort 组合数的递推求解 Combination(Recursion) 枚举组合 Combination 基本的复数类 Complex-Number 割点 Cut-Vertex 深度优先搜索 Depth-First-Search 堆优化的Dijkstra算法 Dijkstra(Heap-Optimised) 并查集 Disjoint-Set-Union 最大流Edmonds-Karp算法 Edmonds-Karp 欧拉函数 Euler's-Totient-Function 有向图的欧拉回路 Eulerian-Tour(Digraph) 拓展欧几里得算法 Extended-Euclid 简单的快速幂 Fast-Exponentiation 树状数组 Fenwick-Tree 所有结点对之间的最短路径(Floyd) Floyd-Warshall 凸包算法(Graham扫描法) Graham-Scan 辗相除法求最大公约数 Greatest-Common-Divisor 堆排序 Heap-Sort ISAP算法 Improved-Shortest-Augmenting-Path(Naive) 插入排序 Insertion-Sort 字符串匹配(KMP) Knuth-Morris-Pratt 最小生成树(Kruskal) Kruskal 最近公共祖先(Tarjan) Least-Common-Ancestor(Tarjan) 使用后缀数组求解最长公共子串 Longest-Common-Substring 最长上升子序列(n·log(n)) Longest-Increasing-Subsequence(n·log(n)) 倍增法求最近公共祖先 Lowest-Common-Ancestor(Doubling) 朴素的矩阵乘法 Matrix-Multiplication(Naive) 归并排序 Merge-Sort 最小堆 Min-Heap 乘法逆元 Modular-Multiplicative-Inverse 仅支持单点修改的可持久化线段树(维护区间和值) Persistent-Segment-Tree(Sum) 试除法素数测试 Prime-Check(Naive) 线性的素数筛法 Prime-Sieve(Linear) 队列的基本操作 Queue 快速排序的优化版本 Quick-Sort(Extra-Optimised) 快速排序的随机化版本 Quick-Sort(Randomized) 快速排序 Quick-Sort 使用向量叉积判断两个有向线段的时针关系 Segment-Direction 线段树维护区间最大值 Segment-Tree(Maximum) 线段树维护区间最小值 Segment-Tree(Minimum) 线段树维护区间和值 Segment-Tree(Sum) 普通的选择算法 Selection Eratosthenes素数筛法 Sieve-of-Erotosthenes 指针版的单向链表 Singly-Linked-List(Pointer) 跳表 Skip-List ST表 Sparse-Table 伸展树 Splay 博弈论SG函数 Sprague-Grundy 栈的基本操作 Stack 递推法求解无符号第一类斯特林数 Stirling-Number(Cycle,Unsigned,Recursion) 递推法求解第二类斯特林数 Stirling-Number(Subset,Recursion) 倍增法求解后缀数组 Suffix-Array(Doubling) 倍增法求解后缀数组(附带Height数组) Suffix-Array-with-Height(Doubling) 使用Tarjan算法求解强连通分量 Tarjan(Strongly-Connected-Components) 数组版的字典树 Trie(Array) 指针版的字典树 Trie(Pointer)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值