读《软件架构师应该知道的97件事》

正文之前
  • 软件架构师是既要精通开发技术和软件平台,又要熟悉客户的业务。优秀的软件架构师应该同时掌握业务知识和技术能力

1. 客户需求重于个人简历
  • 作为工程师,我们常常要向客户推荐技术、手段,甚至方法论来解决问题。但有时我们心里不是想寻求解决问题的最佳方案,而是希望借此丰富自己的简历。这样做很可能得不偿失
  • 积累一批满意的客户,选择切合实际的技术解决他们的难题,让他们乐于推荐你,才是最好的履历。信誉远胜过时髦的编程技巧和流行的范式。掌握最新的技术趋势,与时俱进固然重要,但不能让客户为此买单。作为架构师,职业操守绝不能忘。公司托付重任给你,是期望你恪尽职守,不受利益诱惑。如果你觉得项目不够尖端,挑战性不足,无法满足职业发展的需要,大可另栖高枝,另谋高就
  • 把客户的长远需求摆在自己的短期利益至上,才能立于不败之地
2. 简化根本复杂性,消除偶发复杂性
  • 偶发复杂性(accidental complexity)是人们解决根本复杂性(essential complexity)的过程中衍生的
  • 许多软件框架和厂商提供的“解决方案”都表现出偶发复杂性的症状
  • 设计过度的框架增加的复杂性反而超过了它应该缓解的复杂性
  • 开发人员应该解决问题,而不是解谜取乐。在大型软件项目中,关注根本复杂性,消除偶发复杂性,抽丝剥茧制订解决方案,才是真正的挑战
  • 架构师的责任在于解决问题的根本复杂性,同时避免引入偶发复杂性
3. 关键问题可能不是出在技术上
  • 一旦项目失败,技术往往沦为替罪羊
  • 大多数项目是由人完成的,人 才是项目成败与否的基础
  • 学会尊重他人,给予团队成员充分的信任,是聪明的架构师获得成功必须掌握的核心技能
  • 几个显著改善对话效果的技巧:
    • 不要把对话当对抗
      • 如果你能看到他人的有点,并把沟通视为请教问题的机会,就会有所收获,同时也能避免引起对方的戒备之心
    • 不要带着情绪与人沟通
    • 尝试通过沟通设定共同的目标
  • 首先与同事达成一致的目标,把处理冲突和矛盾的过程视为学习的机会,控制住自己的情绪,那么每次沟通都会有所收获,你会做得越来越好
4. 以沟通为中心,坚持简明清晰的表达方式和开明的领导风格
  • 如果开发人员对项目蓝图和决策过程一无所知,必定会产生隐患
5. 架构决定性能
  • 架构是决定应用性能的根本因素
6. 分析客户需求背后的意义
  • 架构师可以通过询问客户,分析客户妖气的功能和需求的真正意义,定位真正的问题,从而提出比客户的建议更好、成本更低的解决方案。通过关注问题的真正含义,理顺需求的轻重缓急:把最有价值的需求摆在第一位
7. 起立发言
  • 有经验的架构师都很重视“推销”自己的想法,也明白有效沟通的重要性
  • 在两人以上的场合发表意见时,请站起来
  • 当你站起来时,无形中增添了一种权威和自信,自然就控制了场面。听众不会轻易打断你的发言。这些都会让你的发言效果大为改观
8. 故障终究会发生
  • 自动化虽然降低了主动犯错的概率,却增加了错误被忽略的概率
  • 任何自动化系统应付环境变化的能力都比不上人类
  • 应当承认系统中必然存在着不同形式的故障隐患,无论如何都无法彻底消灭
  • 如果不事先设计好防范故障的模型,就无法应对威胁系统安全的意外情况
9. 我们常常忽略了自己在谈判
  • 工程师总是想尽办法寻求合作,谈判者则绞尽脑汁占得先机。谈判时,决不能在对方的第一个要求上妥协让步
10. 量化需求
11. 一行代码比五百行架构说明更有价值
  • 没有天生完美的设计,所有设计都要在实现的过程中逐步完善
12. 不存在放之四海而皆准的解决方案
13. 提前关注性能问题
14. 架构设计要平衡兼顾多方需求
15. 草率提交任务是不负责任的行为
  • 草率提交任务会破坏正常的工作流程,是不负责任的行为
  • 以维护流程通常为重,以浪费他人时间为耻
16. 不要在一棵树上吊死
17. 业务目标至上
  • 架构师必须谨慎的站在业务团队一遍,拒绝开发团队选用价格不菲的软件和售后服务成本过高的技术
  • 如果技术决策脱离了业务目标和现实条件的约束,则无异于用宝贵的稀缺资源进行高风险的投机
  • 用业务目标驱动项目开发,才能保证软件开发团队的长远利益
18. 先确保解决方案简单可用,再考虑通用性和复用性
  • 许多用来实现基础设施的代码,包括组件框架、类库、基础服务,普遍存在一个问题,他们的设计一味强调通用性而不考虑具体应用,导致出现许多令人困惑的可选项和不确定的因素,这些功能常常不是被闲置,就是被误用,甚至毫无价值
  • 通用性来自对需求的理解,理解之后才能简化
  • 提炼通用性可以使我们更加接近问题的本质,通过分析已有案例可以获得清晰、简洁、有依据的规律和方法
19. 架构师应该亲力亲为
  • 优秀的架构师应该有能力发现问题所在,召集团队成员,向大家解释问题产生的原因,或者给出巧妙的解决方案,而不是寻找替罪羊
20. 持续集成
  • 应该在项目中鼓励推广持续集成的方法和工具
21. 避免进度调整失误
  • 导致项目失败的原因很多,最常见的是中途临时调整进度
  • 一般人有一种错误的观念,认为加快进度可以降低成本,提高交付速度
    • 仓促决定的进度会导致拙劣的设计、蹩脚的文档,可能引发质量问题。导致用户拒绝验收
    • 仓促完成的代码,会直接导致最终产品的bug数量增加
    • 紧张的测试进度会导致测试不充分,直接增加测试中可能出现的问题
    • 以上几项都会引发产品质量问题,而解决产品质量问题的代价更高
    • 最后的结果是成本不降反升,通常项目就是这样失败的
22. 取舍的艺术
  • 架构师应该明白鱼和熊掌不可兼得的道理
  • 妄想实现所有需求,只会产生脆弱的一无是处的架构
23. 打造数据库堡垒
  • 创建牢固的数据模型要从第一天开始,这绝非言过其实
  • 尽管业务规则和用户界面经常变化,但是采集来的数据的内部结构和关系通常不会变化。因此,通过正确分析,首先从结构上定义好数据模型非常关键。
  • 将数据原封不动的从一种模型迁移到另一种模型是非常困难的,不但耗费时间,而且容易出错
  • 在不断变化的应用层中,bug无处不在,不会因为你的勤奋而消失
  • 数据库是保护珍贵数据的最后一道关卡。应用层的设计经常变动,无法保证自身的安全。为了妥善的保护数据库,数据模型的设计必须做到能够拒绝无效数据,阻止无意义的关系
24. 重视不确定性
  • 如果抱着头脑中最先闪现的想法不放,被它束缚,偶然性决策就会占上风,软件的灵活性会降低
  • 架构属于设计范畴,但并非所有的设计都属于架构之列。架构代表了那些形成系统的重要设计决策。其重要性由变更决策的代价来衡量
  • 如果出现两个合理的选择,架构师应该停下来,设法找出介于两者之间的、具有更低重要性的决策,而不是简单的在两者中做出选择。了解两者之外还存在其他选择(这个选择并非显而易见),比决策结果本身更有价值
  • 开快车又不愿回头看是会拐错弯的
  • 迫于压力,人们常常为了决策而决策
  • 当你在不同的系统开发路线之间举棋不定时,不要急于做出决策。推迟决策,知道掌握更详实的信息,以便做出更可靠的决策。但也别太迟,要赶着这些信息失效前利用它们
25. 不要轻易放过不起眼的问题
  • 在没有十足把握的情况下,没人愿意冒险与多数人作对
  • 每个团队成员关注的侧重点不同。通常大家关心的是个人职责,而不是项目的整体目标
  • 每个人身上都存在自己难以识别和接受的盲点和不足
  • 自己的盲点自己难以察觉。忠言虽然逆耳,却是你最宝贵的财富
26. 让大家学会复用
  • 人们不会寻找不知道的东西
  • 如果大家找不到可复用的资源,或者不知道如何使用这些资源,人的天性就会发挥作用: 他们会自己动手实现
27. 架构里没有大写的”I”(There is no ' I' in arch itecture)
  • 辩解容易,难的是学会停止辩解;恃才傲物容易,难的是拥有自知之明
  • 驱动架构的是需求,不是架构师,你的任务是竭尽所能满足需求
  • 同事不仅是资源,也是你的设计伙伴和安全网。不被赏识的人是不会勤奋工作的
  • 架构属于团队,不是你一个人的
28. 使用”一千英尺”的视图
29. 先尝试后决策
  • 对同一个问题尝试两种或两种以上的解决方案,比直接决策然后动手实现的工作量要大。但是,如果事后发现仓促决定的方案不合适,架构师将陷入进退两难的困境: 无论是放弃目前的方案还是继续开发,都会造成误工和损失。更糟糕的情况是没人发现方案不合适,在这种情况下,甚至察觉不到损失。
  • 尝试多种解决方案可能是代价最低的选择
30. 掌握业务领域知识
31. 程序设计是一种设计
  • 程序设计是学习的过程,而不是生产和建造的过程
  • 在软件工程里,与传统行业的设计文档具有相同地位的只有源代码。软件的生产则是自动化的,由编译器、构建工具和测试代码共同完成
  • 程序设计属于设计范畴,而不是生产范畴
32. 让开发人员自己做主
  • 当你发现同事遇到麻烦时,可以主动给出建议。但更可取的做法是创造良好的氛围,让大家主动向你征求意见
33. 时间改变一切
  • 如果不选择做正确的事情,会为将来埋下隐患。随着时间流逝,我们勤勤恳恳、兢兢业业的完成了给定的任务,最后却发现做的毫无意义
  • 漂亮的解决方案搭配正确的任务,才能经受时间的考验
  • 时间让我们的行为变得可笑。越早醒悟越好,别再自以为是
  • 别跟以前的工作过不去
  • 学着接受以前的工作吧,克制自己回过头去修改的冲动
34. 设立软件架构专业为时尚早
35. 控制项目规模
  • 软件项目的交付目标表现为一组需求,需求定义了软件的功能和功能的质量。不能为客户创造价值的需求应该遭到质疑。
  • 如果实现一项需求不能为公司带来收益,就应该放弃
  • 缩小项目规模通常会降低架构的复杂性,这是架构师提高成功机率最有效的途径
36. 架构师不是演员,是管家
  • 架构师的职责和管家类似,承担着管理他人资产的责任。所以架构师应该尽可能为客户的利益着想,不能存有私心
  • 客户之所以允许别人动用自己的资金,是为了获得满意的投资回报
  • 架构师不是演员,是管家。别忘了,你花的是客户的钱
37. 软件架构的道德责任
38. 摩天大厦不可伸缩
  • 无论是开发新项目,还是替换已有的系统,都应该逐个部署系统组件
39. 混合开发的时代已经来临
  • 在分布式系统领域,晦涩难懂的二进制协议已经不再是提高效率的前提条件
  • 新的技术变革正逐步瓦解我们以往积累的技术成果,架构师将面临更大的挑战。我们应该拥抱这种变化,跳出原有的思维模式,充分挖掘软件的多元化特征
40. 性能至上
41. 留意架构图里的空白区域
42. 学习软件专业的行话
43. 具体情形决定一切
  • 最重要的设计经验是: 具体情境(context)决定一切,根据它设计尽量简单的解决方案
  • 架构决策只有在情境需要时,才能牺牲尽量简单的原则
  • 脱离了具体的应用情境,孤立的比较技术的优劣是毫无意义的事
  • 别让团队成员被各种设计理念束缚住,鼓励大家具体情况具体分析,努力找出最简单的解决方案
44. 侏儒、精灵、巫师和国王
45. 向建筑师学习
  • 这似乎是个诡异的悖论,但也是最重要的真理: 天底下没有完美的建筑 —— John Ruskin
46. 避免重复
47. 欢迎来到现实世界
  • 真正决定程序流程的不是调用堆栈,而是用户需求
  • 分布式系统不但是松耦合、异步、并发的,而且不遵守传统的事务语义
  • 计算机科学家设想的完美世界正在崩溃。我们首先要接受现实,向令人怀念的调用堆栈架构告别吧,忘掉那些程序员决定程序流程的日子,准备应付随时出现的乱序事件,不断根据具体情境调整策略。用异步的、并发的请求替代一个接一个的方法调用。设计应用时,借助事件驱动的过程链模型或者状态模型控制无序的状态,通过调整、重发,甚至试探的办法纠正错误
48. 仔细观察,别试图控制一切
  • 妄想掌握一切的架构师只能设计出紧耦合的、脆弱的解决方案
49. 架构师好比两面神
  • 称职的架构师应该勇于接受新观念,敢于尝试新的设计思路和工具,促进项目、团队,甚至整个行业的发展。他应该采纳好点子,营造活跃的思考氛围。只有思想开放的架构师才能平衡各种矛盾因素,顺利的完成项目
  • 每位架构师都应该综合考虑新情况与老经验,在成熟技术的基础上不断创新,以满足当前的业务需求,又兼顾未来的发展规划
50. 架构师当聚焦于边界和接口
51. 助力开发团队
  • 要确保开发团队拥有他们所需的工具。工具不应该强行规定,而应当仔细选择,确保它们是开发人员处理手头工作的正确工具。应当尽可能的自动化那些重复和无须动脑筋的工作。同时确保开发人员拥有一流的机器用于工作,拥有足够的网络带宽
  • 要确保开发人员拥有所需的技能,确保它们能够获得必需的培训。在书籍上进行投资,并促进在技术方面开展积极的讨论。开发人员在工作期间必须要动手和实践,但同时也要有活跃的学术研讨
  • 一个充斥平庸之辈的团队很难产生爆炸性的巨响(a big bang)
  • 只要不违背软件设计的总体目标,就让开发人员自己作出决策
  • 保护好开发人员,不要让他们卷入到不那么重要的工作中
52. 记录决策理由
  • 记录软件架构决策理由的文档,长期有用,又无须为之付出过多维护精力,具有很高的投资回报价值
  • 如果事实表明你的决策站不住脚,便应虚心接受批评
53. 挑战假设,尤其是你自己的
  • 臆断是事情搞砸的根源
  • 不要假设——它会让你我出丑
  • 软件架构的最佳实践表明,应该记录下每个决策背后的理由
  • 事实和假设是构建软件的两大支柱。务必确保软件的基石坚实可靠
54. 分享知识和经验
  • 只有能非常容易的作出解释,才表明你真正理解了。只有不断解释和讨论,才能把经验凝聚成知识
  • 我们都是凡人,因此所知的一切不可能都是正确的,我们的每个想法并不都是合理的。只有勇于接受不足,才有不断改进的可能。从失败中能够学到更多,是颠扑不破的真理。如果思想和信仰经不起辩论的考验,那么,现在发现,总比以后重新建立要好
55. 模式病
  • 不要让一展设计模式功力的欲望,遮蔽了务实的真知。应当保持对系统的洞察力,提供切实有效的商业解决方案,使用模式解决适用的问题才是最重要的
56. 不要滥用架构隐喻
57. 关注应用程序的支持和维护
  • 应用程序的支持和维护永远都不应该是事后才考虑的事情。由于应用程序超过80%的生命周期都是在维护上,在设计时就应该多多关注支持和维护的问题
  • 一旦应用程序进入了生产环境,修复错误的压力来自于客户和管理人员,不是项目经理和测试团队,而愤怒的总裁将意味着更大的威胁
58. 有舍才有得
  • 有时,接受某种约束或放弃某个特性,可带来更好的架构
  • 布鲁尔猜想(Brewer's conjecture)也称为一致性(Consistency)、可用性(Availability)和分区(Partitioning)——CAP定理。在分布式系统中通常期望的3个特性,即一致性、可用性和分区容错性(partition tolerance)是无法同时获得的。试图同时拥有三者,将大幅增加工程费用,显著提高复杂度,也无法真正获得预期效果和实现业务目标
  • 接受一些权衡,往往能产生富有创造性和创新性的结果
59. 先考虑原则、公理和类比,再考虑个人意见和口味
60. 从“可行走骨架”开始开发应用
  • 在项目中越是困难大、耗时多的任务,越早完成越好
  • 从“可行走骨架”开始,保持系统一直运行可用,增量式进行培育,使其逐步成长
61. 数据是核心
  • 数据在大多数问题中处于核心地位,业务领域问题经由数据蔓延到代码中
62. 确保简单问题有简单的解
  • 为复杂解决方案付出的直接成本可能看上去很小,但是其潜在成本要大得多
  • 在简单问题上耗费了过多时间,则留给解决复杂问题的时间相对就少了。不当的架构决策徒然间导致了范围的蔓延,往项目中增加了不必要的风险
  • 试图猜测未来的需求时,错的概率是50%,错得很离谱的概率是49%。今天只需要解决今天的问题就好。把应用发布出去,从反馈中生成真实的需求。由于已经创建了简单的设计,当真实需求到来时,要将之整合进来就会更加容易
63. 架构师首先是开发人员
  • 不管解决方案设计得如何优秀,决定实现能否成功的最重要因素之一是让开发人员愿意接下任务。让开发人员肯接活最快捷的办法,是获得他们的尊重和信任
  • 作为架构师,主要目标应该是创建可行、可维护的解决方案,当然,也一定要能够解决当前的问题
  • 如果是你做的系统设计,你也应该能够编程实现自己给出的设计
64. 根据 投资回报率(ROI)进行决策
  • 将架构决策视为投资,并将相关的回报率也一并考虑在内。在判断每个决策选项是否务实或恰当时,这种方法很有用
65. 一切软件系统都是遗留系统
  • 在软件界,“遗留系统(legacy)”不是好词,但实际上,所有的软件系统都不应该排斥这个标签。它并不是一件坏事,因为这或许也表明了你的系统经久考验,符合预期,具有商业价值
66. 起码要有两个可选的解决方案
  • 对于某个问题,如果只考虑了一个解决方案,那你就有麻烦了
67. 理解变化的影响
68. 你不能不了解硬件
69. 现在走捷径,将来付利息
  • 长远看来,系统维护将比项目初期的开发消耗更多的资源。在项目开发初期走捷径,可能会以日后付出高昂的维护费用为代价
  • 你可能觉得单元测试并不直接产生价值,于是就让开发人员跳过这些严格的测试工作。这将导致所交付的系统在未来更难修改,而且在修改时信心不足。即使只做了一点修改,也需要对系统做大量的手动测试,这将导致系统极为脆弱,维护成本不断攀升,其设计也将远逊于经过全面测试的系统,更别说和使用了测试先行(test-first)的设计相比了
  • 发现有不当的设计决策时就要尽快修正,设计不当的特征可能会成为后续特征的基础,将来需要花费很高的成本来更正
  • 碰到架构问题或设计缺陷,作为架构师,一定要坚持成本还很低廉时就动手。搁置越久,为之付出的利息也将越高
70. 不要追求”完美”,”足够好”就行
  • 不要屈服于企图使设计或实现达到完美的诱惑!把目的设定在“足够好”就行,当已经达到目标时,就停下来
  • “足够好”指的是,剩余的不完美之处,对系统的功能、可维护性或性能不会产生任何有深远意义的影响
  • 为什么保持简单会这么困难呢?因为我们在寻求完美的解决方案!
  • 应用程序开发不是选美大赛,因此,停止吹毛求疵的做法,不要再浪费时间追求尽善尽美
71. 小心”好主意”
72. 内容为王
  • 优秀内容成就优秀系统
73. 对商业方,架构师要避免愤世嫉俗
  • 业务是架构师职业存在的原因。为业务服务是我们的生存之本,雇主期望我们能够解决问题,倾听和了解雇主的业务,是我们必须掌握的最为关键的技能
  • 不要让自己成为愤世嫉俗的“天才”
  • 要对工作满怀激情,但不要是那种带着愤怒和火气的“激情”。要学会能够带着不同意见继续前进
  • 要找到能和业务方建立良好关系的方法,不要让自我破坏这种关系。这会使你成为更快乐、更高效的架构师
74. 拉伸关键维度,发现设计中的不足
75. 架构师要以自己的编程能力为依托
  • 为项目设计架构时,对实现每个设计元素所需的工作量,要做到心中有数
76. 命名要恰如其分
  • 如果不知道一个东西应该叫什么,那你肯定不知道它究竟是什么。如果你不知道它究竟是什么,那么你也肯定不能坐下来为它编写代码
  • 如果无法给出合适的命名,那也就无法继续编程。如果发现自己需要多次更改命名,那么最好停下来,直到弄清楚要做的究竟是什么
77. 稳定的问题才能产生高质量的解决方案
  • 只要问题是稳定的,你就可以创建出一个拥有稳定设计的系统。稳定的设计可以让你集中精力,打造出高质量的应用程序
78. 天道酬勤
79. 对决策负责
80. 弃聪明,求质朴
81. 精心选择有效技术,绝不轻易抛弃
  • 作为架构师,我们应当紧跟产业趋势,但我们并不必急于去做拥抱那些羽翼未丰的新技术的先行者。通常,为新技术做第一个吃螃蟹的人,并不能享受到多大好处,反而可能会遭遇不少挫折
  • 不要为了那些没有未来的新技术,把项目置于险境
  • 软件架构师工作很大的一部分,是要选择用以攻克难题的合适技术。精心选择熟悉的武器,不到万不得已绝不轻易抛弃它们。这些技术在过去给你带来了成功,尽量让它们在未来也能为你带来胜利,同时,以审慎的态度更新你的技术武器库
82. 客户的客户才是你的客户!
  • 你的客户的客户,才是你的客户。如果你的客户的客户赢了,你的客户也就赢了。这意味着,你也赢了
83. 事物发展总会出人意料
84. 选择彼此可协调工作的框架
  • 如果选用一个“无所不能”型的框架,那它最好能够提供项目所需全部功能价值75%以上
  • 系统应该由多个相互独立的框架组成,其中每个框架都精专于各自的领域,但同时又非常简洁(simple)、包容(humble)和灵活(flexible)
85. 着重强调项目的商业价值
86. 不仅仅只控制代码,也要控制数据
87. 偿还技术债务
  • 对任何已投入实用的项目,无论是要修复缺陷,还是要添加新功能,总有必须修改产品的时候。在那点上,会面临两个可能选择: 花合适时间“一次做对”,或者取巧走“捷径(shortcut)”,争取尽快推出修改后的产品
88. 不要急于求解
  • 不要立即着手去解决摆在面前的问题,而要看自己能否可以改变问题。有时业务问题确实需要得到解决,但有时,或许并非那么迫在眉睫
  • 在立即去寻找问题的答案之前,还是先想想,如果根本就不存在这个问题,这个世界又会怎样
89. 打造上手的系统
90. 找到并留住富有激情的问题解决者
  • 组建一支汇聚优秀开发人员的团队,是确保软件项目成功非常重要的事情之一。保持团队长期稳定,尽管不像说起来那么容易,却十分重要。因此,要精心筹建优秀的开发团队,一旦建成,务必竭力维护
  • 工作中使用的工具肯定会改变,真正所需的,是那种无论技术如何变化,都善于攻克各种难题的人才
  • 批评过度,可能会扼杀可发人员的创造力,降低其生产力。更糟糕的是,可能会在团队内部造成纷争。好的开发人员都非常聪明;他们知道自己并非一无是处。如果对其工作吹毛求疵,将会降低他们对你的尊重。可以提出建设性的批评建议,但不要强求每个解决方案看起来好像都出自你手
91. 软件并非真实的存在
92. 学习新语言
93. 没有永不过时的解决方案
  • 为当前选择一项好技术已属困难,要选择在未来也切合可用的好技术只会徒劳无功
94. 用户接受问题
  • 人们并不总是满心欢喜的接受新系统的重大升级。这种情势,可能会对项目的成功构成威胁
95. 清汤的重要启示
96. 对最终用户而言,界面就是系统
  • 糟糕的用户界面埋没了太多的好产品
97. 优秀软件不是构建出来的,而是培育起来的
  • 优秀软件不是构建出来的,而是培育起来的
  • 单凭规模大小,便可窥见软件成败的端倪; 细思之下便可知,在一开始就要设计庞大的系统几乎毫无好处可言
  • 无论多么诱人,都要抵制住试图设计出庞大完整的系统,来“满足甚或超出”已知需求和特性期望的想法。要有宏伟的远景(grand vision),但不要有庞大的设。环境和需求的变化是不可避免的,你和你的系统都要学会适应变化
  • 设计尽可能小的系统,帮助成功交付,并推动它向宏伟的远景目标不断演化

from: http://dearymz.blog.163.com/blog/static/20565742012102894917257/?suggestedreading&wumii

你可能感兴趣的:(读《软件架构师应该知道的97件事》)