从优化研发交付流程的角度,如何根本上提升研发效能

先说结论;研发效能现状;特性分支开发模式;聊聊 WorkFlow 流水线:高度自动化;聊聊 CI WorkFlow 流水线;离梦到来还有多远

写在前面:作者李子昂,阿里巴巴集团研发效能部的第一个算法工程师,目前工作主要方向是代码管理和CI。本文探讨的是:从优化研发交付流程的角度,如何根本上提升研发效能。

先说结论

现在阿里主流的分支开发模式,以及研发工具中流水线的设计,导致CI和CD流程是捆绑在一起的,生产环节发布成功,自动将分支合入master,也就是说完成一次CD的同时,才进行一次CI。

这么做也有这么做的好处,你可以保证master上的版本永远和线上代码版本是保持一致的。但是这么做却大大降低了ci的频次,频次越低,体量也就越庞大,单次ci几千行的体量,在阿里内部,倒逼着衍生出了强大的全量回归测试工具,甚至在code Review中按照依赖链路进行分拆评审的“黑科技”。

根据混沌理论,改变足够小的时候,结果是可以预测的。我发布一个两行代码的改动,一个单侧就是妥妥覆盖。但是当很多个这样的改动叠加在一起时,结果就变得越来越不可知了,连锁反应可能会造成意料之外的故障。这样一个CI上线的过程中,很多时候测试同学将它当成一个黑盒来测试,全量的去检测所有对外提供的接口,导致测试流程繁重,一旦出现问题,折返路径也相当漫长。

众所周知,Google是使用大库模式和主干开发的,每一次修改都会很快地合并到master中。高频的CI和直接合入master的高风险,对CI的质量提出了更高的要求,所以诞生了Google的强制CodeReview和重视代码质量的工程师文化。当然这也和Google只要使用C++和Go,和语言特性有关,所以大库+主干在java中的实践也是未来可能会面对的挑战。

怎么把CI和CD解耦开呢?以制品为分界,解绑CI/CD,释放CI灵活性。在CI流水线的终点,可以把代码的编译产物——制品,存放到制品仓库中。而CD流水线直接选择合适的制品,进行发布。

在一些自动化测试,扫描和代码分析的服务的辅助下,配合CodeReview,用高频高质量的CI提高代码质量,代码要被review,开发对自己的作品自然会更加有羞耻心和owner意识。用这种研发交付流程的改变去降低代码腐化,鼓励重构。

这可能是从研发模式上为系统腐化踩刹车的办法。

研发效能现状

其实大部分团队的研发效能并没有大多数同学想的那么糟糕。尽管需求交付速度上来看并不快,但只关注单指标,难免管中窥豹。

人类是唯一可以长时间奔跑的动物。人的身体和耐力非常适合跑步,但是就算是最顶尖的运动员,随着跑步距离的增加,平均时速也会慢慢下降。这种现象,叫做“折中定律”,提升一方面,就要牺牲另一方面

从优化研发交付流程的角度,如何根本上提升研发效能

如果有人让你跑得再快些,你可以答应下来,只要不需要跑很长的距离就好。或者,如果需要长距离选手,那你也可以跑得更远,只要愿意跑慢一些。但你不大可能找到一个既能跑得更快又能跑得更远的人,也没法让一个长跑运动员跑得很快。同理,我们不难理解,随着软件复杂度的上升,交付速度的下降也符合折中定律,是自然而然的。

从优化研发交付流程的角度,如何根本上提升研发效能

我称之为:“码烂故我慢定律”。

从另一个角度使用折中定律,也很合理,如果你让我今天就以速度为第一优先,放弃一些代码质量去奔跑,长期来看对平均时速反而是有损害的。所以,导致研发速度下降的最主要原因,是软件复杂度的膨胀。谷朴谷子哥有说过:“每新增一行代码对软件系统来说都是负担”。那么如何控制系统复杂度呢?

代码质量腐化是效能最大的敌人–《clean Code》书摘

有些团队在项目初期进展迅速,但一两年之后就慢如蜗行,进度缓慢程度严重。对代码的每次修改都影响到其他两三处代码。修改无小事,每次添加或修改代码,都得对那堆扭纹柴了然于心,这样才能往上扔更多扭纹柴。这团乱麻越来越大,再也无法清理,最后束手无策……这就是混乱的代价:

随着混乱的增加,团队生产力持续下降,趋向于零。花时间保持代码整洁不但有关效率,还有关生存。制造混乱无助于赶上期限。混乱只会立刻拖慢你,叫你错过期限。赶上期限的唯一方法——做得快的唯一方法 ——就是始终尽可能保持代码整洁。写整洁代码,需要遵循大量的小技巧,贯彻刻苦习得的“整洁感”。这种“代码感”就是关键所在。简言之,编写整洁代码的程序员就像是艺术家,他能用一系列变换把一块白板变作由优雅代码构成的系统随着混乱的增加,团队生产力持续下降,趋向于零。员工工作成就感也随之降低,离职率上升,让生产力进一步下降。当生产力下降时,管理层就只有一件事可做了:增加更多的人手到项目中,期望提升生产力。

可是新人并不熟悉系统的设计,他们搞不清楚什么样的设计符合设计意

从优化研发交付流程的角度,如何根本上提升研发效能

系统复杂度的无限熵增,让代码改动变得困难,系统迭代变得缓慢,开发成就感降低,负向飞轮一旦转起来就向无尽的加班和越来越低的效率狂奔。

系统本身的复杂性是无法避免的,代码本身需要和现实世界交互,而现实世界本身就是复杂的。但复杂的代码也可以是简洁的代码,好的代码质量可以大大降低系统的腐化。

换句话说,烂代码是研发效能最大的敌人

怎么做?提高CI质量,形成正向优化的飞轮

对于每一位程序员来说,每一次更改代码,都让代码库比你修改前更整洁。

这句话的思想类似于,勿以善小而不为,勿以恶小而为之。我相信大部分程序员都是有操守的,也愿意护卫代码的整洁。但是如何让这样的努力被看到,让好的代码被看到,让优秀的抽象被认可。

如何形成重视质量的文化呢?让一线同学认可,让管理者看见一线同学为维护质量作出的努力。

来看看Google的答卷。大库开发+主干开发模式。大库开发,指所有的代码放在同一个代码库中。主干开发,指开发不能拉分支,只能尽快将自己的修改推送到master分支上。这样的风险显而易见,一旦一个开发把带有Bug的修改push到master上,所有人rebase的代码都是有Bug的,所以对提交代码的代码质量提出了极高的要求。

Google通过CodeReview文化和强力的CI自动化测试工具支撑了这样的质量体系。每个团队还会有专门的组,一旦有问题的代码被push到了master上,要像处理故障一样回滚和复盘,优化自己的CI质量监控体系。

仔细想想,这样一套系统自有他的合理性,主干模式开发让代码冲突的问题从低频大问题,变成了高频小问题。这样一次需要CodeReview的代码量也不会过多。同时也避免了代码重复的问题,一是大库代码对大家都可见,二是主干模式更新及时避免了出现并行开发雷同代码的情况。

对于研发平台方和业务一号位来说,如何设计出这样的研发流程,和能支撑这样研发模式的研发工具,是形成飞轮的关键。

解绑CICD,释放CI能力

对于平台方来说:

提供CI流水线,类似gitlab-runner能力,为研发托管测试devops。让CI与CD解绑,释放CI的频率和能力,通过CodeReview机制,辅以扫描和自动化测试,支撑团队建立适合自己的CI流水线,提升代码质量。提供测试托管能力,提供测试集群auto-pilot能力,为CI流水线自动运行测试任务,通过任务托管优化测试资源利用效率。对于业务团队来说:

找到适合自己的CI研发模式,建立质量保证体系。燎原哥的文章对这一块有不错的讨论,我对团队管理经验完全没有,就不展开了。

Google的最佳实践-大库研发模式和主干研发模式

并不是说Google的就是好的。但是一个自洽的生态必然是有他的合理性的,就像分支研发模式也有自己的优势,在java场景中,起码在阿里的环境里,分支模式还是有自身的优势的。他山之石,可以攻玉。

特性分支开发模式

特性分支开发模式是指为一个或多个特定的需求或者缺陷创建代码分支branch,在其上完成相应的开发后,把它合并(merge)到主干 / 集成分支的开发模式。通常这种分支生命期会持续从几天到几周不等的一段时间。

优点:

分支互相隔离,可以在宽松的时间内完成对自己分支的测试再合入主干;冲突合并次数少,只需要在合并主干的时候进行一次合并;缺点:

分支管理复杂:原因在于大量采用代码分支,且来源分支和合入目标分支各异,操作复杂合并冲突多、解决难:分支生命期越长,意味着与主干的代码差异越大,冲突概率越高,冲突的解决难度越大,不论哪种开发模式,其实解决的冲突总数是一定的,分支管理减少了解决冲突的次数,自然需要解决的冲突也就越复杂;多测试环境可能会被不同分支抢占,各个分支互相隔离所以代码的测试也需要分开,会对相应的测试环境有抢占的情况;

主干开发模式

主干开发,是指开发人员直接向主干分支上推送代码。通常,开发团队的成员 1 天至少 1 次地将代码提交到主干分支。在到达发布条件时,从主干拉出发布分支(通常为 release),用于发布。

流程:

从优化研发交付流程的角度,如何根本上提升研发效能

优点:

分支模型简单高效,开发人员理解成本低分支合并、冲突解决更高频但更容易解决随时拥有可发布的版本有利于推动高质量的持续集成和持续交付缺点:

合入到主干的代码若质量不过关将直接阻塞整个团队的开发工作,因此需要高质量的CI测试和Code Review工具已经开发文化的支持对自动化测试要求高,需有完备单元测试和增量测试能力,确保在代码合入主干前获得快速可靠的质量反馈,大大降低人肉Bug发现和排查的工作量;以制品为分界的CI/CD结耦,优化研发交付流程

CI的流程的最后,代码被编译成制品存入制品仓库,CD流程从制品仓库取制品直接部署。以制品仓库为边界,将CI流程和CD流程结耦开。

制品可以是C++构建后打binary,也可以是k8s中可以直接部署的容器副本,贯彻GitOps的思想,是阿里目前可见的未来的研发基础设施基石之一。

目前是阿里巴巴研发效能部在负责这部分工作,云上则有云效团队正在研发同样的云上产品。另外就是可以支撑这样高频高要求CI的CI工具,类似CI流水线。

聊聊 WorkFlow 流水线

人靠谱么?我靠谱么?为什么要DevOps,为什么要自动流水线

我一直搞不懂为什么要搞那么多权限审批。最近突然一堆权限加到了我头上。我发现,自己心情好的时候,会认认真真的盘问申请原因,提醒申请错了表的同学及时更改。但一忙起来,那是真的闭着眼睛过。之所以说这个事,一部分是我在忏悔我对安全不够敬畏,另一方面这也让我深深的意识到一个问题。

人是系统最大的漏洞。

众所周知,贝叶斯定律是机器学习的基石,好比穿起羊肉串的那根签。比起笨重的机器,人脑才是最擅长用先验概率来偷懒的鸡贼玩意儿。反正之前那么多次都没出事,似乎顺着老路走总没错。估计在座的读者应该都被「休谟的叉子」叉过。记不起来的话我可以举几个例子提醒一下你:换了宿舍,开学还是顺着肌肉记忆走到了之前的寝室。给手机app位置整理了一遍,刚打开的那几次都得找半天。

贝叶斯公式没有错。人的大脑也确实很鸡贼。如果没有这种惯性经验,那这日子可过不下去了,要考虑的事情太多。但是经验会犯错,大概率成功并不是100%成功,有的时候环境发生了细微的变化,但是经验却不能敏锐的感知。其次,经验的传递非常困难(各位如果是第二基地的读心术士那当我没说),为什么高的人员流动会严重加速系统腐化和技术债务?因为太多的信息和知识存在了人的经验里,他已经是保持这个系统正常运转的一个部分了,某个小bug经常会触发,他自己知道某个后门可以定时处理订正,但是因为他觉得太低频就没有做成自动化任务。这种玩意儿一旦转手就变成定时炸弹。

“天猫精灵,放一首《杀死那个石家庄人》”

workflow 流水线

还是先围绕着权限申请说吧。人一个个看过去听起来很靠谱,一旦量大了,这件事还靠谱么?反倒是,制定一些规则,比如某些部门的人员可以自动赋权。或者他已经有某些高权限了,在申请相关低权限表可以自动通过。

可能你会说这个规则也太粗糙了。但是,规则是可以迭代的,可以成长,变得健壮的。而且一旦迭代到一定程度,自动化带来的便捷和安全稳定可以让同学们从重复劳动中解放出来。

可迭代,自动化,这就是流水线的key Point。

action 组件

组件是一种即是一种接口也是一种实现,是对底层资源的封装,把复杂的一坨事变成一个好用的按钮。

以CI/CD流水线为例,比如部署组建,就是把底层的机器资源封装了一层。

再比如自动化测试组件,替我运行我的测试任务,而我不需要去折腾测试任务的资源和任务的调度,很优雅的设计和封装。

一个好问题:什么是好的流水线?

相信大部分读者看到这儿应该和我一样犯病了。

犯了程序员的职业病,我称这个debuff为「最佳实践缺乏症」,看到啥问题都渴望看到最佳实践。

可惜我也只是病友,但是有次和燎大师吹逼的时候提过一个观点,我深以为然。

如果我把代码里的一行return true改成return false, 然后我运行CICD流水线,如果它能在发到线上前被拦住,那么这就是一条健壮的流水线。

这句话很理想,很难实现,但是也很好地点出了可爱的流水线的特质。

高度自动化,包括优秀的自动化测试能力,越少的人工,系统发挥越稳定。

可迭代,如果某个问题被系统漏掉了,那么他可以被用来迭代这个系统,直到有一天它不在漏掉这个问题。

单测,回归测试,不仅是对稳定性的保障,也是对外部系统的一种承诺。不管内部代码如何变换,甚至从零重写了,只要之前的单测和天启之类的回归测试可以通过,那对于外部系统来说就没有任何改变,类似闭包和接口的概念。那么对于应用来说,一条稳定的,测试完整的流水线,是重要的技术资产。

一个更好的问题

你可能会想,那我天天就迭代流水线不得累死,问题是搞不完的,万一它真的漏一个bug到线上,算谁的责任呢?似乎迭代出一个完美的,完全自动化的,所有case都考虑到天衣无缝的终态流水线是一个伪命题。

说到替人做决策,不得不提机器学习。在to B算法产品设计中,有一个原则,我觉得在自动化系统中也适用,套过来可以说:

自动化系统要做到human In Loop,它辅助人类完成繁琐的工作,但不是替代人,人可以把精力投入到更有价值的工作中去。

我相信,在目前的水平来看,就算是最优秀的机器学习算法,最稳定的自动化系统,精准度并不会比行业精英的决策判断更加准确。但是它可以很好的解决效率问题。比如目前的自动驾驶,在安全稳定的情况下提你开车,但是出现紧急情况还是需要人来判断。

在流水线这件事上,把可以自动化的部分尽量交给自动化工具。在代码设计之类的对复杂场景,使用code review之类的方式,通过人来解决。

可能你会问,这不还是要人来搞么?但你想想,提一个code review,结果对方让你改一个变量名大小写的错误或者代码规约对问题。再提一次,过了cr结果测试挂了一个,改完有要cr。乒乓review,是不是很痛苦。

在人类社会提出机器人劳动保护法之前,还是先把这些繁琐的工作交给电脑吧。

聊聊 CI WorkFlow 流水线

说真的流水线真不是什么新鲜玩意儿了。阿里aone的流水线相信各位都是低头不见抬头见了,大部分公司都有成熟的流水线用来发布。但是最近gitlab action又又又火了。

github action

Action是单一的动作可以执行某些功能,比如部署action,代码合并action之类的,把动作组合编排起来则能完成一个有意义的事情,即Workflow

从优化研发交付流程的角度,如何根本上提升研发效能

Action类型分为两大类:

1. Github上的代码库,这里又可以分为3种情况:

– Github pre-built action:Github官方创建的action,主要分为3种类型,一种是运行action需要的组件,如bin,hcl等,另一类是公共云集成,如aws,gcloud,azure,还有一类是例子,以example-为前缀。

– Marketpace:action市场,这里才是Github的厉害之处,以强大的Github生态构建的市场,将会出现如雨后春笋般的action被创建出来。五花八门,可以满足各种各样的需求。

– Custom Action:如果上述的action不能满足你的需求时,还可以自定义地创建新的action,这里的action可以是私有库的,也可以是公开库的。一个公开库里面的action如果对其他人有用时,还可以发布到市场里面去。

2. Docker Image,支持两种来源:

– Docker Hub

– 任何公开的Docker Image库

CI何尝不能是一条流水线

圣诞节收到一个锤子做礼物的小孩,会觉得家里所有的东西都需要敲一敲。

相信各位看完这个github action的介绍,就像拿到了锤子的小孩,想对着自己的日常工作来两下子,让自己过的轻松一些。

来设想一下CI流水线可以有什么能力?

最最基础的,我们团队的CI流程可以被定义了,而且不需要口口相传了。新来的实习生写完了代码,只需要懂怎么触发流水线就行了,不用知道自己要先测那先部分,是发布前给师兄cr还是写完了就给他看?

测试可以左移到CI流程了,这部分可能就依赖Action生态的优秀程度了。但是这种插件化的机制就很有想象力,再也不用等Aone代码组那几个老哥挤牙膏了,开源的扫描任务或者测试任务用action的方式就能接入。尤其是云原生时代有了IaC之后,也不需要像现在的Aone实验室一样还得有专门的镜像才能跑对应的哪几种任务了。外面的Action也可以搞进来用。

离梦到来还有多远?

明显能看到几个技术前置依赖项待解决:

文件级别的权限管控,现在啥啥都as code了,都往代码里存,这玩意儿可不是谁都能改啊,安全小哥震怒!把关键配置文件,只允许部分同学改动编辑的能力是很有必要的。

强大简约的WorkFlow框架能力。能云原生化,serverless化,甚至faas化托管action的流水线。看了网上很多对github action的评测文章,发现有的拿来给一些网站做签到脚本,虽然说其实也是在用流水线,我倒觉得更像是在用它的Faas能力。对于测试来说,faas化的测试和扫描,以后一个新的扫描或者测试任务不在需要维护一个单独的集群或者部署一整套应用,而是只要写好业务逻辑,把任务调度交给workflow系统。

优秀实用的一些关键的基础Action组建,比如优秀的Code Review能力(CI流水线基础中的基础),比如制品仓库的能力(可以将CI和CD流水线结偶的关键),比如自动化测试的组建,以及最最关键的,发布和git操作的能力。

可能的问题

Q:干净的代码也只是降低系统的腐化,要怎么逆转效能的降低,做熵减呢?

A:设计是在信息不完全的情况下做出的决策,所以早期对领域模型的抽象肯定会有不合理性。更好的抽象和重构可以让应用重回青春。

Q:一定要用主干模式替换分支模式么?

A:分支模式也有分支模式的好处,这里可以再用一次折中定律,提升一方面,就要牺牲另一方面。所以什么开发模式其实只是手段,更关键的是如何提升代码质量,建立保证代码质量对研发体系,和形成肯定代码质量的文化。(完)

本文来自投稿,不代表【】观点,发布者:【

本文地址: ,如若转载,请注明出处!

举报投诉邮箱:253000106@qq.com

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2024年4月22日 10:17:36
下一篇 2024年4月22日 10:19:44

相关推荐

  • 黑客的角度学习python,python3黑客编程

    黑客用python做什么 图形处理:有PIL、Tkinter等图形库支持,能方便进行图形处理。数学处理:NumPy扩展提供大量与许多标准数学库的接口。 黑客编程,使用python可以写好多小工具,编写一个arp断网攻击,想要挖web漏洞就必须做好前面的信息收集 ,信息收集可以使用爬虫来处理,爬虫可以爬网页的文字,也可以爬取MM的图片哦,值得学习。 做日常任务…

    2024年5月15日
    4400
  • 3个网页设计时从用户角度需要考虑的问题

    好的网页设计在不知不觉中获得用户认可,最终就是用户不断的增长,设计时3个需要考虑的问题:尊重用户习惯和经验;避免过度设计;切勿假设用户情境 网页设计 出色的网页设计有很多评判标准,也会被很多的因素影响。但是,好的网页设计绝对是用户体验良好的,能够让用户轻松访问,快速浏览,认知和交互成本不需要太高。 无论你是前端还是后台,是视觉效果设计还是交互设计,目标都应该…

    2024年5月7日
    6300
  • 如何理解网页设计规范,从系统性质和产品性质角度

    从两个角度介绍网页设计规范的概念,网页设计规范就是为了使网页的每一个部分具有一致的视觉效果,一致的样式,为了使现在和将来的修改保持一致性 布局和颜色的规范 注:本文的截字体和按钮的规范 系统性质,从iOS和Android方面来简单讲解。我们都熟悉的微信钱包、支付钱包的首页入口以及页面内容,很明显都是属于系统性质的。这种性质是应用设计语言的方式来进行的,将页面…

    2024年5月7日
    5500
  • 网页设计时5个从场景的角度提升用户体验的方法

    网页设计时采用贴近生活的场景,增强用户的参与感;拟物化设计增强用户焦点捕捉能力;立体感增强视觉冲击力;故事性的场景增强对用户的带入能力 贴近生活的场景 将生活当中的一些场景融入网页设计,这样能够提升用户的参与感。 拟物化的场景 拟物化设计 逼真的拟物化设计,能够保证最初的视觉焦点,这是很关键的一点。 立体感的场景 立体设计 立体感的网页设计能够让用户有亲临其…

    2024年5月7日
    6400
  • 5个从渲染的角度增加网页加载速度的方法

    网页中的元素太多,加载速度太慢,使用过程中卡顿严重,5个优化方法:使用viewport,减少DOM节点,优化动画元素,优化高频事件,加速GPU HTML不妨使用viewport viewport 它可以加载页面的渲染。 减少DOM的节点 渲染树 很明显,DOM节点过多的话会影响页面的渲染,因此要减少。在进行页面设计时要意识到这个细节。 优化动画 CSS3动画…

    2024年5月7日 用户投稿
    5700
  • 网页和APP设计在设备尺寸的角度的差别

    网页一般是在浏览器上面显示,电脑的分辨率虽然差异较大,但是总体上来说都是大屏,运行在移动端的APP运行在小屏上,而且需要考虑横屏竖屏 不同屏幕使用情况 概述上来说,网页设计受制于电脑端的分辨率,因此浏览窗口有差别,网页的浏览窗口能够缩放。而APP界面设计受制于移动设备端,分辨率差异很大,还要考虑到横竖屏幕的调转方向。 移动设备屏幕尺寸 1、 APP界面设计的…

    2024年5月7日
    5200
  • 从品牌特征的角度解决网站如何配色的问题

    网站采用什么样的配色是设计师不断纠结的问题,如果找不到好的切入点,可以考虑从品牌特征入手,每一个品牌都有自己的特征,不同特征采用不同的色彩 配色配色配色图-古典、华丽、动感、优雅 但无论如何,配色的方案最终目标是能够给用户提供舒适视觉享受,而非干扰。从品牌角度着手是基础,也是整站设计最终效果统一、协调的重要因素!

    2024年5月7日
    6000
  • 从5个角度搭建一个独一无二的网站

    尝试创建不同的页面布局和结构;品牌化设计,是页面的风格符合自己的品牌;独特的网站建设 如何制作一个与众不同的网站设计?好的网站设计就像一个网站的“门面”,既能向用户展现丰富的信息量,又能留给用户一个好的印象。想要制作一个能在激烈的竞争中脱颖而出的优秀网站,那么,你就需要坚持不懈地尝试创新,精心规划。 布局与结构 从专业的角度来说,没有一个排版布局是没人用过,…

    用户投稿 2024年5月7日
    6100
  • 从源代码编程的角度,4个方法降低网页的大小

    网站布局方式,使用一些流行的方法,如:div css的栅格布局;js文件外部调用,或创建公共js文件;网站压缩传输;去除程序代码中的空格 网站源程序 网站优化的门道有很多,网站运营设计等细节上都需要优化,尤其是网站源代码优化,受到不少站长的关注。代码优化是直接关系到网站的加载速度和搜索页面上的排名。那么,网站代码优化的方法是怎样的呢? 网站布局方式 部分站长…

    2024年5月7日
    4400
  • 极简主义符合你的网站需求吗,2个角度进行判断

    极简主义的网站轻量级的布局和低修改度成本,成为越来越多的企业选择的方式。极简主义不适合广告、活动、内容较多的网站;网站受众的喜好影响风格选择 极简主义 由于极简主义风格自然出自然优雅的风格,加之其轻量级的布局和低修改的成本,使得越来越品牌都青睐这一设计,并用于自身网站当中。虽然极简主义的元素不多,复制别人网站也看似很容易,但并不是所有网站都适合极简主义风格。…

    2024年5月7日
    5900

发表回复

登录后才能评论



关注微信