作为构造方法的方法:组装说明

现代乐高设计师只能组装一个玩具模型,例如飞机。 自订? 您可以交换飞行员座位-就是所有定制。 大约30年前,从设计师那里可以组装几乎所有东西,从飞机到卡车,其零件数量与现代零件相同。 童年时代最现代的方法论的创造者扮演了古老的乐高。 那些现在使用方法论的人已经扮演了现代的角色。 工程实践的差异很大。



根据削减计划,菲利普·德尔加多Philip Delgado )将讨论形成方法论的工程方法。 所有项目和团队都是不同的,领导者是独特的。 一种适合所有人的方法将行不通-根本没有任何方法。 我们必须聘请一名设计师,并从中构建一些独特的东西。 在解读最好的TeamLead Conf报告之一时,将没有少林僧侣的秘密秘密-只有经经验证明的平庸。 我们正在等待有关开发方法,设计和实施时要寻找的内容以及重建方法的规则的详细目录。 对于所有想法,菲利普的经验都是真实的例子。 在他的职业生涯中,他尝试了从Visual Basic到硬核SQL的所有内容,开发了俄罗斯最大的博彩引擎和Yandex.Money,现在可以处理已加载的Java项目。 她定期在各种会议上演讲,包括HighLoad ++。



挑战赛


几年前,我再次来到一个从头开始创建支付系统的项目。 在项目的开始阶段,什么都没有:没有开发人员,没有流程,没有生产。 如果什么都没有,该从哪里开始工作? 立即介绍一些东西是奇怪的,甚至是愚蠢的。 因此,第一步是了解该技术实际需要什么。

肯特·贝克(Kent Beck)在有关SCRUM的书的结尾写道:

所有方法都基于恐惧。 您正在尝试养成可以帮助您防止恐惧变成现实的习惯。

因此,要做的第一件事就是从业务中找出它所担心的

通常,整个项目都会使企业感到恐惧: 太久根本不去做 。 从技术上讲,他害怕可靠性安全性 。 在我们的案例中,这些担心是有道理的:支付系统是交易对手,金钱和严重的义务。

不同的恐惧导致不同的方法。 对超额支付的恐惧导致雇用了容易变动的廉价开发人员-这就是SCRUM。 害怕错误会导致GOST或RUP带有大量正式文档。

开发人员也有自己的恐惧: 编写不受支持的内容做得不好 。 不幸的是,几乎没有人在担心开发人员的情况下开发方法。 只有XP简短地提到开发人员害怕做得不好,让我们尝试帮助他们做好工作。

除了担心之外,企业还希望出于什么目的来优化流程:

  • 便宜
  • 可预测地
  • 以可控的方式;
  • 定性地
  • 可扩展的
  • HYIPOVO。

选择任何一个选项,也许,也许有一天,如果什么都没有发生,并且火星与摩in座的月亮汇合,您将成功。 优化的大多数希望也都与恐惧有关,但是换句话说:便宜-担心多付,控制-担心,不要这样做,可以预见-担心没有时间。

通常企业一次想要一切 。 收集需求时,请遵循“患者总是说谎”的预设。 当企业想要一切时,他就说谎。 尝试了解企业的真正需求,然后尝试将其出售给他。 这不是团队领导者最标准的技能。 但是,如果您不知道如何找出企业的真正需求,那么您需要找到一个可以做到的人。

除了业务的愿望之外,您还需要找出明显的局限性

  • 期限紧迫 。 就我而言,没有硬性的截止日期,例如奥运会,只有两种选择:要么及时,要么不及时。
  • 严格合同 。 如果您与国有公司合作,则不得违反合同。 其他任何事情都可以按照您的意愿进行。 这极大地影响了技术。
  • 定期出货 。 您需要不断推出新功能,这对业务很重要。
  • 认证:CB,PCI DSS 。 这是支付系统设计的主要限制。 最大的风险和担忧与证券,PCI DSS和其他认证有关。

这是本质限制,例如,将FixPrice流程与Time&Material流程区分开。

事实证明,在我们的支付系统项目中,时间太长令人恐惧,不要害怕,可靠性和安全性令人恐惧,但建议尽快进行所有操作。 同时,在工作开始时没有产品,没有开发人员,但是学科领域有专家(例如,我)。 只有彼此之间非常独立的大块才清楚:处理,客户,集成,后台和前台。

确定目标和局限性之后,您可以开始构建一种方法,以准确地了解我们将如何开发所需的系统-至少在第一阶段。

我们正在建立启动项目的方法


但是什么是技术? 我们总体上想要构建什么? 这些问题的答案是Alistair Cowber的一幅美丽图画,有很多观点。



首先,从这张照片来看,三件事对我们很重要: -做事的人; 过程 -他们如何做; 和人工制品 -需要做什么。 我们还没有人员和流程,因此我们将找出需要的工件。

在设计技术时即使已经存在人员和流程 从工件开始也是一个很好的模式 。 明智的做法是从将要在销售中布置或发送给客户的那些工件开始,从交付的价值开始“从头开始”设计工件。 为什么这样会更好-对另一篇文章来说是一个单独的问题。 如果您知道答案,请在评论中写。

选择工件


我们恐惧并理解恐惧消除了哪些伪像。

长期计划和具有里程碑意义的事实免除了对“项目工作时间太长”的恐惧。 出于对可靠性的恐惧- 自动测试 。 为了避免“不做”,请举起“几乎战斗”的立场。 我们将向它证明业务进展 。 因此,您可以立即看到什么是什么,什么是有用的,并且在紧要关头,我们至少会发布一些结果。

我们形成流程


我们处于开发的开始,因此没有时间和理由来创建复杂的正式流程。 因此,我们简化了所有流程,并专注于促进沟通

结果,恰好有两个过程: 解决一个大任务 -考虑解决方案,并检查 完成的工作 。 这些过程是漫长的,研究性的,彼此之间几乎没有联系,因此需要适当的开发实践。

为了快速做到-我们聘请优秀的员工 。 但是,出色的专家不会选择市场上任何人都不知道的支付系统。 它以某种方式解决了,但是解决此问题的方法是单独讨论。 顺便说一句,在评论中写下您是如何在家解决的,您有没有决定?



静修:关于招聘


招聘时,他们通常查看标准的职位描述和候选人级别的通常等级,以绘制团队中的角色图:



但是标准的说明不允许我们了解项目中特定人员的需求。 我通常会为不同的项目保留一些不同的地图,以突出显示对项目重要的不同角色和能力。 例如, 技术

疑难解答 。 这是一个可以在2天之内没有文档和测试的情况下潜入带有某种错误的遗留代码的人,并且出现了这样的短语:“在这一行中,将+替换为-,它将起作用。”

它将起作用。 像四叶草的人很少。 不幸的是,市场不重视这些人,很难向企业解释说,急需一个疑难解答者,值得尊敬和高薪。 结果,他们中的大多数去了团队负责人,而这种资源已经耗尽了。 仍然只是为了提高此类专家的有用性,也许一段时间后情况将会改变。

积分器 这是一个知道如何从几个不同的组件,库,模块创建整体的人。 他不再是XML程序员,而是了解内部结构的人。 这是一种极为罕见的技能,在实际开发中通常非常需要此技能。

专家:框架,安全性,数据库,DevOps 。 关于专家的一切都很清楚-这些人可以联系有关相关主题的问题。

此外,还存在“非技术”角色,即角色的社会地图。 创意产生者是一个可以提出一些想法的人。 评论家 -可以进行建设性批评的人。 有经验的人-有经验的人可以说:“我们这样做了,结果真是胡说八道。” 完美主义者是一个希望一切都变得美好的人。 这是重要的角色。 如果不存在,通常所有内容都会迅速消失,因为没有人告诉您:“又得到了,一切都错了-让我们修复它!”

如果您特定项目的角色图中的某些角色没有填写,那么团队将必须履行它。

因此,请考虑选择谁,并记住不同的面试适合不同的角色和职位 。 在经验丰富的大四学生的陪同下,面试将很快进行-只需了解他在做什么。 通常,您必须与大三学生交谈很长时间,给出测试任务,并找出他能做什么。

候选人的级别越高,面试时间越短。

您还需要其他哪些人?

外向的人是想要并且可以在开发之外进行积极交流的人。 我们没有确切的产品,因此我们需要一些能够了解最终用户的人员,包括内部人员,例如我们的会计师。 性格外向的人不怕说话,找出这种不寻常的“非程序员”的需求-这样的人很少。

评论家 -我首先需要批评我。 这个人会批评我的决定。 没有批评,我恐怕会胡说八道。 当他们批评我时,我必须认真考虑这些论点,事实证明这并非完全是胡说八道。

单调问题专家 :报告,集成。 我确定我们将在项目中提供大量报告。 六个月写会计报告而不会发疯-这是一项难得的技能。 将此功能分散在不同的人中也很不方便,因此我需要一个解决单调问题的专家。

不在乎 。 这是一个不在乎要付什么任务的人。 这个角色对于项目非常重要,因为存在不同的任务:单调的,但复杂的,疯狂的有趣的事情,或者由于外部需求逐渐消失而与我们以前的经验完全无关。

让我们回到我们的项目。 我们不需要疑难解答,因为还没有遗留问题。 我们需要一个集成器和一组专家。 安全专家实际上是在内部成长的。

Database Guru已成功外包。 我真的很喜欢“在外部某个地方的数据库中发挥能力”的想法-如果您没有一家庞大的公司,那么找到这样一个工作人员通常是不现实的,并且至少需要5个人来支持重要的24 * 7数据库。 我们还首先将DevOps Guru外包了出去,但没有成功,对于外部表演者来说,这个角色更加困难。

社会角色也被填补(甚至有一些批评家)。



练习


因此,我们找出了必要的工件,发现了人们的需求和流程要求。 发生了什么,我们如何精确地组织开发:

  • 我们计划大笔画。 我们没有产品,因此不可能更准确地计划。 您需要在三个月内创建一个个人帐户,仅此而已。 我们在Confluence中执行计划。
  • 每个人都是一位分析师 。 没有正常的作品,并且主题领域的能力由不知道如何写作作品的人掌握。 没有人教过他们,您需要从他们中删除此信息。 但是我们有“性格外向的人”。
  • 不需要跟踪器 。 该项目只有20个主要任务-为什么要启动跟踪器。
  • VCS中的一个分支。 在初始阶段,不需要复杂的版本控制工作。
  • 这些过程是近似的 。 人数仍然很少,没有沟通和问题,并且项目本身是不稳定的。 无需详细描述任何内容。

当人们谈论相似但不太形式化的方法时,他们通常只是说:“ 我们有敏捷性。”

我们还获得了经典的“我们有敏捷”。 但是我清楚地知道为什么使用这种技术,为什么它是“敏捷的”,而不是更具体和复杂的东西。 我(和企业)都非常适合这种技术。

细心的读者会注意到,在设计方法时,我们忘记了两个重要的担忧: 对安全的担忧和对认证需求 。 让我们尝试弄清楚如何处理它们。



题外话:考本矩阵


1980年, 阿利斯泰尔·考伯恩Alistair Cowbern)绘制了该项目复杂性的矩阵


纵向-项目的重要性 。 如果发生重大错误,可能会造成什么损失:例如,从用户的舒适度下降到用户的生命丧失,例如,如果我们为核电站设计软件。 水平-团队的大小 。 大小会影响通信数量。 至关重要的是这些通信的细节。 总的来说,这会影响过程的复杂性。

阿利斯泰尔(Alistair)认为,在每个广场上向右或向上移动会极大地复杂化并增加开发成本。 这是合乎逻辑的-越来越多的人需要更多的通信费用。 如果需要更多的正式关系,则需要更多的通信费用。 即 越远,用于非生产性任务和损失的资源就越多。
顺便说一下,作为第三条轴,Alistair可以针对不同的业务需求进行优化。

让我们尝试将付款系统放在此矩阵上。 该矩阵非常适合作为一个模型,用于了解您的项目和系统的估计复杂性。

我们有一个付款系统,这意味着在紧急情况下,我们将损失一些用户钱。 当然,这是令人不快的,但不会导致复杂性的急剧增加。

但是我们几乎有一家银行,在某些情况下,如果违反中央银行的标准或要求,可以从该银行获得许可证。 这已经是很多钱的损失,几乎要倒闭了,非常可悲。


我们拥有大约20人的团队,因此我们进入了E30广场。 这很不好,因为在这个广场上,该技术的一个很好的例子是成熟的Rational Unified Process-正式过程,许多文档,清晰的陈述。 20-30人无法应付。 您必须雇用50名,难度会像滚雪球一样增长。 这种方法论中的类似系统通常由数百人或更多人编写。

怎么办 麻烦了吗 不, 不是整个项目都同样重要 。 我们只有几个关键部分。

  • 洗钱支票-中央银行为此严重打击。
  • 使用银行卡-世界支付系统为此深深打击。
  • 存储个人数据-为此,状态受到了严重打击。

让我们突出显示各个关键模块 。 我们将为他们编写专门的实践来专门处理项目的这些部分: 每次提交的全面PCI DSS审核 ,良好的文档,双重代码审查,特殊的计算过程等等。 只允许高级开发人员编写项目的这些部分。

但是这样的部分很少。 因此,该项目的Cowbern矩阵如下。


对于项目的不同部分,方法将具有不同的复杂性,将有不同的实践方法。 最困难和可怕的是三个人 。 付款逻辑-人员8。其他所有次要的任务(大多数都是最重要的任务),例如帮助系统或后台办公地点的布局(其中错误不是根本原因),大部分由人员来处理。 但是,流程可能是最简单,最非正式的。

大型复杂项目的各个组成部分可能使用不同的实践集。

这是微服务体系结构的一大优势-能够规定一张图的能力,其中不同的服务对不同的技术反应不大,而对同一技术内的不同实践反应不大。 同时,重要的事情仍然很常见:计划,工件,交互的通用方法。



总结一下


我们发现了该方法要求 :目标和局限性。 确定了基本要素 :流程,人工制品和人员。 他们描述了实践 :如何实现流程,对工件的要求,需要什么样的人。

这是一个经典的工程设计方案:我们有需求,然后我们指定架构,然后对其进行编程。

设计方法是一种工程实践,一种工程过程与对模块进行编程没有什么不同。

第一阶段练习


从理论到现实的一些小练习。

有权使用“为什么?”


这是我最喜欢的做法。

每个员工都有权询问任何任务:为什么要这样做,为什么要这样,以及谁需要它?


人们害怕问“为什么”-也许是因为在童年时代,他们仍然没有回答这样的问题。 如果您已明确写下并重复多次“可以并且应该”,那么人们就会这样做。 一旦您开始问所有层面(包括业务)的“为什么”,任务的数量就会减少很多倍,动力也会增加。 人们了解工作的含义,并且可以更快,更经济地完成工作。

不要泛化为三个


在出现三个不同的示例之前,不要创建一站式解决方案如果要设计三架不同的飞机,则无法一次编写其中一架飞机代表的正确类。


此解决方案也用于开发-在与三个不同的集成并且不了解它们的不同之处之前,不要与承包商建立通用集成。但是,在设计技术时,在尝试三种不同的方法并找出它们之间的共同点之前,请不要立即对工件进行正式描述(图表或模板)。这是一种简单但有用的做法,它使您不必突然创建多余的抽象。

IDE驱动的开发


JetBrains — ! , IDE.


, IDE.

. , IDE , . IDE — . IDE — : , , , . : , , .

, , IDE call stack . .

IDE — .


, , . . : « ?» , . , - . , .


3–4 . .

, . , . , , , .


. — , , .

- , . .

— .

: « , , , ».

— , , . . — , , , .

. , - . .


, — , . : , ,

, : . . .

  • . , , .
  • . , , , , - .
  • . 3 -, , , , . , .
  • . , . , - — .

, SCRUM , SCRUM . , , , .

. 20 . SCRUM , , , - . .


.


- — , 3 .

- .

- : PCI DSS , ( ., ), - .


. « , », . , 1 — , - . , . shit happens , — , . , , , , .

, , , — , , .


. . . - , , — .

. , «» , - bus factor — , .

. , , .

. , . , , . , , , . Style guides code review , - , .

, - . ?


Planning poker . « » — . , , . Planning poker , .

. :

  • ;
  • product owner ;
  • ;
  • .

, , , — ? , 2 10% . ?

, Planning poker — , , ? , . . planning poker , , .

: , , . Agile — , . , .

, Agile, SCRUM XP .

, , , , , , . , - , — ? ?

. : , , , . , ?


, Planning poker , , Planning poker :

— , , !

Planning poker . . .

:

  • ;
  • ;
  • .

.

: , , .


Jira , « » ? - -? ?


, , . , junior product manager — , , , .

, — . . , . — !


在检查代码之前。 我经常听到有人对一个重大功能负责,但是他做错了,需要重做。 在编写代码之前,让我们先检查一下代码 。 在编写代码之前,Jira的一名员工用两行或两段文本描述了解决问题的计划。 快速轻松地回顾这两个段落,但是全局错误会尽早发现,特别是如果您有团队负责人或架构师。

此外,这种做法还可以使团队负责人或架构师了解大型项目中的所有更改。 他读的不是错误的代码,而是阅读了两段有关系统中通常发生的情况的内容,并迅速抓住了人们的手。 对于产品的关键部分(例如付款逻辑)尤其如此。

如何改变方法而不杀死项目


因此,在9个月内,我们更改了3种方法:“我们有敏捷”,一日SCRUM和看板。 如何确保您不会丢失资源? 如何彻底改变方法,而不是同时终止项目?

我们设法更改了方法,以使某些开发人员根本没有注意到这些更改。 没有人告诉他们这件事,他们工作了,方法也不同!

最主要的是要了解何时进行更改。

如果您了解原因。 方法经常更改,因为新技术总监来了,他想重做所有事情。 这是一个不好的原因。 最好选择旧的并重命名-一切,技术都不同,更好。 如果您不能回答为什么这个问题,最好不要进行更改。 我通常喜欢问为什么。

如果您认为“如何”。 如果您了解如何从A点到达B点,请进行更改。 直到您提出-不需要。

如果满意“多少”。 改变技术几乎总是昂贵的过程 。 如果做得好,更换将花费团队负责人,HR,Jira定制人员的费用几个工时。 如果不好的话,整个公司要花几个月的时间。 我经常看到从看板到SCRUM的过渡,然后又回来了,在整个开发过程中,每个过渡都花了一个月的时间。 如果您尚未准备好承担此类费用,请不要开始。

准备工作


我们提前开始。 对于小型团队而言,准备工作是在轮班前一个月开始的。

我们绘制用户故事。 与应用程序设计中的方法相同。 我希望您在描述需求时使用“用户故事”。

例如:

  • 用户故事 :作为一名开发人员,我想找到我的下一个任务并继续执行。
  • 验收标准 :作为开发人员,我可以查看当前的所有任务并评估任务的紧迫性和优先级。
  • 例外:如果没有任务,则开发人员知道向谁询问。
  • 链接:用于准备短期计划的方案,其中显示了从何处获得下一个任务。

通过这种方式,遍历您的方法论中出现的所有主要活动,并编写用户案例。

经理如何看待计划的有效性? 高层管理人员如何理解一切都很好? 编写用户故事,然后添加详细信息:为用户和高级管理人员创建仪表板-接受标准通过,一切都很好。

当您已经有了用户故事时,可以开始将它们变成实践。

用真人代替所有角色 。 真实的人比角色更了解。 然后,您将立即发现瓶颈。 例如,您的所有用户故事都使用一个特定的人,尽管他只是戴着不同的帽子,扮演不同的角色。 这很糟糕-找出如何卸载它。

减少人工制品和沟通 。 如果它们太多(每个用户故事都建议其自己的工件和通信),则需要执行某些操作。

寻找瓶颈 。 如果有这样的故事地图,则可以开始使用它。

选择工具


该工具可以识别功能 。 人们普遍认为工具并不重要,或者任何工具都适用-这是胡说八道。

如果该工具使用不方便,则将不使用它们。

而且,供应商总是撒谎。 如果他们说自己的工具可以执行“ 1、2和3”,那么很可能他们有时不能执行“ 1”,“ 2”,而不能执行“ 3”,但这非常糟糕。 确保检查所有内容。

我们正在积极讨论


如果没有对该技术进行积极的讨论,您可能会忘记一些重要的功能,重要的用户故事并冒犯他人。 被冒犯的人会破坏方法的实施 :他最初被冒犯了,被遗忘了,没有人跟他说话。

在此阶段,人们可能会因引入其他技术而表现出负面的先前经验。

-废话! 我们已经有了这个,什么也没有。

为了避免这种情况,请大家加倍努力,问出什么问题。 准确记录并演示您记录的内容-这样,此人将了解他们听到了他的声音。

不要重复负面经历 。 回顾没去吗? 现在,这是一个“星期五蛋糕”。 早上7点的站立训练没有走吗? 现在这就是“收费”。 为相同的做法使用不同的名称,以使人们不会将过去的负面经历与他们联系起来通常会有所帮助。

不幸的是,没有通用的解决方案。 根据您的情况。

实作


IT管​​理的主要价值是尊重。

我们节省了别人的时间 。 如果您需要将任务从一个跟踪器系统转移到另一个系统-编写脚本,请雇用Mechanical Turk帮您完成。 解决此问题,使开发人员在一个星期内不会将其所有任务从一个系统转移到另一个系统上-这是尊重的体现。

我们协助过渡 。 如果我们采用一种新的做法,我们会坐在一个人旁边,并帮助他了解新系统。 我们会事先准备说明。

我们描述了特定的动作。 我们根据用户故事制作了非常具体的文档:“我们需要承担一项新任务。 如何做到这一点写在文档中-您在Wiki上打开某某某某页面,您在某某某某中阅读。”
我们逐步介绍-并非同时介绍所有做法 。 我们的开发人员没有注意到方法的变化,因为我们没有同时实施所有实践。 当我们想从一天的SCRUM顺利过渡到其他事情时,我不是每天都进行回顾,任务显得更长了一些,每天早上的会议悄悄地转移到了更标准的站立式会议上。 在人们看来,一切都在逐步进行。 然后,实践发生了很大变化。 关于某些事情,我当然告诉他们:“现在,我们将稍微改变与Jira的合作过程-现在将是这样。”

路径自行踩踏 。 不要立即规定艰巨的工作流程-让人们自己找到路径。 他们可以方便地将跟踪器中的任务从一个州转移到另一个州,即使他们转移了任务,然后您也将对其进行修复。 立即预测会带来什么便利是很困难的,而禁止请求的过渡也很容易。 但是,随后您不得不忍受很长时间才能使一切恢复原状。

简单起码,至少在一开始就如此。 不要太复杂。

技术支持


我们会为过渡过程提前预留资源,因为它很昂贵 。 从一种技术过渡到另一种技术需要很多钱。 为将在跟踪器,工作流,计算过程中编辑错误的人员分配时间和时间。 如果没有资源,但同时又有些急事,一切都会变得很糟糕。
我们会尽快修复错误

结论


项目也不同,人们也一样-每个人都需要完全不同的技术 ! 所有幸福的家庭都以自己的方式同样幸福,所有幸福。 与项目相同-没有两个相同的项目。

创建技术是一项工程任务。 与编程和设计系统模块完全相同。 这就是您的处理方式。 您知道如何很好地解决工程问题-因此请在此新实践中使用所有知识。

方法的改变是一个经典的SMART项目 。 使用您所了解的有关项目管理的所有信息:定义成功标准,在完成对任务集的检查后检查,记住有限的时间等。

我的个人清单


人员比流程更重要 ,因为流程必须为人员完成。 如果公司的平均年龄为50岁,而且他们来自军方,而您正在尝试立即实施看板,那么很可能不会成功。 人们只是习惯了别的东西。

程序员的主要优点是懒惰。 建立流程,使人们在流程上花费的时间更少,并且开发人员是第一个运行它们的人。 如果人们不寻求实施流程,那么他们将不明白为什么。

碰巧有些实践难以理解,因此难以实施。 有时,改变惯例会更容易,也许与您的任务或员工不符。 不得已-换人,但比改变做法要贵。

结果比习惯更重要 。 最可怕的习惯是听到新工具,将其带回家并立即使用的习惯。 货运崇拜是一种可怕的反抗习惯。 但是,即使即使已经无效,也因为“我们总是那样做”而改变某种做法的恐惧也是有害的。

有时总的来说,任务是如此多样,以至于习惯变得危险。 例如,与有生命的人交流时。

说服力比命令更有效 。 最好的动机是了解并分享您的工作的意义。 开发人员喜欢使用户的生活更轻松,为公司省钱并简化生活-因此,请向他们介绍其行动目标。 学会说服。

这三个原则都是我对世界的个人印象。 如果您有其他前提,则可能需要不同的方法来构建技术。

TeamLead Conf计划委员会中,我们也对旧的Lego更加熟悉,因此我们在程序中选择多维数据集,您可以从中构建适合自己的流程。 在圣彼得堡的秋季会议上,已经组装了15个零件 ,但是我们仍在接受报告的编写

Source: https://habr.com/ru/post/zh-CN456514/


All Articles