写工业级别代码是种怎样的体验？
喜欢这篇文章的话，我还有一个续篇：大型公司开发软件的流程是怎样的？ ，是我近些年的思考。欢迎交流指导。 ----------------------------------原答案----------------------------------------------- 答主混迹在美帝汽车软件行业，在两家一级零部件供应商的软件部门工作过。曾经合作过的整车厂包括通用、福特、克莱斯勒、现代和大众，目前在帮助一家硅谷初创公司完成他们的电动汽车项目。在这里，答主简要介绍一下汽车工业中关键安全部件(Safety CriticalParts)的控制软件是怎么写出来的。 所谓“关键安全部件”，通俗点说就是出了问题要死人的部件，比如发动机、变速箱、转向系统、安全气囊、刹车系统等等。与之相对的非关键部件，则包括电动车窗、空调系统、音响系统什么的。试想一下，发动机软件出了故障也许会导致汽车爆冲（为凯美瑞默哀…），安全气囊软件不工作或乱工作，更是会直接导致驾驶员伤亡（高田掩面而去…）。相比之下，车窗不升降或者乱升降、音响无法控制什么的，一般并不会导致交通事故。所以，关键安全部件的软件设计要比非关键部件严苛得多。 一、行业标准 关于关键安全部件的软件设计流程，有两个公认的行业标准，即aSPICE （Automotive Software Process Improvement and CapabilityDetermination） 和 功能安全标准ISO26262中的软件部分（(Road vehicles – Functional safety - Part 6）。几乎所有整车厂和绝大部分汽车零配件供应商都参考这两个标准来制定自己的软件设计流程。而越来越多的整车厂——比如德国大众——采购零部件的基本门槛之一就是供应商的软件设计流程通过了aSPICE认证。 图1. aSPICE 流程  （资料来源：http://image.slidesharecdn.com/webinarautomotivespice3-150731093914-lva1-app6892/95/automotive-spice-30-was-ndert-sich-9-638.jpg?cb=1452240786） 根据aSPICE流程，软件开发过程可以被拆分为需求分析——架构设计——单元设计——单元测试——软件集成和集成测试，以及最后的软件综合测试，每个步骤依次进行。学过软件工程的人会认为，这就是老掉牙的“瀑布式（water fall）”开发流程嘛，但其实aSPICE与瀑布式流程是不相同的，这个我们后面会提到。 图2. ISO26262 – 第六部分 （ 资料来源：Achieving ISO 26262 Compliance with Reactis: An ISO 26262 Process using Reactis） 再来看看ISO26262。额…这个跟aSPICE也太像了吧。话说我也没有考证过它俩是谁借鉴谁，总之几乎就是一样的。比如图1的SWE.1对应图2的Step 6；SWE.2对应Step 7…… 也许是因为它们太相像了，在业内我们一般统称为“V”型开发流程 （The V Model）。 现在来说说这个V型开发流程和瀑布式流程有什么不同，或者说，它有什么独特之处。答主个人认为，V型流程有两个最大的特点： 1. 同一组责任人负责同一个层级的开发和验证。从图2可以看出，需求文档和需求验证相对应，是第一层级；架构设计和集成测试相对应，是第二层级；单元设计与单元测试相对应，是第三层级。V型流程要求，同一层级的开发与测试，由同一个（或一组）责任人负责。比如，架构师除了参与架构设计，还要制定集成测试用例 （Test Case），并且在集成测试完成后审阅测试报告；又如，参与单元设计的工程师除了写代码，还要写单元测试用例，并且审阅单元测试报告。 这样种流程的设计是为了最大限度的保证软件被可靠而完整的验证，降低由沟通不畅而引发设计缺陷的可能。 2. 每个步骤的实现和验证都具有严格的可追溯性。具体而言，就是在最终发布的代码中任意选择一个语句，都能够快速地确定它是实现哪项软件设计，对应哪一条软件需求，进而对应哪一条客户需求；另一方面，这条语句被哪个单元测试用例测试过，进而又对应哪条集成测试用例，也必须是清晰明了的。而所有这些信息还必须包括对应的责任人、审阅人、日期及必要的备注。 这样设计流程的目的，一方面是为了在团队高流动性的情况下，新人能够快速、准确的上手；更重要的是为了在出现问题以后，能够快速而全面的做出故障分析，得出故障的根本原因。 当然啦，严格遵守这些流程的缺点就是…项目开发中至少一半的时间都用在维护文档上了…没办法，谁叫我们是能决定驾驶员身家性命的程序员呢。 二、团队组成 在涉及具体的软件开发流程以前，答主觉得有必要先介绍一下典型的汽车行业软件开发团队的组成。前面已经介绍过行业标准流程了，那么开发团队自然是按照标准流程来配备的： 图4.典型的软件部门组成 -       需求工程师（Req. Engr.）负责分析和撰写软件需求，对应aSPICE的SWE.1; -       架构师(Architect) 负责软件架构设计，明确各个单元之间的接口，并且负责操作系统的配置和调度对应SWE.2 -       基础软件工程师(Base SW Engr.)负责硬件和底层驱动，应用软件工程师（App. Engr.）负责应用层软件的开发，他们都对应SWE.3 -       集成工程师（Integration Engr.）负责系统集成和测试，对应SWE.10 -       测试与验证工程师（Test/Validation Engr.）负责所有的测试，对应 SWE.9, SWE.10, SWE.11 等等，答主你刚不是说V型开发流程里同一层级的设计和验证，要由同一人负责吗，咋突然冒出来这么多工程师！事实上，图4介绍的团队组成和V型开发流程的特点并不冲突： 虽然工程师分工各有侧重，但在同一层级中，一定是有一个工程师总体牵头负责的。在一些企业中，架构师和集成工程师更是习惯由同一人兼任。对于基础/应用软件开发工程师而言，工程现实决定了在大多数项目里，他们不可能做到既写代码又写全部的测试用例。这时候，就需要由开发工程师确定测试的“大纲”，由测试工程师来完成具体测试用例的编写和执行。开发工程师只需要审阅测试报告就可以了。 图4介绍的只是一种典型的团队组成。不同企业根据自身特点，有不同的团队结构，但万变不离其宗。而应用层工程师还可以进一步细分为控制工程师、通信工程师和故障诊断工程师等等，这里就不展开了。 从具体项目的角度，还可以列出另一种人员组成结构。 图5.典型的软件开发团队 只要满足了图5的人员配置，基本上项目就可以开工了。在实际工作中，有些企业的项目组长(Proj. Lead) 是专职的，另一些企业则是由某个工程师兼任。一般而言，架构师或者集成工程师兼任项目组长的情况比较多，控制工程师兼任的也不少。 除了上述工程师以外，要完成整个软件开发，还需要质量工程师、安全工程师、系统工程师等等。由于这些人员往往并不只为软件部门服务，所以这里就不介绍了。很多企业——特别是美企——软件团队里还会有很多不懂技术，只负责财务、客户沟通以及监督进度的项目经理（PM）。这些人往往都是职场老油条，答主和他们相爱相杀了好多年，这里也不细说了。 三、工业级别的代码是怎么写出来的 卧槽终于写到正题了。答主有一种前面离题万里的感觉有没有。 图6.软件开发流程 首先上场的是需求工程师。需求工程师需要重复阅读客户的需求文档（Customer Req.）以及系统需求(System Req.)，对于其中不明了或者有歧义的地方，要和客户以及其他工程师充分沟通，最终写出一份软件需求文档(SW Req.)。 如果这是一个全新的软件，那么软件组的大牛们会聚在一起讨论设计的可行性，然后进行一个叫dFMEA (DesignFailure Mode and Effects Analysis，设计失效模式及后果分析）的东西。事实上，由于汽车软件行业已经有了好几十年的发展，基本上没有什么软件是“全新”的、总有前代产品可以借鉴，也总有已完成的细节设计和dFMEA可供参考，所以这个过程是不算太长的。如果是既有软件的版本更新，那么dFMEA就可以暂时省去了，只用进行可行性分析和影响分析。 （答主在这提一句，这也是为什么“自动驾驶”进程如此缓慢的重要原因。因为自动驾驶软件真的是没有任何东西可以借鉴，完全是start from sketch） 有了需求文档，项目组长和架构师（很多时候是同一个人…）就开始根据需求文档规划一个软件变更（Change Request），并且把这个变更，根据不同分工和性质细分成很多个小的任务（sub-task），并分配给各个基础软件工程师和应用软件工程师，然后工程师们就开始讨论软件设计啦。对于每一项设计变更，不少企业会进行DRBFM (DesignReview Based on Failure Mode) 来确保设计的可靠性。 完成了软件设计以后，工程师们就开始编程。现在越来越多的企业开始使用自动代码生成工具（TargetLink, Rhapsody等等），只要设计好逻辑，就可以自动生成出高质量的代码。 有了代码之后，这些工程师需要和测试工程师配合，完成一系列的单元测试，包括静态测试（MISRA）、动态测试、模型在环测试（MIL）、软件在环测试(SIL)、处理器在环测试(PIL) 等等，然后进行同行评议（Peer Review），等这些测试都通过了，再整理好文档，每个小任务的单元测试也就完成了。 每个小变更都通过测试，并不意味着它们组合到一起能够通过测试。这时候就要集成工程师把所有小变更集成起来，行成一个可编译的整体软件，这个集成了所有小变更的软件一般叫做“0级软件”，也就是没有任何保障的软件。 随后测试工程师再次登场，在集成工程师的配合下对“0级软件”进行集成测试，包括回归测试（Regression Test）、硬件在环测试（HIL）、故障注入测试（Fault Injection Test）等等等等。如果全部通过，就会被下载到汽车上，进行上车测试（In Vehicle Test），最终在满足了一系列严苛的条件后，成为可以最终送到客户手中的发布软件。 于是一长段工业级的代码就诞生了。 上文所叙的只是最理想情况下的软件开发流程。工程实际要比这个复杂得多，比如客户翻来覆去改需求啦，工期无故被缩短啦，测试出了大问题要返工啦，甚至已发布的软件在消费者手里出了问题需要召回啦等等。如何解决这些问题，就不是一两千字能说得清楚的了，在这里且略过不表。 一句话总结，答主的理解是，工业级的代码就是按照行业公认的标准、经过了严谨的分析、具有详细的文档、通过了严苛的测试的代码。  如果你觉得我这个答案不错，也许你会喜欢我的专栏：汽车软件工程师生存手册