如何建立良好的软件?软件不仅仅是编写代码而更是开发知识! - csc.gov.sg


软件具有使用传统管理技术难以构建的特性; 有效的发展需要一种不同的、更具探索性和反复性的方法。

为什么糟糕的软件会发生在好人身上
糟糕的软件是世界上为数不多的用金钱无法解决的事情之一。数十亿美元的航空公司的航班搜索应用程序通常不如学生群体构建的那些程序。在面临共享租车服务的威胁同时,全世界成熟的出租车公司其实都已经有了糟糕的预订应用程序。痛苦的企业IT系统通常是具有大量预算的项目,这些项目是在多年的时间内建立起来的。不管软件坏的原因是什么,似乎并不缺乏资金。

令人惊讶的是,不良软件的根本原因与特定的工程选择关系不大,而更多地与如何管理开发项目有关。最糟糕的软件项目通常以非常特殊的方式进行:
项目所有者开始想要构建特定的解决方案,并且从未明确地确定他们试图解决的问题。然后,他们收集了大量利益相关者的一长串要求。然后将此列表交给相应大型的外部开发团队,他们从头开始构建这个高度定制的软件。一旦满足所有要求,每个人都会在系统启动并宣布项目完成时庆祝。

不良软件的根本原因与特定的工程选择关系不大,而更多地与如何管理开发项目有关。

但是,尽管系统在技术上符合规范,但在将其置于实际用户手中时会发现严重问题。它缓慢,令人困惑,并充满了微妙的错误,使得使用它是一种挫败。不幸的是,到目前为止,外部开发团队已被解雇,并且没有剩余资源来进行必要的修复。当一个新项目可以在几年后启动时,所有关于导致这些问题的原因的知识已经离开了组织并且循环再次开始。

正确的编码语言,系统架构或界面设计将因项目而异。但是,软件特有的特征导致传统管理实践失败,同时允许小型创业公司以低廉的预算取得成功:

  • 重用好的软件很容易; 它是让你快速建立美好事物的原因; 
  • 软件的限制不在于构建它的资源量,而在于它在崩溃之前可以获得的复杂程度; 和
  • 软件的主要价值是不是生成的代码,而是谁生产它的人积累的知识。

了解这些特征可能无法保证良好的结果,但它确实有助于澄清为什么这么多项目会产生不良后果。此外,这些导致一些核心运营原则可以大大提高成功的机会:

  1. 尽可能简单地开始;
  2. 寻找问题并进行迭代; 和
  3. 聘用你可以最好的工程师。

虽然有许多更微妙的因素需要考虑,但这些原则构成了一个基础,可以让您开始构建优秀的软件。

重用软件可以让您快速构建好东西
软件易于复制。在机械级别,代码行可以逐字地复制并粘贴到另一台计算机上。更一般地说,互联网上有很多关于如何使用在线提供的现成代码模块构建不同类型系统的教程。现代软件几乎从未从头开发。即使是最具创新性的应用程序也是使用已经组合和修改的现有软件构建的,以实现新的结果。
可重用代码模块的最大来源是开源社区。开源软件是一种软件,其中代码可以自由发布,供任何人查看和使用。开源社区的许多最大贡献者都是巨型科技公司。如果你想像Facebook那样使用最先进的行星可扩展数据库,只需下载他们在2008年开源的Cassandra代码。如果你想亲自试用Google的尖端机器学习,请下载TensorFlow系统于2015年发布。使用开源代码不仅可以使您的应用程序开发更快,而且可以让您访问比您自己开发的任何技术都复杂得多的技术。对于最流行的开源代码,它更加安全,因为有更多人关注并修复漏洞。这就是数字技术取得如此迅速进步的原因:即使是最新的工程师也可以利用我们专业提供的最先进的工具。
云服务的出现进一步提高了可重用性,只需订阅费即可充分利用甚至专有系统。需要一个简单的网站?只需使用Squarespace或Wix等网站构建服务,只需点击几下即可配置一个。一个数据库?从Amazon Web Services或Microsoft Azure订阅虚拟版。云服务允许开发人员从专业化中受益; 服务提供商处理所有订户使用的可靠,高质量软件的设置,维护和持续开发。这允许软件开发人员停止在解决问题上浪费时间,而是专注于提供实际价值。
如果所有时间花在重建现有技术上,就无法取得技术进步。软件工程是关于构建自动化系统,而自动化的第一件事就是日常的软件工程工作。关键是要了解要重用的系统是什么,如何根据您的独特需求定制它们,以及修复沿途发现的新问题。

软件工程是关于构建自动化系统,而自动化的第一件事就是日常的软件工程工作。

软件受到复杂性的限制
(banq注:该文没有意识到 重用导致复杂性,因为一个软件要考虑重用,相当于多个职责都要考虑,抽象其中共用职责,万一抽象得不适当,导致使用起来非常复杂,不如各自开发各自得,解耦高于重用,才能降低复杂性,集中在一起重用才会导致复杂,重用导致忽视上下文,变得难以学习和熟练应用,那么多开源软件,真正适合你的应用场景的很少。)

一个软件的有用性通常受其复杂性的限制,而不是建立它所投入的资源量。
IT系统通常充满了功能,但仍然受到用户的讨厌,因为它们变得多么混乱。相比之下,排名靠前的移动应用程序往往因其简单性和直观性而受到称赞。学习使用软件很难。除此之外,新功能实际上会让用户感觉更糟糕,因为累积的复杂性开始变得势不可挡。例如,在担任Apple的媒体生态系统中心近20年后,iTunes今年分为三个不同的应用程序(用于音乐,播客和电视节目),因为它的功能对于一个应用程序而言变得过于复杂。从可用性的角度来看,限制不是可以实现多少功能,而是适合简单直观界面的功能。

即使忽略了可用性,一旦项目变得过于复杂,工程进度就会停滞不前。添加到应用程序的每个新代码行都有可能与其他每一行进行交互。应用程序的代码库越大,每当构建新功能时引入的错误就越多。最终,从新错误中创建的工作速率取消了功能开发所完成的工作速率。这被称为“技术债务”,是专业软件开发的主要挑战。这就是为什么许多大型IT系统存在多年未解决的问题的原因。为项目增加更多的工程师只会增加混乱:随着代码库从自身的重量中剔除,它们开始更快地运行。

构建良好的软件涉及交替扩展和降低复杂性的循环。

在这种情况下,前进的唯一方法是退后一步,使代码库合理化并简化。可以重新设计系统架构以限制意外的交互。即使已经构建了非关键功能,也可以将其删除。可以部署自动化工具来检查错误和编写错误的代码。比尔盖茨曾经说过“通过代码行测量编程进度就像测量飞机建造进度”。人类思维只能处理有限的复杂性,因此软件系统的复杂程度取决于复杂性预算的使用效率。
构建良好的软件涉及交替扩展和降低复杂性的循环。随着新功能的发展,无序自然会在系统中积累。当这种混乱开始引起问题时,暂停进展以花时间清理。这个两步过程是必要的,因为没有柏拉图式的良好工程:它取决于您的需求和您遇到的实际问题。即使是简单的用户界面(如Google的搜索栏)也会在表面下包含大量复杂性,而这些复杂性无法在单次迭代中完善。挑战在于管理这个循环,让它变得混乱到足以取得有意义的进展,但不要让它变得如此复杂以至于变得势不可挡。

没有柏拉图式的好工程:它取决于您的需求和您遇到的实际问题。​​​​​​​

软件是关于发展知识而不是编写代码​​​​​​​
在软件开发中,大多数想法都很糟糕; 这不是任何人的错。只是可能的想法的数量是如此之大,以至于任何特定的想法可能都不会起作用,即使它是非常谨慎和聪明地选择的。要取得进步,你需要从一堆糟糕的想法开始,抛弃最坏的想法,并发展最有希望的想法。Apple是一个富有远见的设计典范,它在登陆最终产品之前会经历数十个原型。最终产品可能看似简单; 这是一个错综复杂的知识,为什么选择这个特定的解决方案而不是它的替代方案,使它变得更好。
即使在产品构建之后,这种知识仍然很重要。如果一个新团队接管了一个不熟悉的软件代码,该软件很快就会开始降级。操作系统将更新,业务需求将发生变化,并且将发现需要修复的安全问题。处理这些微妙的错误通常比首先构建软件更难,因为它需要对系统的体系结构和设计原则有深入的了解。
在短期内,一个不熟悉的开发团队可以通过权宜之计修复来解决这些问题。但随着时间的推移,由于附加代码的临时性质,新错误会累积起来。由于不匹配的设计范例,用户界面变得混乱,并且整体上系统复杂性增加。软件不应被视为静态产品,而应视为开发团队集体理解的生动体现。

软件不应被视为静态产品,而应视为开发团队集体理解的生动体现。

这就是为什么依靠外部供应商进行核心软件开发很困难的原因。您可能会获得一个正在运行的系统及其代码,但是有关它如何构建以及所做出的设计选择的宝贵知识会让您的组织失去兴趣。这也是为什么将系统交给新供应商进行“维护”的原因常常会导致问题。即使系统记录得很好,每次新团队接管时都会丢失一些知识。多年来,该系统成为许多不同作者的代码拼凑而成。继续跑步变得越来越难; 最终,没有人真正理解它是如何运作的。
为了使您的软件能够长期保持良好运行,让您的员工与外部帮助一起学习以保留组织中的关键工程知识非常重要。

良好软件开发的3个原则​​​​​​​

1.尽可能简单地开始
对于特定领域而言,成为“一站式商店”的项目往往注定失败。推理似乎足够明智:有什么更好的方法来确保您的应用程序解决人们的问题,而不是让它尽可能多地解决?毕竟,这适用于超市等实体店。不同之处在于,虽然在设置实体店后添加新商品相对容易,但是具有两倍功能的应用程序的构建难度是其两倍,并且难以使用。
构建优秀的软件需要关注:从可以解决问题的最简单的解决方案开始。一个精心设计但简单化的应用程序从来没有添加必要功能的问题。但是,一个很难做很多事情的大型IT系统通常无法简化和修复。即使是成功的“全部”应用程序,如微信,Grab和Facebook,也开始使用非常具体的功能,并且只有在确保了自己的位置后才能进行扩展。软件项目很少失败,因为它们太小; 他们失败是因为他们太大了。

软件项目不会因为它们太小而失败,; 他们失败是因为他们太大了。

遗憾的是,在实践中保持项目的重点非常困难:只收集所有利益相关者的要求已经创建了大量的功能。
管理这种膨胀的一种方法是使用优先级列表。仍然需要收集所有要求,但每个要求都根据它们是绝对关键功能,高附加值还是非常有用而进行标记。这样可以创建一个低得多的紧张计划流程,因为不再需要明确排除功能。然后,利益相关者可以更加明智地讨论哪些特征是最重要的,而不必担心项目遗漏的问题。这种方法也明确了具有更多特征的权衡。想要提高功能优先级的利益相关者还必须考虑他们愿意优先考虑哪些功能。团队可以从最关键的目标开始,在时间和资源允许的情况下沿着列表工作。

我们对所有最成功的应用程序采用了类似的流程。Form.gov.sg最初是一个手动Outlook宏,花了我们六个小时为我们的第一个用户设置,但今天已经处理了大约一百万个公开提交。Data.gov.sg最初是一个开源项目的直接副本,并且已经发展到每月超过300,000次访问。Parking.sg有大量的200个可能的功能列表,我们从来没有开始构建,但今天仍有超过110万用户。这些系统虽然简单但不是因为它而受到好评。

2.寻找问题并重复
事实上,现代软件是如此复杂,变化如此之快,以至于没有多少计划可以消除所有缺点。就像写一篇好文章一样,尴尬的早期草稿对于了解最终论文应该是什么是必要的。要构建优秀的软件,您需要首先构建不良软件,然后积极寻找问题以改进您的解决方案。
这开始于简单的事情,就像与你想要帮助的实际人交谈一样简单。目标是了解您想要解决的根本问题,并避免仅根据先入为主的偏见跳转到解决方案。当我们第一次开始使用Parking.sg时,我们的假设是执法人员发现必须继续对纸质优惠券进行心理计算令人沮丧。然而,在与经验丰富的官员度过了一个下午之后,我们发现,对于专业人士来说,进行这些计算实际上非常简单。这一次谈话为我们节省了数月的潜在浪费,让我们将我们的项目重点放在帮助司机上。
谨防伪装成问题陈述的官僚目标。“驾驶员在处理停车券时感到沮丧”是一个问题。“作为我们部门家庭数字化计划的一部分,我们需要为司机构建应用程序”不是。“用户对在政府网站上查找信息的难度感到恼火”是一个问题。“作为数字政府蓝图的一部分,我们需要重建我们的网站以符合新的设计服务标准”不是。如果我们的最终目标是让公民的生活更美好,我们需要明确承认使他们的生活更加糟糕的事情。
通过明确的问题陈述,您可以通过实验测试不同解决方案的可行性,这些解决方案在理论上难以确定。与聊天机器人交谈可能并不比浏览网站更容易,用户可能不想在他们的手机上安装另一个应用程序,无论它对国家有多么安全。使用软件,显而易见的解决方案通常具有致命的缺陷,直到它们投入使用才会出现。目标尚未构建最终产品,而是首先尽可能快速且廉价地识别这些问题。用于测试界面设计的非功能性模型。半功能模型尝试不同的功能。匆忙编写的原型代码可以帮助更快地获得反馈。在此阶段创建的任何东西都应视为一次性的。
通过对正确解决方案的充分理解,您可以开始构建实际产品。您停止探索新想法并缩小范围,以确定您的特定实现的问题。从少数测试人员开始,他们将很快发现需要修复的明显错误。随着问题的解决,您可以越来越多地向更大的池开放,他们会发现更多深奥的问题。
大多数人只提供一次反馈。如果你首先向大量观众发布,每个人都会给你相同的明显反馈,你将无处可去。即使是最好的工程师构建的最好的产品创意也会出现重大问题。目的是反复改进输出,打磨粗糙的边缘,直到出现好的产品。
即使在完成所有这些迭代之后,在发布之后,产品问题也是最重要的。只有0.1%的时间发生的问题可能在测试期间不会被注意到。但是,一旦你有一百万用户,每天问题都没有得到解决,你需要处理的是一千个愤怒的人。您需要先解决新移动设备,网络中断或安全攻击造成的问题,然后才能对用户造成重大损害。通过Parking.sg,我们构建了一系列辅助系统,可以不断检查主系统是否存在付款差异,重复停车会话和应用程序崩溃。随着时间的推移建立一个“免疫系统”可以让你避免不堪重负,因为新的问题不可避免地会出现。
总的来说,方法是使用这些不同的反馈回路来有效地识别问题。小的反馈回路可以快速轻松地进行修正,但却错过了更广泛的问题。大型反馈循环可以解决更广泛的问 您希望同时使用两者,尽可能使用紧密循环解析,同时仍具有宽循环以捕获意外错误。构建软件不是为了避免失败; 它是在战略上尽快失败,以获得建立良好事物所需的信息。

3.雇用最好的工程师
拥有良好工程学的关键是拥有优秀的工程师。谷歌,Facebook,亚马逊,Netflix和微软都拥有令人眼花缭乱的全球最大技术系统,然而,他们有一些最有选择性的采访流程,同时仍在激烈地竞争招募最强大的候选人。有一个原因是,即使是应届毕业生的工资也随着这些公司的增长而大幅上升,并不是因为他们喜欢放弃金钱。
史蒂夫乔布斯和马克扎克伯格都表示,最优秀的工程师的工作效率至少是普通工程师的10倍。这不是因为优秀的工程师编写代码的速度要快10倍。这是因为他们做出了更好的决定,节省了10倍的工作量。
优秀的工程师可以更好地掌握他们可以重复使用的现有软件,从而最大限度地减少他们必须从头开始构建的系统部分。他们可以更好地掌握工程工具,自动完成自己工作的大部分日常工作。自动化还意味着释放人类以解决意外错误,最好的工程师在这方面做得更好。优秀的工程师自己设计的系统更健壮,更容易被他人理解。这具有乘数效应,让他们的同事更快更可靠地建立他们的工作。总的来说,优秀的工程师是如此有效,不是因为他们产生了更多的代码,而是因为他们做出的决定可以避免你从不知道的工作中解脱出来。
这也意味着最优秀的工程师团队通常可以比普通工程师的大型团队更快地构建。他们充分利用可用的开源代码和复杂的云服务,并将平凡的任务卸载到自动化测试和其他工具上,这样他们就可以专注于创造性解决问题的工作。他们通过确定关键功能的优先级并减少不重要的工作,快速测试用户的不同想法。这是经典着作“ 神话人月 ” 的核心论点1:总的来说,增加更多的软件工程师不会让项目变得更快,只会让它变大。

构建软件不是为了避免失败; 它是在战略上尽快失败,以获得建立良好事物所需的信息。

与普通工程师的大型团队相比,规模较小的优秀工程师团队也将创建更少的错误和安全问题。与撰写论文类似,作者越多,编码风格,假设和怪癖就越多,在最终的复合材料产品中进行协调,从而为可能出现的问题提供更大的表面积。相比之下,由较小的优秀工程师团队构建的系统将更加简洁,连贯,并且更好地被其创建者理解。没有简单性就无法获得安全性,简单性很少是大规模协作的结果。
工程工作越协作,工程师就越需要。工程师代码中的问题不仅影响他的工作,也影响他的同事的工作。在大型项目中,糟糕的工程师最终会为彼此创造更多的工作,因为错误和糟糕的设计选择会产生大量问题。大型项目需要建立在可靠的代码模块上,并采用有效的设计,并且具有非常明确的假设。您的工程师越好,系统在自身重量崩溃之前就越大。这就是为什么最成功的科技公司尽管规模庞大但坚持要求最好的人才。系统复杂性的硬限制不是工程工作量,而是质量。

结论
良好的软件开发始于清楚地了解您想要解决的问题。这使您可以测试许多可能的解决方案,并采用一种好的方法。通过重用正确的开源代码和云服务,加速开发,允许立即访问已建立的软件系统和复杂的新技术。开发周期在探索和整合之间交替,快速而混乱地进行新的想法,然后集中和简化以保持复杂性的可管理性。随着项目的进展,它将逐渐受到更多人群的测试,以消除日益罕见的问题。启动是指真正的工作需要一个优秀的开发团队:应该构建多层自动化系统来快速处理问题并防止对实际用户造成伤害。


HN评论:
1. 软件不仅仅是编写代码而更是开发知识,管理层在团队之间传递任务,从不关注知识和知识转移,我遇到了更多问题。令人惊讶的是,作为一名软件工程师,在18年多的时间里,我已经看过很多次了。团队将运作良好,然后该机构试图改变。通常他们会试图通过在研发上投入资金来开辟“创新”,基本上是为了增长而增加体积。然后你有很多团队,沟通变得非常具有挑战性,所以他们就会发展出某种“任务管理”层。管理层从不了解谁实际上知道了什么,只是跟踪他们拥有多少“理论带宽”以及创建功能的愿望清单。然后crapware真正开始流动。然后我感到无聊,继续前进到下一个地方。

2. “更喜欢工作代码而非综合文档”并不意味着“不做文档”。
文档是必不可少的 如何工作是一件重要的事情。理想情况下,它应该在版本控制中并从代码生成,因为它不太可能过时。它仍有问题(当代码和文档不一致时你会怎么做?哪个是正确的?)
我用战略DDD修复它 - 我至少开发了一种“普遍存在的语言”(或UL):我会让其他人与我一起制定明确的术语,并确保在用户故事中始终如一地使用它并在代码库中。这是赌注。

然后,我会在我正在工作的环境中发生事件并开始开发高级文档。
即使在这一点上,系统之间的关系也会出现,你可以围绕事物绘制圆圈并命名它们(域,上下文),并且UL会更好一些。此时,您可以开始考虑使用UL以及域和上下文的语言来描述您的一些服务。

到那时,人们应该开始点击这样可以让生活变得更轻松 - 混淆更少,现在你们都在共同努力,以便对设计有一个共同的理解,DDD的重点在于设计和代码的匹配