随想录(软件开发不能是加工作坊)

本文探讨了当前国内IT企业软件开发过程中存在的问题,并提出了包括加强需求分析、构建公共平台、抽象流程等在内的七个改善措施。

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】


     前一段时间看了一本《走出软件作坊》,心情很沉重。不管你是否承认,书中描述的情况在现在的国内IT企业中确实存在,可能涉及的范围还很广。联想到自己目前处于的行业,心中不免唏嘘不已。类似的事件,类似的方法,每天都在上演着。无休止的版本修改,无休止的测试,无休止的开发需求,人员的流失和更替,心中除了累还是累。现在的IT早已经不是10年前的香饽饽行业,大家都在经受着挑战和煎熬。现在的IT行业分布很广,IT信息化公司、网站公司、通信公司、嵌入式产品公司、芯片公司、网游公司、ERP公司,这些公司由于所处行业的上下游位置不同,公司的境遇差别很大。特别辛苦的是那些本身技术门槛比较低,缺少技术壁垒的公司,员工在承受着低福利的同时还要承受着与之不匹配的工作强度。这就是我们的生活现状。我们的IT开发非要这样不可吗?


    一般认为,IT项目开发的过程都是按照需求分析、软件设计与实现、集成测试与系统测试、后期维护这几个步骤进行的。我们应该好好反思一下,这几个步骤有没有提高和改进的空间。


(1)加强需求分析

    从商业的角度来说,交易的出发点就是为了满足客户的需求。只有精准地满足了客户的需求,我们才能更好地交付产品,实现双赢。当然,完成这一切的前提就是需要我们能够准确把握客户的需求。对很多公司来说,需求分析都是由销售来说,这是十分不靠谱的一个举动。因为,我们知道,销售人员的目标就是完成更多的签单,所以很多时候他会毫无原则地接受客户的一切要求。至于这些要求客户是不是真实需要,或者说这些需求本身技术能不能实现,那就超过了他本身的理解范围了。很多销售的理解范围其实就是入职时培训的那一些内容,至于技术细节或者产品性能方面的东西,那真是无能为力了。作为优秀的需求分析师,他不仅仅可以准确把握客户的需求,甚至在某种程度上可以影响客户的需求。这种例子虽然不多,但是也不鲜见。无原则的接单,不仅浪费了开发的资源,从大了说,也会影响公司的诚信水平,甚至危害公司的发展。


(2)抽象公共平台

    现在的服务器系统平台很多,windows可以,linux可以,unix也可以。因此,很多情况你不知道自己的服务端程序最终跑在哪一个操作系统上。所以,你要做的就是做一个抽象的公共平台。在这个平台上面,你可以忽视具体的细节,因为你使用的函数都是平台的函数,你使用的数据类型都是平台的数据类型,所以不管什么操作系统,你的服务器程序都可以准确无误地运行。为了做到这些,你可能在构造平台的时候需要对很多函数重新进行封装,比如,

a)内存申请、释放

b)socket创建、接受、发送、释放

c)信号量操作

d)文件创建、打开、读、写、关操作

e)定时器

f)消息发送和接受

g)延时函数

h)线程创建、优先级设置、属性设置、属性获取等等


(3)构建自己的软件库

    软件作为一个工程来说,事实上它也是由很多的子模块组成的。这里面的模块很多,比如说基本算法模块、数据库访问模块、图形模块、解析模块、日志模块、解压缩模块、pic读取模块。对于很多项目来讲,很多模块的功能都是可以复用的,那么如何把这些模块抽取出来就是我们需要完成的一个工作了。最最理想的情况就是,我们所有的软件都是由各个模块按照搭积木的方式组成的,如果我们需要对应的功能,那么打开对应的编译宏就可以了;反之,如果不需要这个功能了,那么关闭对应的宏即可。这方面,我们可以看一下linux代码。它上面的很多代码都是以模块存在的,那么多cpu、那么多fs、那么多chip都可以在上面运行,这说明linux整个系统的设计是非常开放和健壮的。


(4)抽象流程

    作为一个软件的主流程,这好像应该是软件主程序员应该负责的事情。其实,作为某一个模块的程序员,我们也可以从中学习到一些东西。就拿我经常说的一个例子来说,假设现在我们需要设计一个音频播放器,它需要支持mp3、wav、ogg等多种音频格式文件。看到这里,大家可以先考虑一下,这个软件应该设计?在这个地方,我们应该思考一下,所有的文件操作有什么共性的地方,能不能在各种音频文件之上构造出一个通用的文件访问流程。有了这个抽象的访问流程之后,那我们对各种音频的处理就是一个简单的注册和解析工作了。即使我们写的程序不正确,也不会影响原来主流程的运行过程。有了这一层抽象之后,可以极大提高我们工作的开发效率。


(5)单元测试

    单元测试是一种非常好的方法。本质上说,代码设计者应该是代码的最终负责人。可是在实际工作中,我们把软件的质量问题过多地放在了测试人员身上。好多人认为软件测试是一个非常无趣且单调的工作。其实,情况并非如此。对于功能性测试,我们应该尽可能采取自动化测试的方法,实现版本的每日构造和每日冒烟测试。而对于模块的测试,那就要进行代码的单元测试。就我个人的经验而言,如何设计stub函数,如何设计单元测试是非常考验人的一件事情。随着单元测试用例的增加,我们的代码会越来越健壮,整个模块也会越来越稳定。可是在很多公司,单元测试做的很不足,或者说很多公司干脆彻底就不做了。


(6)自己编写测试工具

    平时测试软件的时候,我们的方法其实不多。有的人习惯使用windows的性能分析工具,或者如果公司比较富裕一点,会自己购买pure coverage等工具。但是,其实很多时候我们是可以自己编写测试工具的。这些工具的编写不复杂,比如说

a)可以利用malloc重定向的方法,统计malloc的内存个数,看看内存有没有泄漏

b)利用开源工具gcov测试代码覆盖率

c)自己编写脚本解析模块,灵活地对代码功能进行测试

d)利用assert属性,捕捉一切异常的属性等等


(7)仿真工具

    在嵌入式开发中,实际环境常常是非常有限的。所以,创建一个有效的仿真平台是什么重要的。就拿android来说,我们就是在windows上面开发一个android的仿真环境,那么app的开发者就不需要每次开发一个版本之后,到实际phone上下载之后才能看到实际的运行结果。有了pc的仿真之后,他在pc上看到的结果基本上就是在phone上看到的效果。这中间没有别人的打扰、没有实际环境的搭建,工作的效率自然而然就可以提高上去了。当然不仅GUI可以仿真,原则上只要不和具体硬件相关的操作都可以而且应该放在pc上来解决。


    上面很多的内容都是我个人的一些总结和意见,欢迎朋友们多多交流看法。



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)
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式-老费

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值