Author: nista

2 次浏览

有关读书、系统思维和怀疑主义

“读书 v.”、“认真 adv.”、“简朴 adj.”、“专注 adv.”,这些都只是人生历程的工具而已。最终要获得的总是某个目标,比如某个感悟或顿悟、成长或价值收益、知识增长或喜悦幸福感,这个目标就是获得满足感。刚才说的系统思维,让我想起前两天看到的一个很好的例子。

 

古希腊有个比较小众的哲学学派叫怀疑主义学派,有位学者叫皮浪,他有句名言“不做任何任何肯定的结论,悬置(Epochê)判断”。你对这句名言的理解,可以出于自身的阅历、也可以处于别人写的书、或者其他人的点评。总之,只要能够给你提供对于这句话的更多启发和理解,那就是达成了获得感的目标。

 

我从其他地方得到的关于对这句话的理解是:人生都是处处都是在做选择的过程,但是过多的选择、决策会让我们感到焦虑,因为我们不知道某种选择是否正确、选择后的结果是否能获得价值增量、我们会甚至怀疑自己做决策的驱动力是否充足。

 

这个时候,对于【不紧急的】决策,我们可以考虑选择悬置判断。意思就是:当你面临这样一个问题时,例如“我该不该跳槽”,不要下决定,悬置它,此时,想象有一位高人,他会帮我们做出选择,他无比的理性客观,掌握海量的现实数据,知道现时趋势。此时,这个高人的画像(Persona)就在我们心中形成了,你需要做的只是寻找这样的高人具体在哪。

 

你也许会发现,这位高人可能是具象的主题专家或我们能遇见的经验丰富的导师,但是,如果从抽象角度而言就是所谓的市场、客观数据、社会环境、行业趋势、聘用现状。

 

因此,当我们面对这类问题时,我们可以只专注于当下,持续的积累、发展自己并不断地洞察市场、环境、趋势,不急于决策,有时候这位“高人”就会把你自然的带到你的下一个位置。

 

悬置判断,不做决策,相信无形的手一直在那,有些时候,没必要让自己把焦虑生成后,再反过来输出给自己,此时的焦虑就是一种浪费。

 

这个事情的全部过程,感觉就像 教练 + 系统思维 + 精益 + 怀疑主义哲学了,挺有意思的。

 


对于古希腊哲学的时代划分,可以参考下边这个文章,之所以拿出来,是你会发现社会的萌芽发展、盛世或由盛转衰的不同阶段,所导致的新思想的诞生,或者被大家选取的主流思想,总是相似的,希腊就是个例子,也许当下的中国也是。

 


希腊哲学的小流派:怀疑主义

  • 塞克斯都·恩披里柯,怀疑主义学派学者
  • 皮浪,古希腊怀疑主义学派,怀疑论者:他有一句名言:“不做任何肯定的结论,悬置判断”

 

有关古希腊哲学时代的划分

 

皮浪有一句名言,可能很多人都听过,他说“不做任何肯定的结论,悬置判断”。你可能说,这种既不肯定,也不否定,有什么意义呢?这正是一种怀疑主义精神,一方面,悬置判断是因为事物本身的不确定性;另一方面,也可以说是对“真理”或者“决定论”的质疑。这种怀疑主义精神,在后来的笛卡尔、大卫·休谟为代表的近代西方哲学中非常常见。

 

在了解皮浪的思想之前,让我简单聊聊一下皮浪所处的时代背景。

 

在公元前4世纪,古希腊经历了伯罗奔尼撒战争之后,古希腊文明已经走向衰落,而皮浪就出生在古希腊走向衰落的这一段历史时期,皮浪出生于公元3世纪左右,具体的年份存在争议。在这一时期,古希腊出现了几个人文主义的哲学流派,比如以第欧根尼和安提西尼为代表的“犬儒学派”,这个学派直译过来就是:像狗一样生活。但其实他们的生活方式看似朴素,但并不低贱。他们鄙视财富和享乐,主张回归自然,简朴的禁欲主义生活方式。他们推崇人与人之间的友爱互助,在苏格拉底后期,人文主义哲学思想的重要代表。

 

另外还有以伊壁鸠鲁为代表的“伊壁鸠鲁学派”,他们和犬儒学派正好相反,他们的主张就是“快乐主义”,鼓励人们,积极追求人生的幸福和快乐。伊壁鸠鲁有一个关于死亡的名言,流传很广,他说:我们不需要害怕死亡,因为我活着的时候,死亡不会来临。而当死亡来临的时候,我们也已经不在了,所以没有必要害怕死亡。这听起来还真有点道理,其实这背后,正是体现了这个学派的一种乐观积极的人生态度。

 

第三个是以巴门尼德的学生芝诺为代表的“斯多葛学派”,这个学派充满了理性主义和自然主义倾向,他们的部分思想和我国的道家思想非常类似,他们都认为,自然界的一切发展和变化都是有规律的、是符合理性的。芝诺曾经说:“与自然相一致的生活,就是道德的生活,自然指导我们走向作为目标的道德。”

 

斯多葛学派强调顺从天命,要安于自己在社会中所处的地位,要恬淡寡欲,只有这样才能得到幸福。所以,斯多葛学派强调人和自然的和谐共处,他们认为自然界的一切发展和变化都是有规律的、是符合理性的,人应该尊重和顺应大自然的变化,相信“一切都是最好的安排”。

 

另外一派就是我们今天要介绍的以皮浪为代表的“怀疑主义学派”了。其实这一时期的四个学派,和苏格拉底时期,或者苏格拉底之前的古希腊哲学有一个非常不同就是,他们都关注人类自身的感受和幸福,而不再关注外在的宇宙万物。

 

其实古希腊哲学大概经过了三个发展阶段,①一个是苏格拉底和普罗泰戈拉之前,比如泰勒斯、巴门尼德、赫拉克利特、毕达哥拉斯等等,他们的哲学思想关注的是宇宙万物的构成,以及宇宙万物的变化规律和法则等等②第二个阶段是苏格拉底、柏拉图、亚里士多德为代表,他们从关注外在的宇宙万物,转而关注到人类自身的伦理道德,自我的认识,人类的真理等等,苏格拉底一生都致力于认识自我和对自我的探索。③而在苏格拉底之后,古希腊由盛转衰,而古希腊哲学也开始更多关注自身的幸福和快乐,比如快乐主义的伊壁鸠鲁,比如禁欲主义的犬儒学派等等,都是这方面的代表。

 

当然,这一时期的“怀疑主义”其实也充满了时代的特征,那为什么皮浪要提出“悬置判断”呢?除了对客观世界和真理的不确定性以外,其实也是基于人的自身幸福和快乐出发的。怀疑主义认为,对于任何事物,只要我们去判断,不论好坏,都会让我陷入苦恼。人总是容易有自己的主观理论和想法,当我们自己做出判断和别人想法不一致时,我们就会陷入混乱和苦恼,这时内心的宁静就被打扰了,我们就会不快乐。

 

有一个著名的故事,有一次皮浪乘船出海,在海上遇到了风暴,大船颠簸很厉害,船上的人都惊慌失措,而只有皮浪若无其事,非常淡定。他指着一头同样若无其事的的小猪说:我们就应该像这头小猪一样,处变不惊。这个故事也阐述了皮浪的人生态度,不去想事情会往好的方面或者坏的方面想,不做任何判断和假设,这样,我们就不会有恐惧和担忧。

 

那皮浪为什么会提出这样的思想呢?皮浪的怀疑主义,一方面承认世界的不确定性,另外,也是对确定性的一种质疑。他的老师德谟克利特就提出了著名的“原子论”,认为世间万物都是由不可再分割的原子构成的,当然这里的“原子”和我们今天物理学的“原子”还不是太一样,因为德谟克利特认为,人的灵魂也是由原子构成的。而且在苏格拉底之前,古希腊的哲学主要是“本体论”,也就是对世间万物的本原提出某些观点,比如泰勒斯的“水本原论”、毕达哥拉斯的“数本原论”、巴门尼德的“存在论”等等,而这些思想的背后,是一种“决定论”的思维,认为这个世界存在某种唯一的确定性,要么由某种单一的东西构成,要么这个世界存在某种确定性的规律。但皮浪对他们的思想根基提出了挑战和质疑,这也是为什么皮浪说“不做任何肯定的结论,悬置判断”,这种怀疑主义精神,其实对后来的西方哲学思想的发展,带来了巨大的影响,比如笛卡尔的哲学思想,就是从普遍怀疑开始的,休谟的哲学思想也带有非常强烈的怀疑主义色彩等等。

 

虽然皮浪的怀疑主义,在当时还并没有形成较为系统的理论体系,只是奠定了怀疑派的基础,确立了思考方向和原则,但他把怀疑主义应用到了实践,因此,我们也称他的怀疑主义,是怀疑主义的实践性阶段,怀疑主义学派的这种对权威的挑战,对确定性的质疑精神,让后来的西方哲学思想也充满了辩证思维,让我们看待世界,多了一个角度和思维框架。

14 次浏览
7 次浏览

硬件也敏捷 – Yes, Hardware Can Be Agile!

近日翻译了这篇 2015 年由 Nancy Van Schooenderwoert 发表在 InfoQ 上的文章《硬件也敏捷》供大家参考。

PDF 版本下载:硬件也敏捷 – Yes, Hardware Can Be Agile!


 

硬件也敏捷!

Yes, Hardware Can Be Agile!

 

Nancy Van Schooenderwoert

Mar 18, 2015

原文:https://www.infoq.com/articles/hardware-can-be-agile/

 

作者简介

Nancy Van Schooenderwoert

President and Principal Coach at Lean-Agile Partners, Inc.

Nancy Van Schooenderwoert 是最早将敏捷方法应用于嵌入式系统开发的领军人物之一,她曾担任电子、固件和软件工程师、经理和顾问。她在苛求安全、高度监管的行业中引领了软件开发的敏捷变革活动,并教授现代敏捷方法,如 Mob 编程、敏捷硬件和精益开发方法。她曾在美国、英国和德国指导过敏捷团队。她是全球敏捷相关会议的定期演讲者。她是大波士顿地区首屈一指的敏捷用户组 Agile New England 的创始人和前任总裁。

 


 

没有人能够在硬件领域推动以两周为单位的循环迭代!当人们谈起敏捷方法在包含了硬件及软件产品开发时,第一反应都是类似的论调。然而,已经有一些团队,尝试将已有的可靠硬件开发理念与少量从敏捷软件中借鉴的新鲜思想结合,进而去应对——甚至已经解决了——进行硬件快速迭代的挑战。

 

敏捷运动的硬件根源

当今我们称之为测试驱动开发(TDD的方法,其起源正是来自 Ward Cunningham 在嵌入式系统方面的研究工作,以及在此过程中与他的伙伴 Kent Beck的协作过程。上世纪 70 年代,Ward 在一家研究机构中任职,当时他正致力于找出一条测试新型硬件的切实可行的途径。对此他曾回忆到:“在那个时代,微处理器是件新鲜事物。而我得出了这样一个结论:必须让机器告诉我们,它正在做什么。如果我们打算信任机器传递给我们的信息,那么这部分内容必须是非常简单的…… 自此之后,这个理念在我所做的每一项工作中都留下了它的影子。”同样地,Kent 的经验也折射出类似的本质。

我在某次早年的敏捷会议上与 Ward 结识。当时我十分好奇,是什么吸引了他,让他步入由 Kent Beck 倡导,后来称作“极限编程(XP”的敏捷过程。Ward 告诉我,在上世纪 80 年代,他与 Kent Beck 在 Tektronix 从事 Smalltalk 方面的工作时,发现如果将一些简单的实践经验结合在一起,就会将研发效能提高到令人惊艳的程度。

Ward 介绍到,当新的产品理念推进到了特定节点时,就需要面对公司内部的官僚流程。为了避免陷入到相应流程带来的文书工作之中,他们两位首先确定一个新颖的产品构思,然后推动另一个团队采用它。接下来,他们重复这个过程,做好新产品构思的快速迭代,并展示足够的可行性,从而使得其他人也开始认识到这一产品构思的价值,并促使其顺利通过内部官僚体系的流程。

结对、简洁、测试优先(Test First等后来演化为 TDD 的实践经验和概念就此诞生并发展。就这样,在硬件领域,基础的敏捷实践方法 TDD 便应运而生。

 

在敏捷专用集成电路(ASIC)设计中引入敏捷软件中的TDD理念

受到在软件开发中运用 TDD 的启发,硬件验证工程师 Neil Johnson 首先将其应用到他对 SoC(片上系统)的验证工作中。

为了便于理解验证工程师(Verification Engineer)的工作职责,我们可以观察下面的工作模型。在 SoC 开发过程中,验证工程师与电路设计师(Circuit Designer)并肩工作,采用与类似裁缝和设计师之间的工作模式。这幅图片展示了在设计并制作一套全新的服装时,所需的工作步骤。在最后一步里,只有裁缝完成了自己的工作,而且没有引入任何缺陷(Defects)的情况下,设计师才能够对设计方案进行问题修正。而如果裁缝使用了错误的织物,或是未能正确测量模特,那么设计师将不得不去应对这些由她的设计方案以外的缺陷所引出的问题。

 

1. 验证工程师的工作内容 —— 与时尚设计的工作模型的类比。设计师画出服装的设计图并选择织物。接下来裁缝测量模特身材,在设计图的基础上打样并缝制服装。最后设计师查看模特服装穿着的效果,并对其设计进行调试

2. 验证工程师与电路设计师协同工作时,需要做哪些事情。电路设计师首先使用 CAD 系统绘制电路,接下来明确标注组件数据。验证工程师编写测试代码,进行仿真设计并检查电路表现。最后电路设计师查看 SoC 电路表现,并对电路设计进行调试

 

同样,电子工程师负责设计电路;而验证工程师负责编写测试代码,并对 SoC 承载的功能进行确认,从而完成设计的验证工作。因此,如果验证工程师编写的测试代码中出现了 Bug,那么显然会对测试效果的可靠性造成影响。

如果验证阶段引入了新的 Bug,那么整个工作流程中就不得不增加额外的循环来查错和修复(返工),这将极大地增加硬件开发的时间。因此,Neil 希望弄清楚,如果在“编写测试代码”的阶段使用 TDD,那么与过去相比,他的验证工作能够变得有多“干净”。当 Neil 花费几周时间将 TDD 框架落实到位,而后又经过数周时间对 TDD 工作流进行了熟悉之后,他准备好了对采用 TDD 进行工作前后的情况进行评估和对比。

Neil 对使用 TDD 前后的结果进行了基准测试,他发现当使用 TDD 的 SVUnit 框架时,他的测试代码只出现了 1 个 Bug;而在使用传统 SoC 验证方法的时候,会出现 15 个 Bug。确实,这个结果只是基于一个项目数据样本,但依旧展现了令人惊讶的提升 —— 特别考虑到 Neil 本身就是一位经验丰富的验证工程师,这一研发效能的提升就更为明显。1

 

敏捷 IC 开发——每个 Sprint 阶段都产出可用硬件

德州仪器(Texas Instruments)德国分部的一位数字设计总监,对使用 Scrum 加速定制化 IC(用于闪存和铁电存储器 FRAM 2 )的交付非常感兴趣。在当时,德州仪器的一般交付周期为 12 至 36 个月,而面对这种特殊用途的 IC,其最短交付周期为 18 个月。但一个新客户对交付周期提出了更高的要求。那么,德州仪器将如何使用 Scrum 来加速 IC 开发呢?

在项目启动阶段,他们尝试寻找一位Scrum 教练,但却根本找不到熟悉 IC 领域的人选。不过,他们依旧确信,至少可以将部分 Scrum 或敏捷实践引入到他们的硬件研发工作中。

最终,这支团队选择采用以下敏捷实践:

  • 跨职能团队
  • 4周迭代
  • 采用相对估算
  • 每周进行2 至 3次站立会
  • 每个迭代周期都交付可用的硬件
  • 实施回顾

此时,关键的问题是,当芯片制造流程超过4周,而且并不在团队掌控之中的情况下,究竟如何在“每个迭代中”将可用的硬件交付给客户?除此之外,上述的其它敏捷实践则可以非常容易地运用到硬件工作中。

要想以增量方式工作,可以采用的一个方法,是交付经过编程的 FPGA(现场可编程逻辑门阵列) —— 它们具有与完整 IC 内部的每个 IP 块非常相似的表现(一般来说,每个 IP 块的内部循环时间是 6 至 12 个月)。团队有能力逐个交付已经完成的 FPGA,而且他们主动向客户建议采用这样的方法。不过,虽然这么做能够在一定程度上降低风险,但该外部客户依旧不希望不断被这些中间步骤打扰——他们只想看到完整的 IC。

由于不能与外部客户进行共同迭代,数字设计团队找到了愿意配合的内部客户:团队可以向德州仪器内部的软件工程团队交付 FPGA —— 其软件是在进行硅片制造前的最后一步。

接下来,数字设计团队开始逐月向软件工程团队交付其 FPGA。通过使用可用硬件原型,而不是仅仅停留在设计阶段的方式,团队让 Bug 充分暴露。最终实现了“每个为期 4 周的 Sprint 完成一份可用硬件”的目标,尽管只是通过将 FPGA 硬件作为 IP 块原型的方式。由此,数字设计团队将循环开发时间从每个 IP 块“6 至 12 个月”,降低为“每四周”。

那么,面向外部客户的交付周期时间是否也有得到了优化?不幸的是,由于外部商业原因,这个项目在完成前就被终止了,因此我们无法通过数据来了解外部的交付周期被缩短了多少。

除了有效减少 IP 块的周期时间外,敏捷实践还带来了其它一些意义重大的好处。数字设计团队在工作流程中需要面对一套全新的平台,仅该平台极大增加了不确定性和复杂度。如果他们使用瀑布模型的话,意味着在开始工作前需要先投入为期 12 个月的工作规范化。

领导数字设计团队的 Tobias Leisgang 发现,“规划扑克和相对大小估算,有助于工程师们快速找出沟通问题以及对范围的误解,同时也让整体工作更具有现实感,而不必经历为期数周的详细规划阶段。跨职能团队确保反馈回路得以缩短,我们不会遇到在制造出一块昂贵的硅片之后,测试工程师才发现模拟电路无法测试的情况。3

 

基于原型电路板的敏捷电气(Agile Electronics)

结合一些前期的规划,我们可以把迭代作为主要策略,特别是对于早期产品原型中的电路板和其它物理组件部分。施耐德电气(Schneider Electric)的一支跨职能团队开发了一套照明控制系统,他们在一个 Sprint 中,整合了 3 块电路板和告警灯。

该团队的领导者 Timo Punkka 4 ,介绍了他们的射频控制照明调光系统(RF-controlled light dimmer system)是如何在为期 4 周 Sprint 中演进的:

“开发团队由固件、电气、PCB 布局和塑料方面的设计者组成。原型产品包括三块 PCB 板、一套电源、一套控制单元、一套 UI 以及射频转换器。在第一个迭代过程中,只有电源相关的 PCB 板就绪了,而其它两块 PCB 板则通过通用原型板代替。在第二个迭代周期中,所有 PCB 板则均可用,到了 Sprint 结束的时候,用于塑料部分的快速原型也已到位。在 Sprint 回顾中,原型帮助我们体会到,如果我们选择了这一概念设计,那么产品外观的真实感受会是怎样。另外,固件提供了简单的开关功能。要想做到这一切,我们需要与原型制造商之间建立可靠的合作关系。在这个案例中,原型制造商同意,当按照先期约定的时间收到最终图稿后,就能够在次日交付制造好的硬件原型。

 

3. 为期 4 周的 Sprint 产出的调光系统原型。

 

这支团队使用负载弹簧连接器和磁铁,将每个模块连接到一起(类似 Mac 的电源连接器)以验证他们的想法。他们还试着将射频部分和天线构建在 PCB 尺寸的 SD 卡上。这两个实验,都涉及了大量的不确定性 —— 最终这些硬件都能够运行,但没有人能够预先确保这一点。

对团队来说,这些实践的结果推动了实验的前进,结合其它一些敏捷实践的支持,使之成为一次具有前瞻性的尝试:

  • TDD 确保他们能在早期就尝试测试并持续进行实验,而不会打破早期设计决策。
  • 模块化让他们能够分离出需要进行更多实验的区域。
  • 仿真使得及早进行实验成为可能,从而规避了针对实际目标进行实验带来的高昂成本。

Timo 观察到,实验或者说逐步达成(Emergence,在敏捷思维中处于最核心的位置。它与第一次就把事情做对的思路截然相反。通过在一次 Sprint 中构建一份原型,让硬件、软件和需求能够以并行的步骤逐步实现。

 

汽车行业的敏捷研发

Wikispeed 项目为我们带来了一部可以合法上路的汽车,它由志愿者打造,且团队每周都会对它进行修订。现在,消费者们就可以购买 Wikispeed 汽车,而且它的百英里油耗仅为一加仑!面对“需要数年之久才能够将一部新车模型投放到市场”的传统理念,Wikispeed 项目向其发起了挑战。Wikispeed 汽车的研发仅仅耗时 3 个月,而且志愿者们持续构建和销售它。该项目的核心策略是模块化。得益于稳定的接口,他们每周都会修订 8 个模块中的一个或几个并进行快速集成。这要归功于他们采用的交错迭代的方法,它支持在每个模块中并行地进行实验和学习。

 

4. Wikispeed 汽车分解视图,展现了它的 8 个模块。

 

另一个关键策略是在材料类型和数量方面的简洁性:“XM(极限制造,eXtreme Manufacturing)的核心实践是只使用那些能够在不超过一周的时间里进行低成本迭代(重新制作)的材料,并确保在实际中采用的材料和工艺的类型都尽可能少,以快速积累丰富的经验。” 5

Wikispeed 团队的领导者是在西雅图工作的软件咨询顾问 Joe Justice,这是一支由国际化志愿者组成的团队,他们使用敏捷技术来构建汽车。在 2008 年,Justice 注意到 Progressive Insurance X 大奖的信息——这是一项奖金额度达到一千万美元的评选,旨在探寻是否有可能构建出油耗低至 100MPG(英里 / 加仑)且符合道路安全规范要求的汽车。他参加了这一活动,并在博客上公布了自己的想法——于是一些读者加入了他的团队。

Joe 感到非常吃惊:有如此多的人,愿意努力且免费地工作,只是为了构建一些以开源形式存在的东西,并且能够让所有人受益。目前,已经有 23 个国家中出现了 Wikispeed 商店。其汽车售价大约为两万五千美元;消费者也可以选择花费一万美元,从套件开始自己搭建它。

Joe 从 Wikispeed 团队中获得的第二个关键认知是人们的动机。他表示,与其他志愿者相比, Daniel Pink 的思路在目标、掌握力和使命感方面都更胜一筹。他们积极主动地相互学习,并保持对彼此的关注。

Wikispeed 汽车由八个模块组成(引擎、框架、碳纤维车身等等)。得益于这些模块之间的松耦合关系,人们可以便捷地替换每一个部分。团队每周都会对八个模块中的一个或几个进行修订。并保证随时都有一部能够运转的 Wikispeed 汽车版本,同时还能够快速实现任何升级。那些已经拥有 Wikispeed 汽车的人们,则可以购入新的模块,并自己动手替换原来的对应部分,而无需任何汽车领域的技术背景。

在 Progressive Insurance X 大奖的竞赛中,Wikispeed 在核心组别获得了并列第十名的位置,将超过一百款来自全球各地、得到了企业和大学的充裕资金支持的汽车甩在了身后。6

Wikispeed 团队在运用“极限制造(XM)”过程中积累的经验,反映了这样一个现象:Joe Justice 和团队中的许多志愿者都具有敏捷软件开发的背景,他们把这一方向的背景知识引入到了 XM 中。当被问起在制造和工程领域运用敏捷方法的过程中,学到了哪些东西时,Joe 的回答是:

当我开始与 Jeff Sutherland 一起进行联合培养的时候,我建议工程师们使用面向对象的架构契约优先的设计以及测试驱动开发(TDD。我所学到的第一个要点,是 Jeff 告诉我,我需要在工程领域打造这些理念——对软件人士而言,这些都是显而易见的知识,但对工程师而言则必须经过翻译才能够掌握。

对于进行极限制造(XM):

第一步,也即是第一个 Sprint,将产品或服务划分为 10 个以内的松耦合模块。

第二步,明确面向对象架构、每个模块的输入输出、这些模块在物理上或通过数据如何互相联结、以及冷却等等方面的内容;但要注意,这个阶段应避免涉及任何模块内部的设计或架构。此时应只关注模块之间的接口

第三步,通过以团队方式对每个模块进行版本搭建和重建,进行迭代;同时以增量的方式从实践中学习。一旦他们开始进行概念验证工作,就会在每个模块中增加“illities(对非功能性需求的功能响应)”,就像持久性、可承受性等等。

大部分工程师团队都会遇到这样一个经典问题:他们希望提前做好全部架构工作——所有的接口及“箱子”(接口背后的子系统)中的全部内在。然而,这将产生巨大的前置时间并造成大量浪费。

还有部分团队希望甩开架构直接投入到具体工作中。这种方式起步固然迅速,但很快就会放缓节奏。

这两类团队都很少采用最佳的方式:预先进行架构设计,但只细化到接口层面,接下来停止架构工作,通过实践并构建箱子内部来学习。这似乎是产出可交付产品的最快方式,而且它能够保证持续的速度。

在一款复杂的物理产品中,最好综合运用一些迭代和并行方法。当工作性质中具有“发明部分”的时候,这一点尤为有用。以 Wikispeed 汽车为例,它的各个模块并行开发,错落添加各项变更,因此这些模块不会同时发生故障。这确保了团队每周都能够交付可用的软硬件。

 

总结

我们刚才已经浏览了一些硬件开发过程中使用的经典技术:

  • 仿真或原型设计(IC 开发领域与电气领域)
  • 模块化(电气领域与 Wikispeed 汽车)
  • 组成部分的简洁性(Wikispeed 汽车)

我们还看到了一些直接从敏捷软件实践中借鉴的内容:

  • TDD(ASIC 验证)
  • 短迭代(IC 开发、电气领域、Wikispeed 汽车)
  • 面向对象设计(Wikispeed 汽车)

长期以来,硬件领域一直向着更具灵活性的方向不断前进。部分硬件现已支持非常迅捷的变更,使得不必提前进行完整规划,即可让良好设计逐步浮现成为可能。而对于尚未达到这样灵活性水平的地方,我们会自然而然地混合运用模拟、模块化和预先规划。提前许久便锁定设计,是极具风险的策略,而综合运用这些技术则能够缓和这些风险。

这里还引出了另一个非常强大的缓和策略——众包(Crowdsourcing,或者称之为:调动团队的集体智慧)能够在一些关键的敏捷实践中发挥作用,例如回顾、结对编程、每日立会、故事编写等等。在任何一支运行良好的敏捷团队中,集体智慧都能够发挥强大的作用。

没错!要想在采用短迭代方式的同时,还保证每次迭代都能产出可用硬件,是件困难的事情。不过,我们刚才讨论了一些有助于团队落实这一方式的策略:

  • 经典的硬件开发技术,例如仿真和模块化,能够极大提高灵活性和开发速度。
  • 可以将诸如 TDD 和短迭代等敏捷软件实践,引入硬件开发工作中,从而加速实验和学习的过程。
  • 有时候我们需要超越项目要求的范围(例如,在一个月内完成 ASIC)去看问题,并满足客户(或是内部客户)更大的需求。激发团队智慧将帮助我们获得达成目标所需的创造力。

借助我们手中的这些思维工具,硬件绝对可以变得敏捷

 


 

参考文献

1      JohnsonNeil;测试驱动与硬件验证的新范式(TDD and A New Paradigm for Hardware Verification);Agile 2014 大会,可前往以下网址阅读: http://www.AgileSOC.com

2      LeisgangTobias;如何与足球队一道打篮球(How to Play Basketball With a Soccer Team,论文);Agile 2012 大会

3      Tobias Leisgang,德州仪器德国慕尼黑分公司系统工程经理;由作者进行采访。

4      PunkkaTimo:嵌入式敏捷(Embedded Agile,论文);发表于 2010 年嵌入式系统大会

5      http://wikispeed.org/category/wikispeed_shop_manufacturing/

6      DenningSteveWikispeed:如何在三个月内开发一部 100MPG 汽车(Wikispeed: How a 100 mpg Car Was Developed in 3 Months);福布斯杂志 20125 月,可前往以下网址阅读 http://www.forbes.com/sites/stevedenning/2012/05/10/wikispeed-how-a-100-mpg-car-was-developed-in-3-months/

23 次浏览

中华人民共和国行政区划代码 Excel 整理版

根据中华人民共和国民政部 2020 年 12 月发布的“中华人民共和国县以上行政区划代码”(https://www.mca.gov.cn/article/sj/xzqh/2020/20201201.html),精心核对制作!

形式举例:

行政区划代码 行政区划代码(TEXT) 地区 省/直辖市/自治区 市 区县
210200 210200 辽宁省大连市 辽宁省 大连市
210202 210202 辽宁省大连市中山区 辽宁省 大连市 中山区
210203 210203 辽宁省大连市西岗区 辽宁省 大连市 西岗区
210204 210204 辽宁省大连市沙河口区 辽宁省 大连市 沙河口区
210211 210211 辽宁省大连市甘井子区 辽宁省 大连市 甘井子区
210212 210212 辽宁省大连市旅顺口区 辽宁省 大连市 旅顺口区

Excel Code for Administrative Divisions of PRC

下载地址: https://download.csdn.net/download/nista/85707643?spm=1001.2014.3001.5503

如何通过身份证号链接上述的 Excel 表格获取人员的籍贯?

  • A2 单元格为身份证所在单元格,注意,需要将其设置为 Text 文本 类型,例如,210102199001010011
  • B2 为身份证的前 6 位的行政区划代码,值为 =TEXT(LEFT(A2,6),0)
  • C2 为连接了上述 Excel Sheet 页 “China” 进行籍贯识别后的结果,将它的值设置为以下代码:
=IFERROR(
     VLOOKUP(
        B2,
        China!B:C,
        2,
        0
    ),
     CONCAT( "*(",
         IFERROR(
             VLOOKUP(
                CONCAT(
                    LEFT(
                        A2,
                        4
                    ),
                    "00"
                ),
                China!B:C,
                2,
                0
            ),
             IFERROR(
                 VLOOKUP(
                    CONCAT(
                        LEFT(
                            A2,
                            2
                        ),
                        "0000"
                    ),
                    China!B:C,
                    2,
                    0
                ), "<未找到>" 
            ) 
        ), ")" 
    ) 
)

这段代码会去查找行政区划代码对应的籍贯文字,如果无法找到,则回退到查找市县、如果依然无法查找到,则查询省。

7 次浏览

【攻击形式】打字节奏特征 Typing Rhythm Characteristics

有一种网络安全领域的攻击形式,是根据目标的打字节奏,猜测/辨识目标的身份,同时,也可以根据所输入字符速率(因为键盘的实际间距不同),可以统计猜测猜测出你输入的内容。我们称之为 Typing Rhythm Characteristics 打字节奏特征。

为了应对这类攻击,有一种统一的思路叫做“填充网络流量”(Traffic Padding) 你可以从 ISO/IEC 7498 – 2 这一文档中,了解到有关定义。

更多信息可以参考: https://axbom.com/keystroke-dynamics/

ISO/IEC 7498 标准整理下载: https://download.csdn.net/download/nista/85799713?spm=1001.2014.3001.5503

2 次浏览

通过 Git 提交代码时,如何为某一个文件增加可执行权限?

有时候我们可能会提交一些需要在远端(例如 Jenkins CI 服务器等)Shell 环境下执行的脚本文件,我们可以在提交代码时,通过 Git 命令进行权限的修改(即使是在 Windows 环境下)

git update-index –chmod=+x

如果你习惯使用 TortoiseGit 管理你的代码,你也可以通过以下方式添加文件的可执行权限

TortoiseGit Add As Executable

28 次浏览

回顾会议 Retrospective 流程化工作指南

回顾会议工作指南 (90 分钟版)

1 在会前,
1.1 查看远程/其他 Scrum 团队最近的 Retro 会议记录
1.1.1 发现其中的共性痛点
1.1.2 发现与本团队相关的表扬或改善点
1.2 查看近期 Retro 的行动项,与团队成员同步状态
1.3 查看 Retro Board,确定近期大家的反馈
1.4 与 SM 等团队关键相关方,确认本次 Retro 关注的主题、目的(设定基调)、Retro 会议类型

2 在会前准备完成,
2.1 完成定量数据的收集
2.2 召开地点、零食、画板、计时器
2.3 提前打开所需的 Scrum Board、Burndown Chart、Metrics、Bowler 等页面或文件

3 在会议开始时(3 mins)
3.1 在每一个阶段使用计时器
3.2 指定好第二主持人 / 记录员
3.3 快速地说明高效会议纪律、团队章程、回顾宣言
3.4 强调这是一个宽松、安全的时间

4 启发:展示本次迭代的所有 Epic / Story(3 mins)
4.1 跟进 Review Meeting,让大家进行短暂的回想

5 启发:开始定量分析(10 mins)
6 启发:分享其它数据(5 mins)
6.1 其他团队 Retro 的反馈
6.2 遗留的 Action Item 状态更新

7 启发:开始定性分析(10 mins)
7.1 心情曲线、Sprint 评分
7.2 建立并填充 Liked-Sad-Thanks-Sharing 板

8 团队投票,聚焦要解决的话题(Top 3 / Top 1)(10 mins)

9 发散:展开讨论(40 mins)
9.1 随时记录可能的行动项、想法、问题
9.2 通过引导式提问充分讨论:通过 5 Why、苏格拉底式提问,引导团队寻找原因
9.3 促成收敛:得出结论

10 明确高优先级行动项(5 mins)
10.1 创建相关任务或承诺提供时间

6 次浏览

Azure VM 虚拟机的系统盘占用空间过大,如何裁剪节省成本?

我们在创建 Azure 云的 VM 虚拟机时,其自带的系统盘(C:)往往占用了过大的空间,产生很多额外的开销,我们可以通过如下的 PowerShell 脚本进行空间的裁剪,以优化节约花费!

具体操作步骤,请参考: https://jrudlin.github.io/2019-08-27-shrink-azure-vm-osdisk/

Shrink Azure VM System Disk PowerShell Script:

# Variables
$DiskID = "TODO: 形式如同: /subscriptions/203bdbf0-69bd-1a12-a894-a826cf0a34c8/resourcegroups/rg-server1-prod-1/providers/Microsoft.Compute/disks/Server1-Server1"
$VMName = "TODO: Azure VM NAME VVVVVV"
$DiskSizeGB = 32
$AzSubscription = "TODO: Azure Subscription NAME SSSSSS"

# Script
# Provide your Azure admin credentials
Connect-AzAccount

#Provide the subscription Id of the subscription where snapshot is created
Select-AzSubscription -Subscription $AzSubscription

# VM to resize disk of
$VM = Get-AzVm | ? Name -eq $VMName

#Provide the name of your resource group where snapshot is created
$resourceGroupName = $VM.ResourceGroupName

# Get Disk from ID
$Disk = Get-AzDisk | ? Id -eq $DiskID

# Get VM/Disk generation from Disk
$HyperVGen = $Disk.HyperVGeneration

# Get Disk Name from Disk
$DiskName = $Disk.Name

# Get SAS URI for the Managed disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName -Access 'Read' -DurationInSecond 600000;

#Provide the managed disk name
#$managedDiskName = "yourManagedDiskName" 

#Provide Shared Access Signature (SAS) expiry duration in seconds e.g. 3600.
#$sasExpiryDuration = "3600"

#Provide storage account name where you want to copy the snapshot - the script will create a new one temporarily
$storageAccountName = "shrink" + [system.guid]::NewGuid().tostring().replace('-','').substring(1,18)

#Name of the storage container where the downloaded snapshot will be stored
$storageContainerName = $storageAccountName

#Provide the key of the storage account where you want to copy snapshot. 
#$storageAccountKey = "yourStorageAccountKey"

#Provide the name of the VHD file to which snapshot will be copied.
$destinationVHDFileName = "$($VM.StorageProfile.OsDisk.Name).vhd"

#Generate the SAS for the managed disk
#$sas = Grant-AzureRmDiskAccess -ResourceGroupName $resourceGroupName -DiskName $managedDiskName -Access Read -DurationInSecond $sasExpiryDuration

#Create the context for the storage account which will be used to copy snapshot to the storage account 
$StorageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName -SkuName Standard_LRS -Location $VM.Location
$destinationContext = $StorageAccount.Context
$container = New-AzStorageContainer -Name $storageContainerName -Permission Off -Context $destinationContext

#Copy the snapshot to the storage account and wait for it to complete
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $destinationVHDFileName -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $destinationVHDFileName -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state

# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName

# Emtpy disk to get footer from
$emptydiskforfootername = "$($VM.StorageProfile.OsDisk.Name)-empty.vhd"

# Empty disk URI
#$EmptyDiskURI = $container.CloudBlobContainer.Uri.AbsoluteUri + "/" + $emptydiskforfooter

$diskConfig = New-AzDiskConfig `
    -Location $VM.Location `
    -CreateOption Empty `
    -DiskSizeGB $DiskSizeGB `
    -HyperVGeneration $HyperVGen

$dataDisk = New-AzDisk `
    -ResourceGroupName $resourceGroupName `
    -DiskName $emptydiskforfootername `
    -Disk $diskConfig

$VM = Add-AzVMDataDisk `
    -VM $VM `
    -Name $emptydiskforfootername `
    -CreateOption Attach `
    -ManagedDiskId $dataDisk.Id `
    -Lun 63

Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

$VM | Stop-AzVM -Force


# Get SAS token for the empty disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Access 'Read' -DurationInSecond 600000;

# Copy the empty disk to blob storage
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $emptydiskforfootername -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $emptydiskforfootername -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state

# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername

# Remove temp empty disk
Remove-AzVMDataDisk -VM $VM -DataDiskNames $emptydiskforfootername
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

# Delete temp disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Force;

# Get the blobs
$emptyDiskblob = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $emptydiskforfootername
$osdisk = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $destinationVHDFileName

$footer = New-Object -TypeName byte[] -ArgumentList 512
write-output "Get footer of empty disk"

$downloaded = $emptyDiskblob.ICloudBlob.DownloadRangeToByteArray($footer, 0, $emptyDiskblob.Length - 512, 512)

$osDisk.ICloudBlob.Resize($emptyDiskblob.Length)
$footerStream = New-Object -TypeName System.IO.MemoryStream -ArgumentList (,$footer)
write-output "Write footer of empty disk to OSDisk"
$osDisk.ICloudBlob.WritePages($footerStream, $emptyDiskblob.Length - 512)

Write-Output -InputObject "Removing empty disk blobs"
$emptyDiskblob | Remove-AzStorageBlob -Force


#Provide the name of the Managed Disk
$NewDiskName = "$DiskName" + "-new"

#Create the new disk with the same SKU as the current one
$accountType = $Disk.Sku.Name

# Get the new disk URI
$vhdUri = $osdisk.ICloudBlob.Uri.AbsoluteUri

# Specify the disk options
$diskConfig = New-AzDiskConfig -AccountType $accountType -Location $VM.location -DiskSizeGB $DiskSizeGB -SourceUri $vhdUri -CreateOption Import -StorageAccountId $StorageAccount.Id -HyperVGeneration $HyperVGen

#Create Managed disk
$NewManagedDisk = New-AzDisk -DiskName $NewDiskName -Disk $diskConfig -ResourceGroupName $resourceGroupName

$VM | Stop-AzVM -Force

# Set the VM configuration to point to the new disk  
Set-AzVMOSDisk -VM $VM -ManagedDiskId $NewManagedDisk.Id -Name $NewManagedDisk.Name

# Update the VM with the new OS disk
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

$VM | Start-AzVM

start-sleep 180
# Please check the VM is running before proceeding with the below tidy-up steps

# Delete old Managed Disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $DiskName -Force;

# Delete old blob storage
$osdisk | Remove-AzStorageBlob -Force

# Delete temp storage account
$StorageAccount | Remove-AzStorageAccount -Force