20 世纪 60 年代,软件开始脱离硬件,逐渐成为一个独立产业。至今,软件开发过程从瀑布模型、CMM/CMMI,到 20 年前敏捷的诞生,再到今天 DevOps 的火热,一代代软件人在思考和探索,如何避开“焦油坑”,试图寻找软件交付的“银弹”。

焦油坑:复杂且让人感觉束缚,越陷越深难以摆脱。常被软件开发者形容软件产品的复杂度成倍增长;银弹:比喻词,形容解决问题的捷径。 图源网络

DevOps 作为目前软件工程界的集大成者,备受关注,业界也有很多讨论。近年来包括博云在内的很多厂商,也投身于 DevOps 之中,希望将更好的软件研发管理方法与工程实践通过产品和服务带给客户。

软件工程的故事

软件工程的产生:软件危机

20 世纪 60 年代以后,硬件技术快速发展,第三、四代计算机陆续产生,软件从计算机实验室逐渐走向更广阔的的军工、商业、民用场景。在这个转变的过程中,软件也逐渐开始脱离硬件独立销售,软件的重要性逐步提升超过硬件,其复杂性也愈发凸显。

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

●  从软件产品(软件开发的产物)维度来看,研发成本过高、质量不可靠、难以维护、开发效率/产能跟不上硬件的发展及用户需求的增长,成为核心的难题;

●  从软件项目(软件开发的过程)维度来看,成本超预算、计划严重延期成为常态。1975 年 Brooks 的《人月神话》,提出了“焦油坑”和“银弹”的说法。时至今日,软件工业仍然在寻找“银弹”的路上。

在此等复杂性的背景下,这个阶段软件开发技术与方法的发展却没有跟上。愈发复杂的需求、场景与环境倒逼着软件开发技术与方法的交织发展。

软件工程第一次被提出是什么时候呢?1968 年,北约的计算机科学家第一次提出了软件工程的概念,希望通过系统化、规范化、数量化等工程原则和方法来实现复杂软件系统的开发和维护。

以下为《nota1968》中的部分描述摘录,五十年前的观点,如今看来仍然鲜活:

关于软件项目管理:

软件开发管理将继续背负目前的在成本和计划有效性上的坏名声,直到有朝一日,人们对软件设计工程有了更全面的理解和认识。

我们开发软件系统就像莱特兄弟制造飞机——造好整个系统,把它推下悬崖,让它坠毁,然后重新开始。

管理工作方面,大型软件的开发是一个令人恐怖的事情。人们的认识中,这种工作通常会成为血本无归的泥潭,耗费财力,永无止境。人们的这种认识也许并不是偏见。

关于用户需求:

用户感兴趣的是对系统提需求,而且按照需求购买系统。但这里的潜台词是用户能说出他们想要什么。而大部分的用户说不清楚。

我们应该在设计过程中尽早的获取用户反馈。

软件工程的本质性难点:四个特性

软件工程的本质性难点到底在哪里?让我们引用转述一下《人月神话》作者 Brooks 在 1986 年的论文《No Silver Bullet》中的观点。布鲁克斯认为,附加性的困难会随着工具的改善而逐渐淡化,反而是本质性的困难最难以解决,因为大部分的活动是发生在人们的脑海里,缺乏有效的辅助工具。依照布鲁克斯的说法有下列几项:

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

●  复杂性(complexity):软件要解决的问题,通常牵扯到计算步骤,这是一种人为、抽象化的智能活动,多半是复杂的。

●  隐匿性(invisibility):尚未完成的软件是看不见的,即使利用图标说明,也常无法充分呈现其结构,使得人们在沟通上面临极大的困难。

●  配合性(conformity):在大型软件环境中,各子系统的接口必须协同一致。由于时间和环境的演变,要维持这样的一致性通常十分困难。

●  易变性(changeability):软件所应用的环境常是由人群、法规、硬件设备、应用领域等,各因素所汇集而成,而这些因素皆会快速变化。

对软件工程的定性:综合性学科

Brooks 在他的论文《No Silver Bullet》中表示:不存在任何一个单一的开发技术或管理技术能够解决软件工程所面临的所有问题。因而软件工程是一个包括一系列概念、理论、模式、语言、方法以及工具的综合性学科。

所以,软件工程,或者说软件的实现过程,实际上是一个综合性的学科。涉及到技术手段、管理方法和内外部环境等因素。

旧世界的软件工程:瀑布与 CMM

洛克希德软件技术中心的 Winston W. Royce,在其 1970 年的论文“Managing the Development of Large Software System”中提出了一个长得很像瀑布(waterfall)的流程。

按照科学管理的理念,只要按照标准流程、标准动作来,就能产出高质量软件、解决软件危机,这就是最佳实践。然而真的是这样吗?旧世界的软件工程并没有给出答案:

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

●  很多软件工程著作并没有对软件开发的日常实践给出具体的指导,结果到实际工作中就变成了“边做边改、边改边做”;

●  CMM 评估流于形式,例如评估小组只询问文档在哪里而并不真正关注文档的内容是否恰当以及文档本身的价值;

●  CMM 所关注的“成熟度”与软件开发过程的“有效性”脱节,通过了 CMM5 级认证的团队照样会开发出没人买单的糟糕软件。

旧世界软件工程的灵感来源:泰勒主义

说到这里不得不提的是科学管理之父泰勒和他的科学管理理论。科学管理、泰勒主义的理念是:

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

少数精英负责制定标准的工作流程、工具操作、工艺质量,再由管理层完成监督和管理工作,大量的蓝领工人可以不用动脑子、不思考地就可以完成符合质量标准的工作,从而产生合格的产品。我想大家一定都记得 K12 课本里出现过的一句话:“我需要的是一双手,而不是一个人”。

客观上来讲,科学管理的提出和应用确实带动了工业制造的发展提速,解决了生产力不足、无法满足市场需求的问题,这也是工业革命后不可逆的趋势。但科学管理也并非永远是万能的。

后科学管理时代的探索:敏捷

20 世纪以后,行业面临的挑战从“生产力不足、无法满足市场的功能性需求”逐渐转变为“灵活性不足,无法满足消费者对创新和定制需求”。“快速、残酷与不确定性的变化”将是未来制造业面对的常态挑战。每个行业都开始强调变化,这个问题的本质直到今天仍然适用。

而对于软件工程领域来说,Agile/敏捷,就是关于企业如何在一个动荡的、竞争激烈的经营环境中获得利润。跟“科学时代的软件”的区别在于:

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

● 对市场的需求和认知。承认变化、接受变化,不认为客户需求可以一次性集中研究然后完全获取。

● 对生产的态度和工作方法。接受错误、愿意试错、快速调整、持续优化。

● 对生产资料的态度。承认个人的能力与价值,“让一线呼唤炮火“,而不是“精英定规则,生产线上只要一双双手”。

当然,以上并不是完全否定瀑布与 CMM 的价值和意义。只是,在现下的世界与环境中,CMM 与瀑布并不能完全解决我们的问题,所以我们仍然不得不继续寻找“银弹”。

认识 Agile,敏捷的诞生

众所周知,2001 年 2 月,在盐湖城外 25 公里的雪鸟城(Snowbird),一个滑雪胜地,17 位业界先锋聚在一起,制定并签署了行业历史上最重要的文件之一:敏捷宣言。随后,他们共同商讨出 12 个敏捷原则,对宣言进行进一步的解读。

敏捷,另一类软件工程方法论

方法论,是关于“如何开发一个软件系统”的行为框架,包含如何组建团队、如何做项目计划、团队成员及跨团队如何交流、如何管控项目进度与质量。

在“雪鸟会议”之前,已经存在了很多这种被称为“轻量级(Lightweight)”的软件开发方法,雪鸟会议的结果是正式地将这些方法统称为“敏捷(Agile)”软件界开发方法。

一些“敏捷”的软件工程实践方法

不同敏捷方法的共同特征

不同的新方法有着一些共同的特征。例如,强调适应性而非预见性。所以敏捷主义者会说“拥抱变化”,敏捷宣言中也有那句“响应变化重于遵循计划”。

可能有人会说,既然这样,那充满变化、不按计划、没有边界,那项目还怎么做?敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

再比如,强调面向人,而非面向过程。强调人的作用与价值。所以敏捷宣言中会说“个体和交互重于过程和工具”。

也可能有人会说,那是不是敏捷就可以不写文档了?其实并不是完全不写文档了,而是为了减少浪费,减少准备文档、沟通传递信息、理解文档这个过程话费的时间和人力。不在对客户没有价值的东西上面浪费时间,这是“敏捷不写文档”的真实含义。用这个原则指导,用户故事/需求、架构图、复杂业务/计算逻辑等都是无法从代码中看出来的,这就是需要写的。而高保真设计、数据库表结构、伪代码等就可以精简或者不写。

敏捷的出发点

敏捷方法的出发点,是运用一些更有效的方法(工程实践),交付高价值、高质量的软件。

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

什么是高价值的软件?产生商业价值的能力更强。所以在使用用户故事这一工具时,它的描述规格是“可以实现 xxx 商业价值”。

什么是高质量的软件?更稳定、更灵活、易维护、易扩展。

敏捷实践的总结:通过可落地的、紧密配合的一系列敏捷实践,来产生高价值、高质量的软件。

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

什么是紧密配合?举例来说,简单设计后如何进行进一步的优化?那就必须使用重构。如何能让重构成为一种经常性的、快速有效的开发方法?单元测试代码的开发就是必不可少的步骤,而测试驱动开发就是其最佳实践。如何让持续集成跑的顺利?必须推广统一的编码规范,并且还得有自动化测试用例集。

敏捷常见工程实践

● 用户故事,让需求认知过程成为一种协作方式

敏捷软件开发方法的需求梳理工作,一般是通过用户故事的形式来开展的。

在《用户故事实战》一书中,将 US 的特性概括为”3 个 C”:Card、Conversation、Confirmation。后面两个 C 体现的就是 US 作为一种写作方式的定位(而非传统需求文件试图去描述完整需求)。

另外,从需求的传递与跟踪来看,Story Backlog 和 Kanban 都是常用的工具,但各有其应用场景。从最初产生的场景来看,Kanban 更适合连续性的、源源不断的工作项跟踪,而用户故事 Backlog 是以迭代为单位,分批去消化不同的需求。

● 重构,在不改变软件行为的前提下优化软件内部结构

重构是敏捷软件开发方法里非常核心的实践之一,强调的是在不改变软件行为的前提下优化软件的内部结构。一个小小的重命名工作,通过“如烹小鲜”般的一系列动作来完成。看似琐碎,却严谨、小巧、可控。通过这一系列的操作,使得重构的难度(对原有代码可观察行为的影响)大大降低。特别是在复杂的软件项目中,这种极高的可操作性会体现它的巨大价值。

在 IDE 高度进化的今天,类似这种重构的场景,甚至更多复杂的重构场景,都可以借由现代 IDE 的内置功能去完成,重构变得更加简单、快捷、好用。

● 单元测试/自动化测试,让不断重构成为可能

重构可以在保持行为不变的前提下改善软件内部质量。但如何检验软件的行为是否变化,只能靠测试。试想一下,如果你在通过重构来完成代码的开发,这个过程如何检验软件在修改前后是否真的“行为不变”?只有通过测试。而高频重构/修改,则意味着需要高频的测试。高频的测试,自动化是必然。

那是否自动化了就够了,或者说如何更好的做自动化测试、单元测试、契约测试等等自动化测试工作,它们之间又有什么区别与联系?这又是另外关于自动化测试的敏捷实践方法。

● 持续集成,以配置/源码管理为基础

如何保障软件质量?XP 的方法是,从可观察性区分,外部质量靠自动化测试,内部质量靠重构。但对于一个中大性项目团队,在通常为期数月甚至以年计的交付过程中,如何坚持下来?这就要靠持续集成。持续集成的工程实践包括一系列的工具和方法:

敏捷方法是需要用实践方法来解决这些问题。比如响应变化这一点,XP 方法中,如果(迭代中的)一个用户故事还没有实现,允许可以考虑用另外的用户故事替换,替换的前提的工作量是相等的;而 Scrum 强调一旦开工会定下来,需求就不允许被添加进来,并且需要 ScrumMaster 来把关。

● 通过代码/配置管理工具,将代码集中版本化管理起来;

● 通过代码分支管理策略,建立起适配项目特点的分支/版本管理方法;

● 通过 CI 工具,完成构建过程的自动化,建立持续集成流水线;

● 通过单元测试、自动化测试工具和方法,保证持续集成的质量和可靠性;

● 通过将 CI/CD 过程中的非代码部分的必要产物(如 Shell 脚本)也版本化,来保证整个持续集成过程的自动化、稳定性和可重现性。

● 迭代,旧时代瀑布模型的升级版

团队需要在指定的时间段内完成一系列工作项的过程称为迭代。迭代的持续时间一般由团队和产品所有者决定。

在 Scrum 框架中,迭代被称为 Sprint(冲刺)。而 Scrum 的项目过程有一系列的 Sprint 组成。Sprint 的长度一般控制在 2-4 周,通过固定的周期保持良好的节奏。产品的设计、开发、测试都在 Sprint 期间完成。在 Sprint 结束时交付可以工作的软件。整个 Sprint 的过程中(计划会议确定下工作项内容后)不允许发生变更。

Agile 和 CMM 的本质不同是什么?

一般意义来讲,CMM 更多的是一种预定义的、固化的模型。而 Agile 是一种经验性的、弹性的模型。软件开发,本质上是一种解决复杂未知问题的过程,解决这种问题用经验性过程会更加有效。

但在 CMMI 目前较新的版本中,也增加了对使用敏捷方法的组织的指南。以下 为 CMMI V1.3 中对于敏捷的描述片段。

Scrum

XP

用户故事

认识 DevOps

从 Agile 到 DevOps,区别是什么?

从研发管理的视角来说,Agile 已经涵盖了流程、方法、实践、文化,甚至一部分工具。Scrum 框架其实就是一些轻量级的流程方法的集合。而 XP 则更多的强调了很多好的工程实践。敏捷宣言本身就是敏捷文化的体现,甚至自带敏捷先驱们的浪漫主义情怀。而持续集成这一工程实践不可能脱离工具而存在。但 Agile 的各家言论并没有真正把“尽快交付业务价值”的最后一公里走完,所以完整的工具链,以及生产环境管理要到 DevOps 中去寻找答案。

而从应用管理的视角来看,我们在实践 DevOps 的时候,多半也会去使用很多 Agile 中的流程、方法、实践。比如我们做需求的时候经常会用到用户故事、看板;做设计的时候也会强调简单设计,避免过度设计;整个开发过程使用迭代的方式去管理、测试和部署时会使用持续集成并遵循一定的分支及流水线管理策略等等。而在上线包括上线之后,则会多出一些 Agile 中 没有说的特别清楚的重要内容,Ops。

通过上面的观察,我们会发现 Agile 和 DevOps 是密不可分的,并且他们本质上都是在做一件事情,就是软件工程。

当然,对于不同的行业,DevOps 还是可能会有很多不一样的地方。比如在强监管的金融机构,测试环境和生产环境之间难以逾越的高墙可能一直会存在。而这在很多互联网公司或普通企业来讲就不是一个特别要命的问题。

无论有什么样的差异性,无论我们把正在做的事情是叫 Agile 还是 DevOps ,甚至未来又出现什么新的名字,其实我们都在解决软件工程领域的问题,质量、效率、成本铁三角是这个领域永远的命题。

工具链集成,建立高效开发协作体系

工具链的集成构建是 DevOps 落地过程中非常重要的一项工作。整个工具链的串联会涉及到很多工具。我们可以从两个维度列举一些常见的工具:

研发工程维度(软件交付过程):

  • 项目协同:Jira、BeyondDevOps Synergy;

  • 代码托管:GitLab、Bitbucket;

  • 制品管理:Artifactory、Nexus、Harbor;

  • 构建管理:Maven、Docker、NPM;

  • 代码质量:Sonar、Junit;

  • 自动化测试:JMeter、Selenium;

  • 流水线工具:Jenkins、JFrog。

基础设施维度(周边配套系统):

  • IaaS 平台:VMware、Openstack、BeyondCube;

  • 容器管理:Kubernetes、BeyondContainer;

  • 部署平台:Ansible、Python、Shell、BeyondStage;

  • ITSM:CMDB、CMP、BeyondCMP、BeyondCMDB;

  • 日志平台:ELK、EFK;

  • 自动化监控:Zabbix、Prometheus。

可编排的流水线

如果没有流水线,就无法完成自动化的 CI/CD 过程,“持续”二字也就无从谈起。以博云的 BeyondDevOps 产品来说,基于 Jenkins 提供了产品、服务两级流水线自定义编排能力。

同时通过任务模板,通过“流水线、步骤、任务”多级模板、提供“乐高”式的流水线搭建体验。并且平台已经内置各种常用模板:从代码构建、代码扫描、制品/镜像拉取、自动化测试、到制品部署各个环节。如果已有模板并不满足使用,也可以通过自定义模板功能来实现新模板的开发和接入。

代码分支管理规范

代码分支策略及分支管理规范,是 DevOps 中 SCM 领域的核心实践。常见的代码分支策略包括 Git Flow、GitHub Flow、TBD Flow 等等。不同类型的公司、产品、系统、项目,适用的分支策略也未必完全相同。好的 DevOps 平台,可以适配不同的代码分支管理策略,帮助客户通过平台和工具链完成 SCM 的整体管理,比如在某些场景下,可以将某种分支管理规范形成一个 Template 固化到平台中,帮助研发团队降低复杂分支策略的使用门槛。

制品管理规范

制品是软件开发过程的核心输出物,也是产生最终业务价值的载体。对于企业来说,建立组织级的统一制品管理体系是非常必要的。其中包括制品的命名标准、二方包管理规范、三方包管理规范、生产/非生产制品包管理规范,还包括制品如何晋级,以及相应的安全策略等。

自动化生产级应用发布过程

生产环境的发布和运维,是一个非常严谨的工作。相对于大多数企业和互联网公司来说,金融机构对于生产发布的要求会更高。包括上线申请单、资源协同开通、制品管理、可定义的自动化发布过程、应用配置中心等。特别是在很多传统金融机构中,生产环境发布还意味着多部门配合、多系统调用、多网络环境协同等等工作。博云的 BeyondDevOps 整体解决方案就是在这种复杂的业务场景中产生的。

博云 DevOps 整体解决方案概览

咨询+产品+实施

DevOps 作为目前软件工程界最佳实践的集大成者,确实是炙手可热的研发管理方法。但只要是管理,就有万千不同。对于向落地 DevOps 的团队和组织来说,所处行业、组织文化、软件形态、团队现状、内部环境等等因素的不同,都会导致其研发管理和工程实践的不同。

如何在充分考虑现实因素(目标、时间、成本、资源)的情况下,去选择一个切实有效的路径和方案,是落地 DevOps 项目成功的关键。所以对于一个 DevOps 项目来说,并非买一套产品就万事大吉。博云基于自身在金融、工业、企业、政务等行业的经验,总结了一套可以为不同行业客户落地的交付方案,愿意与各个行业的朋友持续交流切磋。

项目建设路径

罗马不是一天建成的,DevOps 作为研发管理之重器,也并非一朝一夕就可以建设好、运用好。对于大多数的组织来说,完整 DevOps 体系的建设和应用之路需要做好规划,特别是有一些企业对于 DevOps 除了实践应用之外还有过信通院能力成熟度模型的需求,还要为过级评估工作留出充分的时间。

在目前很多企业年度预算制的开支框架下,DevOps 项目需要做好规划与路径,做到有明确的短期、场景的目标与实现路径,才能更好地完成项目,达成预期。

博云 BeyondDevOps 平台开放试用,欢迎访问bocloud.com.cn,或扫描下方二维码或点击文末阅读原文申请试用。

深入思考软件工程,开启 DevOps 之旅的更多相关文章

  1. Win从环境变量开启MySQL之旅

    Win通过环境变量开启MySQL之旅 这篇文章主要介绍了Windows7下如何在命令行使用MySQL的相关资料,需要的朋友可以参考下 我在Win7下安装的MySQL版本是mysql-5.0.22-wi ...

  2. 开启RxSwift之旅——开篇

    开启RxSwift之旅——开篇 RxSwift 是 ReactiveX 在 Swift 下的实现.ReactiveX 是一个通过使用可观察序列来组合异步和基于事件的程序的库. 很多地方通常把 Reac ...

  3. Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅

    原文:Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅 在前几期中总结分享了Android的前世今生.Android 系统架构和应用组件那些事.带你一起来聊一聊Android开发 ...

  4. SpringBoot:1.开启SpringBoot之旅

    什么是 Spring Boot Spring Boot是Spring团队设计用来简化Spring应用的搭建和开发过程的框架.该框架对第三方库进行了简单的默认配置,通过Spring Boot构建的应用程 ...

  5. 开启Github之旅

    在那个远古时代,我以为可以用GoogleCode干点事,结果啥也没干好.如今,Github已经成为了业界标杆,就连Google.微软.Facebook的开源项目都往Github搬.Github作为全球 ...

  6. 开启Java之旅

    学习应用系统的服务器开发,也许并不算什么“旅行”,也不会那么‘愉快’.但是,我希望这次能够同以往有所不同,更加努力地学习J2EE. 从2月份开始,从事web前端开发,并在公司的的项目中,独立完成了4个 ...

  7. (一)C#编程基础复习——开启编程之旅

    回想当年学习编程,刚开始学习是非常艰苦的,可能是因为文科生原因,刚开始接触工科类的知识不是很擅长,上去大学第一年基本没有好好学习编程,入门C#编程基础一窍不通,也许那时年少无知,第二学期开始奋发图强, ...

  8. 5分钟开启Esper之旅

    原作者:CoffeeOneSugar 翻译:刘斌华 在我之前发表的文章中,我提到我最近热衷于Complex Event Processing (CEP) (复杂事件处理).简单来说,CEP把数据流作为 ...

  9. 开启Laravel之旅的标准姿势

    1.github下载最新的laravel https://github.com/laravel/laravel 2.下载到本地,改名,composer install,安装项目的依赖包 compose ...

随机推荐

  1. Workflow Core + asp.net core 5.0 实现简单审批工作流

    我们知道企业业务系统到处都可以审批工作流的,但也很少有像OA系统一样复杂多级多条件的审批工作流需要设计,所以我们需要一个轻量级的容易上手的workflow框架,通过GitHub,我发现danielge ...

  2. Go依赖包管理--间接依赖

    目录 1.indirect含义 1.2 直接依赖未启用 Go module 1.2 直接依赖 go.mod 文件不完整 2.总结 1.indirect含义 在使用 Go module 过程中,随着引入 ...

  3. Spring Mvc原理分析(一)

    Servlet生命周期了解 Servlet的生命(周期)是由容器(eg:Tomcat)管理的,换句话说,Servlet程序员不能用代码控制其生命. 加载和实例化:时机取决于web.xml的定义,如果有 ...

  4. java的stream的使用

    过滤 filter: //匹配第一个元素 Optional<Integer> findFirst=list.stream().filter(x->x>6).findFirst( ...

  5. Tolist案例(父子传参实现增删改)

    1.Tolist案例(父子传参实现增删改) 目录结构 实现效果: App.jsx class App extends Component { // 状态在哪里, 操作状态的方法就在哪里 state = ...

  6. 从零开始实现简单 RPC 框架 7:网络通信之自定义协议(粘包拆包、编解码)

    当 RPC 框架使用 Netty 通信时,实际上是将数据转化成 ByteBuf 的方式进行传输. 那如何转化呢?可不可以把 请求参数 或者 响应结果 直接无脑序列化成 byte 数组发出去? 答:直接 ...

  7. SDOI2021集训 R1 半夜 题解

    先贴两个博客:ajthreac yspm,建议结合起来看 \(O(n^3)\):对 \(XX\) 每个长度为 \(n\) 的字串与 \(Y\) 跑 LCS.设 \(f[i,j,k]\) 表示 \(X[ ...

  8. Devexpress主题/皮肤

    如何在C#中使用DevExpress皮肤管理器.步骤1: 单击 新建项目,然后选择左侧的Visual C#,然后 视窗 ,然后选择 Windows窗体应用程序.将您的项目命名为" SkinD ...

  9. Spring基于XML方式加载Bean定义信息(又名:Spring IOC源码时序图)-图解

  10. vue-element-admin 全局loading加载等待

    最近遇到需求: 全局加载loading,所有接口都要可以手动控制是否展示加载等待的功能 当拿到这个需求的时候我是拒绝的,因为我以及局部写好了0.0,这是要大改呀....,没办法老板的要求,只能硬着头皮 ...