对抗软件复杂度的战争
一、何为研发效能?
二、本质复杂度和偶然复杂度
三、复杂度的爆炸
原本方案只需要直接改动系统 A,但由于负责系统 A 的团队并没有解决该问题的动力,其他人不得不绕道去修改系统 B,C,D 来解决该问题。
原本方案只需要直接改动系统 A,但迫于系统 B 负责人或者上司的压力,方案不得不演进成同时改 A,B,甚至引入 C。
四、错误的应对方式
五、正确的技术战略
六、微观层面的复杂度控制
function emailsForCustomers(customers, goods, bests) {var emails = [];for(var i = 0; i < customers.length; i++) {var customer = customers[i];var email = emailForCustomer(customer, goods, bests);emails.push(email);}}function biggestPurchasePerCustomer(customers) {var purchases = [];for(var i = 0; i < customers.length; i++) {var customer = customers[i];var purchase = biggestPurchase(customer);purchases.push(purchase);}}
对于这种到处可见的逻辑,即遍历集合的每个元素,对其中每个元素做一些处理,返回一个新元素,最后装成一个新的集合,可以被抽象成一个 map 函数。在这个例子中,我假设 JavaScript 支持 map 函数,那么上面的代码可以写成:
function emailsForCustomers(customers, goods, bests) {return map(customers, function(customer) {return emailForCustomer(customer, goods, bests);});}function biggestPurchasePerCustomer(customers) {return map(customers, function(customer) {return biggestPurchase(customer);});}
抛开语言语法的因素不谈,这段代码除了这个 map 函数,剩下的就是函数名了,而函数名只要是命名得当的,那它其实就是本质复杂度,就是业务逻辑。行业先辈,大家耳熟能详的 Martin Fowler、Kent Beck、Robert C. Martin,无不在他们的书籍中强调命名的重要性,都是希望代码能够清晰地沟通意图,而这里最核心的意图应当是与问题域匹配的。
It works
It is easy to understand
It is safe to change
低质量的单元测试:包括不写 assert,到处是 print 语句,要人去验证。
不稳定的单元测试:代码是好的,测试是失败的,测试集无法被信任。
耗时非常长的单元测试:运行一下要几十分钟或者几小时。
用代码生成单元测试:对不起,我认为这个东西除了提升覆盖率虚荣指标外,毫无意义。
七、软件道德观
八、系统架构对复杂度的影响
不同子系统对于同一个概念有不同的名称,交互的时候会涉及各种翻译。
不同子系统承担了同一个实体的部分概念,导致修改的时候需要大范围一起修改,且容易出错。
更高的运维成本。
小结
我曾经的大老板郭东白曾在一次 QCon 的演讲中讨论优秀架构师的特质,除了大家都很好理解的有眼光、善于思考、能感召等几个特质外,还特别强调了“有良知”,他说:
Why choose Domain-Driven Design? 这篇文章清晰地解释了本质复杂度和领域驱动设计的关系。 《人月神话》-「没有银弹」一篇阐述了本质复杂度和偶然复杂度的概念。 《The Lean Product Playbook》- 本书的第2章清晰地解释了 Problem Space 和 Solution Space。 Wardley Map - 分析技术战略的绝佳工具,合理地选取商业产品可以帮助降低系统复杂度。 Grokking Simplicity - 在微观层面,使用函数式的思维降低软件复杂度。 Design Docs at Google Conway’s Law 警惕复杂度困局:关于软件复杂度的思考
阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。