软件开发为什么很难

 问题的分类

  最初在1999年被Dave Snowden开发出来的 Cynefin 框架尝试把世界上的问题划分到了5个域中(大类):

  • 简单(Simple)问题,该域中的因果关系非常明显,解决这些问题的方法是 感知-分类-响应(Sense-Categorise-Respond),有对应的最佳实践
  • 复合(Complicated)问题,该域中的因果关系需要分析,或者需要一些其他形式的调查和/或专业知识的应用,解决这些问题的方法是感知-分析-响应(Sense-Analyze-Respond),有对应的好的实践
  • 复杂(Complex)问题,该域中的因果关系仅能够从回顾中发现,解决这些问题的方法是探索-感知-响应(Probe-Sense-Respond),我们能够感知涌现实践(emergent practice)
  • 混乱(Chaotic)问题,该域中没有系统级别的因果关系,方法是行动-感知-响应(Act-Sense-Respond),我们能够发现新颖实践(novel practice)
  • 失序(Disorder)问题,该域中没有因果关系,不可感知,其中的问题也无法被解决

  显然,软件开发过程更多地是一个复杂(Complex)问题。在一个产品被开发出来之前,不确定性非常高,团队(包括业务人员和技术人员)对产品的知识也是最少的,而且需要大量的学习和尝试才可以明确下一步可能的方向。不幸的是,很多时候我们需要在一开始(不确定性最高的时候)就为项目做计划。这种从传统行业中非常适合的方法在软件开发领域不再适用,这也是敏捷开发、精益等方法论在软件开发中更加适合的原因。

  正因为软件开发事实上是一个学习的过程,我们学习到的新知识反过来会帮助我们对问题的定义,从而带来变化。这里的变化可能来自两个方向:

  • 功能性
  • 非功能性

  功能性的变化指随着对业务的深入理解、或者已有业务规则为了匹配市场而产生的变化。比如支付方式由传统的货到付款变成了网银付款,又变成了微信支付、支付宝扫码等等。一个原始的电商平台仅仅提供基本的购物服务,但是后来可以根据已有数据产生推荐商品,从来带来更大的流量。这些变化需要体现在已有的代码中,而对代码的修改往往是牵一发而动全身。

  非功能性的变化是指随着业务的发展,用户规模的增加,数据量的变化,安全认知的变化等产生的新的需求。比如100个用户的时候无需考虑性能问题,但是100万用户的时候,性能就变成了必须重视的问题。天气预报应用的数据安全性和网络银行的数据安全性要求也大不相同。

  而在业务提出一个需求的时候,往往只是一个简化过的版本。

  非功能性复杂性

  这是一个经过设计师精确设计的界面,在它被设计出来之前,用户事实上无法准确的描述出它。设计过程中经历了很多的诸如:

  • 线框图
  • 颜色的确定
  • 交互的动画
  • 信息层次

  往复多次之后,界面确定了。在没有仔细思考使用场景的时候,开发会误以为这个功能非常简单。但是如果你是一个有经验的开发者,很快会想到的一些问题是:

  • 在宽屏下如何展示
  • 在平板上如何展示
  • 在手机上如何展示
  • 即使仅仅支持桌面版,跨浏览器要考虑吗?支持哪些版本?
  • 有些UI效果在低版本的浏览器上不工作,需要Shim技术

  除此之外,依然有大量的其他细节需要考虑:

  • 性能要求是什么样的?
  • 安全性要考虑吗?
  • 在网络环境不好的时候,要不要fallback到基础视图?
  • 既然涉及发送邀请函,送达率如何保证
  • 与外部邮件服务提供商集成时的工作量

  等等。这些隐含的信息需要被充分挖掘出来,然后开发者才能做一个合理的评估,而且这还只是开始。一旦进入开发阶段,很多之前没有考虑到的细节开始涌现:字体的选用,字号,字体颜色,元素间的间距等等,如何测试邮件是否发送成功,多个角色之间的conversation又会消耗很多时间。

  需求的变化方向

  作为程序员,有一天你被要求写一段代码,这段代码需要完成一件很简单的事:

  1. 打印”Hello, world”5次

  很容易嘛,你想,然后顺手就写下了下面这几行代码:

print("Hello, world")
print("Hello, world")
print("Hello, world")
print("Hello, world")
print("Hello, world")

  不过,拷贝粘贴看起来有点低端,你做了一个微小的改动:

for(var i = 0; i < 5; i++) {
  print("Hello, world")
}

  看起来还不错,老板的需求又变成了打印”Goodbye, world”5次。既然是打印不同的消息,那何不把消息作为参数呢?

function printMessage(message) {
  for(i = 0; i < 5; i++) {
      print(message);
  }
}

printMessage("Hello, world")
printMessage("Goodbye, world")

  有了这个函数,你可以打印任意消息5次了。老板又一次改变了需求:打印”Hello, world”13次(没人知道为什么是13)。既然次数也变化了,那么一个可能是将次数作为参数传入:

function printMessage(count, message) {
  for(i = 0; i < count; i++) {
      print(message);
  }
}

printMessage(13, "Hello, world");
printMessage(5, "Goodbye, world");

  完美,这就是抽象的魅力。有了这个函数,你可以将任意消息打印任意次数。不过老板是永远无法满足的,就在这次需求变化之后的第二天,他的需求又变了:不但要将”Hello, world”打印到控制台,还要将其计入日志。

  没办法,通过搜索JavaScript的文档,你发现了一个叫做高阶函数的东东:函数可以作为参数传入另一个参数!

function log(message) {
  system.log(message);
}

function doMessage(count, message, action) {
  for(i = 0; i < count; i++) {
      action(message);
  }
}

doMessage(5, "Hello, world", print);
doMessage(5, "Hello, world", log);

  这下厉害了,我们可以对任意消息,做任意次的任意动作!再回过头来看看那个最开始的需求:

  1. 打印”Hello, world”5次

  稍微分割一下这句话:打印,”Hello, world”,5次,可以看到,这三个元素最后都变成了可以变化的点,软件开发很多时候正是如此,需求可能在任意可能变化的方向上变化。这也是各种软件开发原则尝试解决的问题:如何写出更容易扩展,更容易响应变化的代码来。

  小结

  软件的复杂性来自于大量的不确定性,而这个不确定性事实上是无法避免的,而且每个软件都是独一无二的。另一方面,软件的需求会以各种方式来变化,而且往往会以开发者没有预料到的方向。比如上面这个小例子中看到的,最后的需求可能会变成将消息以短信的方式发送给手机号以185开头的用户手机上。

时间: 2024-10-08 09:58:35

软件开发为什么很难的相关文章

即时动作类网络卡牌游戏开发真的很难

我的<凤舞三国>--即时动作策略类网络手机戏从策划到实施如今已经过了16个月了,终于到今年4月(2016年4月)有望出一个内测包给众多期望代理偶家产品的发行们.蓦然回首才发现,偶从2011年3月进入手游开发行业如今已经过去5年了- 不由发出一声感叹:"光阴如梭.人生苦短啊." 我们也像其他众多小游戏公司,在2014年确定了开发三代即时动作类网络卡牌游戏的目标,因为先找到了一个非常好的美术外包合作伙伴,所以当时我们非常有信心8个月能够定搞定该游戏.我们当时认为:就游戏产品开发

学习android app开发会很难吗?

app开发是一个新兴的技术,也是一个新兴的行业,对于学校以及学生都还是新鲜事物,在概念和课程体系上,我们都没有太多的了解,很多刚开始想学习android app开发的同学都觉得android app开发很难学,那么到底android app开发难学吗? 学习IOS平台的app需要会objective –c语言,学习android则需要java语言,反正无论选择哪个平台,追溯到最基础的层面,C或者C++的基础必须有,这也使得厦门APP开发成为了新兴而高端的课程,这样的课程要说十分好学也谈不上,如果

软件开发为什么那么难

问题的分类 最初在1999年被Dave Snowden开发出来的 Cynefin 框架尝试把世界上的问题划分到了5个域中(大类): 简单(Simple)问题,该域中的因果关系非常明显,解决这些问题的方法是 感知-分类-响应(Sense-Categorise-Respond),有对应的最佳实践 复合(Complicated)问题,该域中的因果关系需要分析,或者需要一些其他形式的调查和/或专业知识的应用,解决这些问题的方法是感知-分析-响应(Sense-Analyze-Respond),有对应的好的

软帝学院:java开发程序很难吗?学会这十步,5分钟搞定一个程序

大致分为两部分第一部分一.要把实际问题提取为数学问题(相当于数学中的建模→抽象问题具体化);二.把其分解为若干个小的函数,并明白每个小函数怎样实现其功能,同时注意功能函数与主函数间的数据交互问题;三.作出流程图,整体把握,从而为软件构建良好的结构,但也应该注意其细节问题;四.优化其算法(强调数学能力),并结合其算法选择恰当的数据结构,算法和数据结构是软件的灵魂;五.编译软件,在编译时要养成良好的书写习惯,增强其可读性;六.调试软件,可以函数为单元进行纠错.注意:你的思路不清晰,写出来的东西没有目

Android JNI/NDK开发(一)NDK真的很难吗?

少壮不努力,老大徒伤悲.大学时光的潇洒散漫导致今天连C/C++编程都不会.作为一个程序员,不会C/C++说出去简直丢人啊.最近浏览公司招聘信息(Android职位),发现对NDK开发的要求越来越普遍了.笔者学习的是java,从事Android开发,对于Android底层的东西多少有点畏惧,因为没有涉及过,但是我们知道,不能因为怕就放弃.如我曾经签名所言:现在开始行动,就比还在犹豫的人快了一步. 回到正题,NDK开发真的很难吗?其实不是的,觉得难是因为你没懂,不懂是因为没学.那就来吧 NDK:什么

敏捷软件开发和传统软件工程

一.   传统软件工程 从上个世纪60年代开始,人们开始逐渐认识到了确实存在着"软件危机" 这样一个事实,软件开发人员被诸如下列问题困扰: 软件生产不能满足日益增长的需要 软件开发成本和开发进度估计往往不准确 软件开发人员和用户之间信息交流不充分,用户对完成的软件满意度很低 软件价格昂贵,软件成本在整个计算机系统中所占的比例急剧上升,软件已成为许多计算机系统中花钱最多的项目 软件质量难以保证 软件可维护性差,程序中的错误很难改正,适应性或完善性维护都极其困难 导致危机问题的一个重要原因

传统软件开发与敏捷软件开发的比较

本篇博客分别基于软件开发生命周期和范围管理这两个不同的方面对传统软件开发方法和敏捷软件开发方法进行分析比较,希望与读者分享交流. 传统方法: 从本质来讲,传统软件开发方法是一个软件开发架构,其开发过程是通过一系列阶段顺序展开的.通常,这一方法不能很好地表达和描述用户的需求,而且在项目整个开发周期的所有阶段都有需要不断完善的文档. 敏捷方法: 软件行业飞快发展,软件技术不断创新,客户期望迅速变化,考虑到需要克服传统开发方法的缺点,敏捷开发在近十年来兴起,以其灵活性,易操作性得到软件行业的广泛关注.

基于 Android 和 WCF 技术的软件开发

最近,同学的同学找我做了一款简单的安卓手机软件,第一次,一个人,做一个完整的项目.所以,在这里总结一下完整的开发流程和步骤,方便后来人入门学习. 其实,我是一个新手,没有系统的学过android也没有系统的学过WCF,这些都是自己一点一点尝试出来的. 先说一下我的基础: 安卓在三年前接触过,当时乱看一气.主要看的网站就是这个(http://www.fenby.com): 今年三月份,深入学习了数据库相关知识,对于数据的增删改查,三范式,E-R有了深刻了解: 今天五月份,负责一个软件开发的服务器端

软件开发和产品经理是怎么回事

过年的时候,经常有七大姑八大茄子们问道,你的工作是做什么的?电视上天天报道的黑客什么的,你们在外面可别干什么违法的事啊.每到这时候,我都想直接说,我就是一个修电脑的,可是真要这么说了,七大姑八大茄子们又要带着去各家转,一边转还要一边修电脑,说不定还要手机下载电影小说歌曲,对了,还要贴膜. 为了能够把软件开发这回事说得更清楚,正好是周末,就花时间把内容写一写,让七大姑八大茄子们也能知道什么是软件开发,我们这伙人每天都在做些什么事. 首先,说一下什么叫软件. 软件,就是一切支撑电子设备的灵魂.你使用