Efficiency02

《软件研发效能提升之美01》

内容来自《软件研发效能提升之美》一书 上一节偏思想方面,这次更多偏实操相关 DevOps落地精要 核心解读 DevOps字面上是Development和Operations的组合,即开发、运维⼀体化,测试作为质量保障角色也会融合其中,⼀体化最大的优势就是打破壁垒、拉通职能,最终体现在效能的提升上,同时把握产品质量与业务增长的平衡点 DevOps的六大武器 标准化作业 好处是能够固化流程,将人力从重复劳动中解放出来,同时有效地减少错误的发生 快速失败 DevOps实践中,通常会引入CI/CD技术,通过高频集成和高频交付,将问题暴露在早期 快速反应 消除角色壁垒,尽可能用工具替代人力,并用流水线方式加速流程 高质量与高效率 DevOps通过持续部署、快速反馈的方式,更好地将不确定性逐步转变为确定性,从而保障⾼质量交付 降低成本 当DevOps模式运作成熟,尤其是快速迭代的流水线初成规模后,软件研发的效率将大大提升,研发成本将明显降低,应对变化的能力也将大幅提高 团队协作 DevOps依靠⼯具和自动化(尤其是流程自动化)来弥补开发与运维之间的技能鸿沟和沟通鸿沟,将软件研发中的三大角色(研发、运维、测试)有效地黏合在⼀起 DevOps是⼀种软件研发管理模式和思想,是⼀种文化实践,并不是具体的⼯具或技术,所有在保证质量的前提下提升效能的方法都属于DevOps的范畴 DevOps生命周期 持续开发 持续开发包含计划和编码⼯作,伴随这⼀阶段的工具主要有代码仓库、版本控制⼯具、包管理⼯具,以及⼀些计划可视化方法,如⽢特图、燃尽图等,也会涉及分⽀管理、单元测试等⼯作 持续集成 持续集成是DevOps中最核⼼的组成部分,通过频繁地提交代码、频繁地编译代码、频繁地构建项⽬、频繁地执⾏单元测试等,不断高频集成,贯彻快速失败的原则,尽可能早地收敛问题 持续测试 持续测试需要确保软件代码的每⼀次提交都能够被及时验证,并输出完整的质量反馈。纯⼈⼯的⽅式很难做到持续测试,⾃动化测试和容器化手段是这⼀阶段的标配 持续监控 这⾥的监控不仅是指对软件运行状况的监控,还包括对DevOps各项⼯作执⾏的监控,以便我们及时做出处理和纠正。持续监控还可以提供历史趋势信息,帮助我们更好地提供决策依据 持续反馈 整个软件研发周期的每⼀项⼯作都对外暴露了大量的信息,持续反馈在每个关键节点介入,以各种形式总结输出反馈建议,并不断改进,从而推动项目良性循环 持续部署 持续部署同样基于高频的理念,尽可能早地让软件产品接受生产的检验,快速发现并收敛问题,保证软件产品及早与用户见面 持续运营 运营阶段涉及对事件和变更的管理,如配置管理、容量管理、高可用管理、用户体验管理等,是DevOps的最后⼀个阶段。运营也是⼀项连续性的⼯作,是需要持续不断进行的。相对于其他周期项,持续运营更需要具备全局视角,才能做到目无全牛 不适合DevOps的场景 在⼀些传统行业或政府机关,软件需求较为固定,甚至会采用外包的方式,且研发周期较长,对质量要求很高 传统金融领域或⼀些从事机密行业的商业机构 代码,分支与流水线 代码是软件产品的原始生产产物,经过编译、构建、测试、发布、部署后,形成可用的产品对象 三个重要因素对研发效能起关键作用 代码,软件产品究其本质都是由代码构成的,代码的质量直接决定了整个研发周期的投入和产出 分支,我们探讨的不是小作坊式的单⼈开发模式,软件开发团队需要在同⼀代码基础上并发进行多个功能的代码编写活动,显然不可能让员工在相同文件上作业,这时候就需要分支和配套的⼯作流。分⽀的实质是能够让研发活动并发起来,提升效率 流水线,在上述例子中,无论是软件代码的集成和交付过程,还是厨师生产美味佳肴的过程,都是⼀道道⼯序不断向前推进的 持续过程。流水线⼀方面可以使流程固化,避免⼈为因素造成的不确定性;另⼀方面能够透明地将每道⼯序的结果呈现出来,便于及早识别问题 代码质量 代码质量不是研发单体的责任,发现质量问题也不是测试单体的责任。我们所谈到的代码质量是需要由软件项⽬中涉 及的所有角色共同保证的,⽬的就是尽可能早地将质量问题消除在源头。绝⼤部分缺陷都是在编码阶段“写”出来的,⽽问题发现得越早,修复的成本就越低 测试驱动开发 从实践⾓度来讲,狭义的TDD,即UTDD(Unit Test DrivenDevelopment,单元测试驱动开发)对研发模式的侵⼊性是⽐较⼤的,现实中很多研发⼈员会不习惯,导致推⼴困难。我们可以考虑将测试⼯作上升至业务层,推行ATDD(Acceptance Test DrivenDevelopment,验收测试驱动开发),先定义质量标准和验收细则,再通过⾃动化测试的⽅式进行验收,这样就能够在代码编写和交付阶段预防缺陷 静态扫描 ⼀种成本较低的质量保障手段,它最⼤的好处是不需要运行代码,仅仅通过分析静态代码结构和抽象语法树,就能发现潜 在的风险和违反规约的地方。目前市面上比较流行的静态扫描⼯具有阿里的代码规约扫描⼯具、Sonar、Findbugs、PMD等 代码评审 代码评审对质量保障的重要性不言而喻,但实际⼯作中就是另⼀番景象了,做得好的团队经常能够主动发现代码问题,做得不好的团队则流于形式,草草签字、画押了事 并至主干分支前进行,评审完成后再进行正式的测试⼯作 分支与工作流 分⽀的存在允许多人同时⼯作而互不干扰,不过我们总要在某个时机,将多分⽀代码合并成⼀份,以便⽣成最终交付的代码版本,这个过程涉及版本管理、发布管理、缺陷修复等⼀系列环节 工作流: Git Flow⼀共设计了5种分支,其中包含两种长期存在的“主要分支”(master和develop)和三种暂时存在的“协助分 支”(feature、release和hotfix) master分支上保持的版本必须是时刻可以发布运行的,因此不允许在master上直接进行修改和提交,只有其他分支的代码经过⼀系列的流程验证后,才能合⼊master。 develop分支是代码开发的基准分支,也不允许直接修改和提交。 feature分⽀是开发者编码的主要⼯作分支,在develop分支建⽴完成后,通过PR的⽅式合并回develop分支。 release分⽀是⽤来进⾏版本发布的分支,发布成功后分别合⼊develop和master分⽀。 hotfix分⽀⽤于处理紧急bug修复,在master分支建⽴、修复完成后,hotfix分支将合⼊develop和master分支 流水线 从DevOps的视⾓看,流⽔线是持续交付的载体,通过构建⾃动化、集成⾃动化、验证⾃动化、部署⾃动化等⼯作,完成从开发到上线的持续交付过程。其中,每个环节相当于CPU中的各个电路单元,在整体上形成了并⾏的效果,通过持续向团队提供及时的反馈,让交付过程⾼效、顺畅 精益理论强调了价值流动的重要性,⽽价值流动需要通过⼀定的⽅式来体现,DevOps流⽔线从某种程度上说就是⼀个很好的 载体,因为它⼏乎覆盖了整个端到端的流程。在流⽔线上,各环节流转得越快,价值流动就越快,这间接体现了公司的研发效能 Jenkins Pipeline⽀持声明式和脚本式两种脚本编写⽅式,前者语法限制多,偏模板化,⽽后者则更⾃由,善于应对复杂的需求。下⾯我们以声明式的⽅式尝试编写⼀个流⽔线脚本,覆盖代码拉取、构建、部署、测试等⼀系列环节 持续集成与持续交付 集成,指部分向整体合并的过程,⽐如,某⼯程师研发的代码,将其合并到主干的过程就是集成,这其中涉及编译、打包、构建、单 元测试等工作。...

《软件研发效能提升之美00》

内容《软件研发效能提升之美》之书 总论 研发效能是多⼈组织协同效率的课题,在现代基础设施、架构理论和AI算法的加持下,研发效能的内容也从敏捷方法论快速进化到非常具体的⼯具、流程和指标系统 研发效能定义:在保证质量的前提下,尽可能⾼效地持续交付价值。为此,我们重新审视 整个软件⽣产流程(需求、设计、开发、测试、部署、发布、运营),在不增加成本和保证质量的前提下,提升各个步骤的标准化、⾃动化、系统化和⼀致性⽔平,并不断优化团队的交付效率 **没有银弹:**没有任何一项技术或方法可使软件工程的生产力在十年内提高十倍 希望软件研发能够成为⼀个技术密集型产业,⽽不是劳动密集型产业 敏捷作为⼀种通过创造变化和响应变化在不确定和混乱的环境中取得成功的能力,具备高度的实践性和创造性,这样的特性在赋予敏捷强大适配度的同时,也给落地造成了困难 概述 研发效能提升案例 前端代码的自动生成 我们手绘出GUI界⾯的草稿;通过Sketch2Code可以直接将这份草稿转换成目标平台的代码,如果我们指定的目标平台是Web,那么代码格式就是HTML,如果我们指定的目标平台是iOS,那么代码就以XCode项目的形式呈现;最后,完成编译打包,可直接在iPhone上安装执⾏了。 这种方式的引入将大幅提升原型构建环节的效率 临界参数下的API测试 考虑引⼊⼀种机制,通过⼯具或脚本去主动检测API输入参数的类型,根据不同的类型⽣成相应的容易出错的临界值,我们⽤这些临界值作为测试数据去⾃动调⽤API。如果API返回预期外的异常或错误(如HTTP-500错误),那就说明这个API没有妥善处理我们传⼊的临界值 进⼀步的,我们可以将这个机制与CI流水线集成,在CI执行走过程中主动执⾏临界参数下的API测试,以求问题更早地被暴露 例如:当⼯具或脚本识别到某API的输⼊参数是String类型的时候,就可以⽣成NULL、超⻓的字符串、包含⾮英语字符的字符串、SQL注⼊字符串等⼀系列临界值,将其作为测试数据去检测程序潜在的问题 基于流程优化的效能提升 效率的提升既可以由技术来驱动,也可以由流程来驱动,流程要整体方便 第一性原理:顺畅、高质量地持续交付有效价值的闭环 顺畅:价值的流动过程必须顺畅,没有阻碍 高质量:如果质量不行,那么流动越快,死得也会越快 **持续:**不能时断时续,小步快跑才是正道 **有效价值:**这是从需求层⾯来说的,你的交付物是不是真正解决了用户的本质问题 **闭环:**强调快速反馈的重要性 鹅生金蛋的故事。是不是鹅生的金蛋越多,效能就越高呢?其实不是,⼀味地让 鹅全日无休地生金蛋,早晚会把鹅累死,这不是可持续的长远战略。真正的效能应该是让鹅生鹅,鹅再生鹅,让更多的鹅⼀起来下金蛋 从软件开发、测试和发布的视角来看⼀下各个阶段研发效能提升需要关注的问题,其主线是围绕CI/CD的⼀些实践 举例:选择合适的发布策略也会对效能和风险之间的平衡起到积极的作⽤ 架构相对简单,但是集群规模庞大,则优选金丝雀 架构⽐较复杂,但是集群规模不是太大,可能蓝绿发布更占优势 研发效能这个领域,要保证我们所做的研发效能工具⼀定是能解决实际问题的 研发效能提升实践: 找钉子,场景举例: 本地编译耗时长:提供增量编译和分布式编译能力 本地测试困难,测试环境准备复杂且耗时长:基于Kubernetes的Pod提供⼀键搭建测试环境的能力 自动化测试用例数量大,执行回归时间过长:采用并发测试用例执行机制,使用几百、几千台测试执行机并行执行用例,实现用硬件资源换时间 自动化测试用例维护成本高:测试用例采用模块化和分层体系,实现低成本的自动化用例维护 测试数据准备困难:引入统⼀的测试数据服务(Test DataService)能力 集群规模庞大,发布过程耗时过长:各个层级的并发部署能力,集群内节点的并发、集群间的并发等。 项目的过程数据都是后期集中填充,失去度量意义:项目的过程数据由⼯具自动填充,不再依赖⼯程师手工输入。比如,开发完成的时间不再依赖于开发人员手工填写,而是由Jenkins构建完成后自动填写,以保证所有过程数据的真实有效性,从⽽为后面的度量和改进提供可靠的信息输入 全局切入: 软件缺陷的流转,软件需求的实现与交付,软件制品包发布等待 持续改进: 比如,需要在Jenkins中通过hook机制去触发⼀些操作(比如代码静态扫描、单元测试等),最简单的做法就是在hook中实现操作的具体步骤,这种实现在开始时效率很高,也非常容易实现,但却不是最优的⽅案,因为hook中的代码只会被执行⼀次,而且hook越来越多以后,各种实现都散落在各个地方,难以维护,⼀旦有新的需要(比如要加⼊慢SQL扫描),就需要改hook实现,而且这种做法也违背了IaC(Infrastructure as Code)原则。 更好的做法是引入研发效能的消息中心,通过下游操作的订阅模式来实现未来的可扩展性 效能平台的灵活性 将Jenkins持续集成⼯具视为⼀个平台,在这个平台上支持安装各种插件,以增强平台功能,从而实现平台架构的灵活性 杜绝"掩耳盗铃" 代码质量门禁Sonar设而不卡 单元测试只是执行,不写断⾔Assert 代码覆盖率形同虚设 Peer Review走过场 代码递交过于随意 监控超配,有报警但无人认领 发展方向与未来展望 研发各个环节的全链路横向打通 CI/CD和测试不再是⼀个个独立的环节,软件研发从需求开始到最终线上交付采用⼀站式的研发效能平台,实现统⼀的研发⼯具和流程 研发全流程的可视化 研发流程的可视化在后期⼀定会成为⾏业的标配,通过流程的可视化,可以展示各个需求的进展情况,让各级管理者和⼀线⼯程师清楚地知道项目目前所处的状态。 “稳态”和“敏态”齐头并进 研发效能的提升并不⼀定都要绑定到敏捷开发实践上,事实上,对于那些需求明确并且稳定的项目,传统的瀑布模型依然是最佳的选择,此时采用“稳态”实践才是获得最佳效率的途径。只有那些需求变更频繁的项目才是践行敏捷实践的最佳选择。因此,敏捷对传统瀑布而言并不是取代,⽽是互补,“稳态”和“敏态”会在长时期内和谐共存。 研发能力的中台化沉淀 研发各阶段的垂直能⼒必然会沉淀到中台,以统⼀服务化的形式对外提供服务。⽐如,代码覆盖率的统计能⼒会统⼀到⼀个单⼀的服务中,为各个语⾔的业务提供代码覆盖率的统计;再如,分布式编译加速的能⼒也会成为企业级的服务,为各种⼤型项目提供编译加速。 数据驱动下的效能提升 以后的决策⼀定会基于数据来开展。效能提升实践的效果衡量也会⾼度依赖于数据。研发效能数据中台的建设必定会被提上⽇程,通过收集存储研发各阶段的各种过程数据,实现基于研发效能⼤数据平台的决策体系 进阶解读 软件生产作为智⼒密集型活动,掺杂着⼤量人的因素,很难严格地标准化 质量和效能是“既要、也要”的关系,效能的提升能够将软件研发中的⻛险更快、更及时地暴露出来,同时减轻⼈脑负担,反过来⼜ 能提升质量本身 渴望尊重和欣赏,是⼈性的需求之⼀。适度的关注和赞美能够产⽣强烈的心理暗示,继而带来效能的提升 反摩尔定律告诉我们,越迟交付的价值其价值越低 信息熵衰减对研发效能的影响是巨⼤的,要想方设法将信息传递的效率提升上去 自解释的代码不是无注释和无文档的代码,而是伴随着⾼信息熵的代码体系。内容简洁合理的注释与⽂档,同样也是优秀代码的⼀部分,能够给效能的提升带来帮助 基于流程优化,打破各个环节看不见的墙,去除不必要的等待,提升价值流动速度,这些是研发效能试图解决的一大类问题 项目管理提效 敏捷宣言...

数据结构知识

数据结构 数据结构知识 具体内容 数组:数组的内存空间是连续的,随机访问的时间复杂度是O1,适用于需要按索引访问元素的场景,但是插入和删除元素较慢,时间复杂度是On 链表:链表是由节点组成,节点之间是分散存储的,内存不连续,每个节点存储数据和指向下一个节点的指针。适用于频繁插入和删除元素的场景,随机访问元素较慢 栈:栈是一种后进先出的数据结构,只允许在栈顶进行插入和删除操作 队列:队列是一种先进先出(FIFO)的数据结构,允许在队尾插入元素,在队首删除元素 树:树是一种非线性数据结构,由节点和边组成,每个节点可以有多个子节点。树适用于表示层次关系的场景,例如文件系统、组织结构等 数组与链表区别? 访问效率,插入和删除操作效率,缓存命中率 应用场景:数组适合静态大小、频繁访问元素的场景,而链表适合动态大小、频繁插入、删除操作的场景 平衡二叉树 二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质: 非空左子树的所有键值小于其根结点的键值。 非空右子树的所有键值大于其根结点的键值。 左、右子树都是二叉搜索树。 二叉树搜索树的目的:缩短插入、删除、修改和查找节点的时间 一棵理想的二叉搜索树所有操作的时间可以缩短到 O(logn) 一棵每个结点只有右孩子的二叉搜索树,那么性质就和链表一样 平衡树将二叉查找树平衡均匀地分布,好处就是可以减少二叉查找树的深度 平衡二叉树平衡的特性: 左右两个子树的高度差(平衡因子)的绝对值不超过1 左右两个子树都是一棵平衡二叉树 红黑树 每个节点要么是红色,要么是黑色。 根节点是黑色。 每个叶子节点(NIL节点)是黑色。 如果一个节点是红色,则其子节点必须是黑色。 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。 二叉查找树 当每次插入的元素都是二叉查找树中最大的元素,二叉查找树就会退化成了一条链表,查找数据的时间复杂度变成了 O(n) 平衡二叉查找树 每个节点的左子树和右子树的高度差不能超过 1。也就是说节点的左子树和右子树仍然为平衡二叉树,这样查询操作的时间复杂度就会一直维持在 O(logn) B+树 B+树是一种自平衡的多路查找树,所有叶节点都位于同一层,保证了树的平衡,使得搜索、插入和删除操作的时间复杂度为对数级别的 非叶节点仅包含索引信息,不存储具体的数据记录,用来引导搜索到正确的叶节点 所有数据记录都存储在叶节点中,且叶节点中的数据是按关键字排序的。叶节点包含实际的数据和关键字,是数据存储和检索的实体单元。叶节点之间通过指针相互链接,形成一个链表,便于范围查询和顺序遍历 B+树与B-树 检索路径:B树在查找数据时,可能在非叶子节点找到目标数据,路径长度不固定。B+树中所有数据都在叶子节点,查找数据时必须走到叶子节点,路径长度固定(均等)。即查找总是要到叶子节点结束 叶子节点结构:B树中叶子节点之间没有特别的链接,彼此独立。B+树中叶子节点通过指针连接,形成一个有序链表,便于范围查询和顺序访问 非叶子节点内容:B树中非叶子节点存储数据和索引。B+树中非叶子节点只存储索引,不存储实际数据。当数据量比较大时,相对于B树,B+树的层高更少,查找效率也就更高。 **高效地范围查询:**B+树叶子节点采用的是双链表连接,适合 MySQL 中常见的基于范围的顺序查找,而 B 树在进行范围查询时需要进行中序遍历,性能较差 堆 堆是一颗完全二叉树,实现的堆也被称为二叉堆。堆中节点的值都大于等于(或小于等于)其子节点的值,堆中如果节点的值都大于等于其子节点的值,我们把它称为大顶堆,如果都小于等于其子节点的值,我们将其称为小顶堆 排序算法 冒泡排序:通过相邻元素的比较和交换,每次将最大(或最小)的元素逐步“冒泡”到最后(或最前)。时间复杂度:最好情况下O(n),最坏情况下O(n^2),平均情况下O(n^2)。,空间复杂度:O(1) //冒泡排序 public static void bubbleSort(int[] arr) { int temp;//临时变量 boolean flag;//是否交换的标志 for (int i = 0; i < arr....

Hot100

Hot100 回溯 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案 class Solution { List<List<Integer>>res; int len=0; public List<List<Integer>> permute(int[] nums) { this.res=new LinkedList<>(); this.len=nums.length; dfs(nums,new LinkedList<>(),new boolean[len]); return res; } public void dfs(int[] nums,LinkedList path,boolean[]p){ if(path.size()==len){ res.add(new LinkedList<>(path)); return; } for(int i=0;i<len;i++){ if(p[i]){ continue; } p[i]=true; path.add(nums[i]); dfs(nums,path,p); p[i]=false; path.remove(path.size()-1); } } } 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集 class Solution { List<List<Integer>>res=new LinkedList<>(); LinkedList<Integer>track=new LinkedList<>(); public List<List<Integer>> subsets(int[] nums) { backtracking(nums,0); return res; } public void backtracking(int[] nums,int start){ res....

算法总结

以前写的总结 框架思维: 数据结构的存储方式:数组和链表 任何数据结构无非遍历和访问,在不同的场合高效增删改查 而遍历分为线性和非线性(for/while和递归) 算法心得: 算法的本质就是无穷举 数组/单链表常用:双指针,二分搜索,滑动窗口,回文子串,前缀和,差分数组 二叉树常用:1.遍历一遍二叉树(递归函数无返回值) 2.分解问题计算答案(递归函数有返回值) 二叉树: void traverse(TreeNode root) { if (root == null) { return; } // 前序位置 :进入结点的时候 traverse(root.left); // 中序位置 traverse(root.right); // 后序位置 :离开结点的时候 } 层序遍历:竖while横for 链表:双指针 设置虚拟结点,用指针来进行变化,同时新建链表注意指明最后一个结点 回溯算法: 回溯算法是在遍历「树枝」,DFS 算法是在遍历「节点」 我们通过保证元素之间的相对顺序来防止出现重复子集—-用start变量 标记那些元素可以被选择—–用used数组 重复元素中找可能解—-先排序,用num[i]==num[i-1]来减枝 重复元素的全排列—-先排序,用num[i]==num[i-1]&&!used[i-1]固定相同数值位置来减枝(规则:相同则前一个必走过),且used[i]标记该数值是否被使用 子集/组合问题必须用start,排列问题必用used数组 数组: 双指针 一维前缀和:preSum[i] 记录 nums[0..i-1] 的累加和 二维前缀和:preSum[i][j] 记录 matrix 中子矩阵 [0, 0, i-1, j-1] 的元素和 对于前缀和问题:申请新数组范围是n+1,数组实际从索引1开始 差分数组:差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。 后面数据靠前面的差值,diff[i] 就是 nums[i] 和 nums[i-1] 之差 如果你想对区间 nums[i..j] 的元素全部加 3,那么只需要让 diff[i] += 3,然后再让 diff[j+1] -= 3 即可:...

devops概述

devops介绍 https://github.com/lcomplete/TechShare/blob/master/docs/engineering/devops.md全文 定义 DevOps 所要实现的目标都是一致的——缩短软件开发生命周期并使用 持续交付 提供高质量的软件,开发运维之间 沟通合作 发展背景: 敏态需求的增加,即探索性工作的增加: 软件开发从传统的瀑布流方式到敏捷开发,再到现在对敏捷开发提出了更高的要求,近些年创新型的应用不断涌现,这些应用的研发过程中多采用小步快跑、快速试错的方式,这些探索性工作要求运维能够具备一天发布多次的能力,需要企业完成由稳态到敏态的转变 软件开发活动在企业经营活动中占比的不断增加: 业务发展对软件的依赖由轻度依赖、中度依赖发展到目前的重度依赖。 企业存在对消除浪费的需求: 软件开发活动在企业中的位置越来越重要,软件开发活动中也存在着许多的浪费,企业管理上必然存在着 识别并消除浪费 的需求 软件开发中的浪费包括不必要和必要的浪费: 不必要的浪费有:无人使用的功能、软件bug、等待测试、等待审批等 必要的浪费包括:工作项移交、测试、项目管理 以上为深层原因,浅层原因有:容器化技术的发展、微服务架构的发展等等 DevOps原则 DevOps 原则是总体指导思想,实践是具体的执行方法,DevOps 是一个动态的过程,在进行相关实践的时候可以看看其应用了哪些原则,当违背原则的时候需要思考实践的合理性 三大原则: 流动原则:加速 从开发、运维到交付给客户的流程 反馈原则:建设 安全可靠 的工作体系 持续学习与实验原则:采用科学工作方式,将对组织的 改进和创新 作为工作一部分 流转原则: 坚持少做 产品开始开发时采用 MVP 原则(最小可行产品原则) MVP要求抓住最核心的产品流程,剥掉多余的功能或者高级功能,只要主流程可以跑起来可以。完美并不是我们的目标,快速试错才是我们目标 产品迭代时要适时做减法 持续分解问题 大的变更或需求拆解为一系列小的变更,快速解决 工作可视化 采用 Sprint 看板将工作可视化 控制任务数量 减少前置时间,降低测试人员的等待时间 任务越多,预估越不准确 减少交接次数 减少不必要的沟通和等待 持续识别和改善约束点 识别出影响流动的主要前置因素,比如搭建环境、需求文档 QA、开发、运维、产品持续提升生产力 为非功能性需求预留20%的开发时间,减少技术债务 消除价值流中的困境和浪费(导致交付延迟的主要因素) 半成品——未完全完成的工作 额外工序——从不使用的文档、重复编写接口文档等 额外功能——用户实际不需要的功能 任务切换——将人员分配到多个项目或截然不同的工作任务中 等待、移动、缺陷、非标准化的手动操作 返回原则 在复杂系统中安全地工作 管理复杂的工作,识别出设计和操作的问题 群策群力解决问题,从而快速构建新知识 在整个组织中,将区域性的知识应用到全局范围 领导者要持续培养有以上才能的人 及时发现问题 快速、频繁和高质量的信息流——每个工序的操作都会被度量和监控 技术价值流的每个阶段(产品管理、开发、QA、安全、运维),建立快速的反馈和前馈回路(包括自动化构建、集成和测试过程) 全方位的遥测系统 在源头保障质量 过多的检查和审批流程,使得做决策的地方远离执行工作的地方,这导致流程有效性降低,减弱了因果关系之间反馈的强度。 让开发人员也对系统质量负责,快速反馈,加速开发人员的学习。 为内部客户优化工作 运维的非功能性需求(如架构、性能、稳定性、可测试性、可配置性和安全性)与用户功能同样重要 持续学习和实验原则 建立学习型组织和安全文化 将日常工作的改进制度化 把局部发现转化为全局优化 在日常工作中注入弹性模式 缩短部署的前置时间、提高测试覆盖率、缩短测试执行时间,甚至在必要时解耦架构,都属于在系统中引入类似张力的做法。 领导层强化学习文化 领导者帮助一线工作者在日常工作中发现并解决问题 DevOps实践 基于 DevOps 的相关原则,有与其对应的实践,在应用这些实践之前还需认真设计组织结构,使其有利于实践的开展...

Hot100

Hot100 二叉树 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<Integer>(); Stack<TreeNode> stk = new Stack<TreeNode>(); while (root != null || !stk.isEmpty()) { while (root != null) { stk.push(root); root = root.left; } root = stk.pop(); res.add(root.val); root = root.right; } return res; } } 思考中序过程就行 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 class Solution { public int maxDepth(TreeNode root) { return Depth(root); } public int Depth(TreeNode root){ if(root==null){ return 0; } int left=Depth(root....

Hot100

Hot100 哈希 两数之和 class Solution { public int[] twoSum(int[] nums, int target) { HashMap<Integer,Integer>map=new HashMap<>(); int len=nums.length; for(int i=0;i<len;i++){ int p=target-nums[i]; if(map.containsKey(p)){ return new int[]{i,map.get(p)}; } map.put(nums[i],i); } return new int[]{0,0}; } } 字母异位分组 class Solution { public List<List<String>> groupAnagrams(String[] strs) { HashMap<String,LinkedList<String>>cur=new HashMap<>(); for(String str:strs){ char[] c1=str.toCharArray(); Arrays.sort(c1); String s=String.valueOf(c1); if(!cur.containsKey(s)){ cur.put(s,new LinkedList<>()); cur.get(s).add(str); }else{ cur.get(s).add(str); } } return new LinkedList(cur.values()); } } 最长连续序列:给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度 class Solution { public int longestConsecutive(int[] nums) { int ans = 0; Set<Integer> st = new HashSet<>(); for (int num : nums) { st....

数据结构Java版

数据结构 数组 ArrayList`<Integer>`arr=new ArrayList<>();int[]c=new int[3];数组建立 arr.add(99);数组增加 arr.toArray(a);集合变成数组类型 arr.add(3,99);数组插入 arr.get(3);获得元素值 arr.set(3,22);改变元素值 arr.remove(3);移除某索引的元素 Collections.reverse(arr);反转一个arr arr.size();长度 普通数组长度为:length arr.contains(99);是否含有某元素 Collections.sort(arr);集合类的排序 Arrays.sort(c);普通排序 Arrays.fill(c, 1);填充数组c的所有值为1 二维数组排序:第一列按升序排列,第一列相同,则第二列按升序排列 compare方法返回值int类型,返回值大于0,交换两数,小于零,排序正确,等于0,两数相等 Arrays.sort(arr, new Comparator<int[]>() { public int compare(int[] e1, int[] e2) { // 如果第一列元素相等,则比较第二列元素 if (e1[0]==e2[0]) return e1[1]-e2[1]; // e1[1]-e2[1]表示对于第二列元素进行升序排序 return e1[0]-e2[0]; } Arrays.sort(intervals,new Comparator<int[]>(){//两数相减或相加会产生int值溢出,因此通过比较后,直接返回-1,0,1 public int compare(int[]e1,int[]e2){// e1[0]-e2[0]表示对于第一列元素进行升序排序 if(e1[0]>e2[0]){ return 1; }else if(e1[0]==e2[0]){ return 0; }else{ return -1; } } }); Arrays.sort(intervals, (e1, e2) -> Integer.compare(e1[0], e2[0]));//上面可简写 Arrays.sort(strings,(s1,s2)-> (s1+s2).compareTo(s2+s1)); Collections....