GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

GO 语言项目开发实战专栏目录总览

Go 语言简单易真正学,对于很多开发者来说,编写可运行的代码自己一件难,但如果想成为编程高手,你需要去编程的哲学。

在我的 Go 开发代码中性问题中,我遇到了各种各样的问题,例如:代码不规范,不可阅读;函数共享差,代码重复率高;提供接口测试,代码扩展差,代码测量;代码质量下。 究其原因,是因为这些开发者很少花时间去认真研究如何优雅地去开发一个代码项目,开发时间更多的是在需求开发中埋头。

如果你也遇到过花点时间来研究下如何开发一个自己的 Go 项目,那么你只要正常安排这样的时间,就可以让开发者在职责上正常运行。算命,并正确。

其实,之前所学的各种规范设计也是为了写出一个优雅的Go项目。 。这一点比较多,但很重要,希望你能花点精力讲清楚掌握,掌握之后,能够为你开发出一个出众的内容。

如何写出优雅的 Go 项目?

在回答这个问题之前,我们先考虑另外两个问题,如何写出一个优雅的去项目呢?

是 Go 项目,而不是 Go 应用?

一个优雅的Go项目有哪些特点?

先看第一个。Go 工程化的概念,项目应该包含一个偏向的 Go 应用,还包含了项目管理和项目文档

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

这第二个问题,一个优雅的项目要求,我们的应用是优雅的,我们确保我们的项目管理和优雅的。这样,文档根据前面讲学到的去设计规范,很容易总结出一个优雅的 Go 应该具备的特点:

符合 Go 编码规范和最佳做法;

易阅读、易理解、易维护;

易测试、易扩展;

代码质量高。

解决了这两个问题,让我们回到这一讲的核心问题:如何写出优雅的 Go 项目?

写出一个优雅的 Go 项目,在我看来,就是用“实践”的方式实现 Go 项目中的 Go 应用、项目管理和项目文档。具体来说,就是写出高质量的 Go 应用、高效管理文档项目、编写精品项目。

为了你的理解,我将把这些画作画出来。

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

,就看看根据讲讲节目的去设计我们的设计规范,实现一个优雅的去学习项目。我们先从项目中编写如何去应用看起。

写高质量的围棋应用

根据我的研发经验,要写一个优秀的 Go 应用,其实可以归纳为代码结构、代码规范、编程哲学和软件设计的 5 个方面,见下图。

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

,我们详细描述这些内容。

代码结构

因为组织合理的结构是一个面。我们可以通过项目的两种方式来组织代码结构。

第一个内容是,组织一个好的目录结构。关于如何组合一个好的目录结构,你可以回顾06讲的。

二是手段,选择一个好的模块组合方式。做好分解,可以在职责范围内分明,实现低模块内部高耦合。

去项目中,如何拆分项目呢?目前有一个方法,分别是按层分解和按功能分解。

首先,我们看下层,最典型的按 MVC 架构中的模块结构方式。 。

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

每层不同的功能:

View(视图)是提供给用户的操作界面,用于处理数据的显示。

控制器(控制器),负责根据从查看层输入的指令,确定模型产生和中的数据,控制器最终进行相应的用户操作,最终结果。

Model(模型),是应用程序中用于处理数据逻辑的部分。

我们看一个典型的按层分解的目录结构:


$ tree --noreport -L 2层

层数

├── 控制器

│ ├── 计费

│ ├── 顺序

│ └── 用户

├── 型号

│ ├── 计费。去

│ ├── 秩序。去

│ └── 用户。去

└── 浏览量

└── 布局

在项目中,这些功能分解又会带来很多不同的问题。最大的问题是被引用:相同的功能可能在不同的层中使用到,而在不同的层中,很容易导致循环循环按引用。

所以,你只要大概知道那一层是什么就够了,在Go项目中我建议你使用项目中最常见的方法,这也是Go中最常见的方法。

例如,一个订单系统,我们可以根据功能将其分解成用户(用户)、订单(order)和计费(billing) )3个模块,每个模块提供独立的功能,功能更单一:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

下面是该系统的代码目录结构:


$树包

$树 --noreport -L 2 包

包

├── 计费

├── 订购

│ └── order.go

└── 用户

不同的意思也可以理解为:功能上的约定

不同模块,功能,可以实现高度内部聚低连接的设计哲学。

因为所有的减少只需要实现一次,引用清晰的引用,展现出循环的作用。

所以,有很多优秀的 Go 项目都是采用按功能拆分的模块方式,比如 Kubernetes、Docker、Helm、Prometheus 等。

除了组织合理的代码结构这种方式外,编写 Go 应用的另外一个行之有效的方法,是遵循 Go 语言规范来编写代码。在我看来,这也是最容易出效果的方式。

代码规范

那要遵循哪一个来写我们在我看来,应用代码就最好呢:编码规范和实践。

首先,参考的代码要符合 Go 的编码规范,这是最容易的方法。Go 社区有很多我们的代码规范实现,其中比较是Uber Go 语言规范实现。

读这些规范有用,也确实有花时间、花精力。就是“特别放送| 给你清晰、可直接套用的 Go 编码规范”。

之后,大家参与、遵守规范,共同继承组织。其实,要靠开发者甚至来组织所有的规范。这时候,我们可以使用编码规范,而不是任何容易的事情来开发代码检查工具,约束者的行为。

代码行代码检查之后,可以确保开发者编写出的每一行代码都符合规范的,还可以将工具代码在集成CI/CD流程中提交。这样,之后自动地检查代码,就保证只有符合编码规范的代码,才会合入主干。

Go 语言推荐的代码是代码检查有很多,目前用的最多的是golangci-lin,这是我极力和你使用的工具。关于这个工具的使用,我会在第15 讲详细介绍。

“实践”是经过社区发现的、符合Go语言的特色和共识的,它可以帮助你获得最佳的帮助开发出一个优质的代码。

这里我给你推荐几篇介绍去语言最佳实践的文章,供你参考:

Effective Go:高效Go,由Golang官方编写的代码,里面包含了编写Go编程的一些建议,也可以理解为最佳实践。

Go Code Review 评论:Golang 官方编写的 Go 实践,作为 Effective Go 的最佳补充。

Go 包的样式指南:包含了如何组织 Go 包、如何选择 Go 包、如何编写 Go 包文档的一些建议。

代码质量

组织合理的代码、符合 Go 语言规范的 Go 之后,我们还需要通过一些代码来确保我们开发出一个高质量的代码单元,这可以通过测试和 Code Review 来实现。

执行单元测试后的一段代码单元测试非常符合我们的测试标准测试用例,还需要我们确保代码是可测试的,以及具有高的单元测试覆盖率。

,我就来介绍下编写一个可定制的测试代码。

如果我们要对函数 A 进行测试,并且 A 中的所有代码均能在单元测试环境下按规定的代码执行,那么函数 A 的预期块就是可测试的。我们来看下一般的单元环境测试有什么特点:

可能无法连接数据库。

可能无法访问服务。

如果函数一个依赖、环境连接服务,那么在执行单元测试数据库一旦失败,函数就无法测试的。

解决方法也很简单:将依赖的数据库、数据库等服务方法抽象成测测中调用的接口,在测试时模拟某种类型,确定从服务等依赖的方法测函数中解耦出去。如下图所示:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

为了提高代码的可测量性,降低测试的复杂度要求,对功能和模拟的要求是:

要减少功能中的依赖,让功能只依赖的模块。创建一个必要的、职责分明的函数,有更多的依赖。

模块应该是易Mock的依赖。

为了帮助你理解,我们先来看一段不可测试的代码:


包邮_

 

导入“google.golang.org/grpc”

 

类型帖子结构{

名称 字符串

地址字符串

}

 

func ListPosts (client *grpc.ClientConn) ([]*Post, error) {

返回客户端.ListPosts()

}

因为该服务代码中的ListPosts函数是不可测试的。因为ListPosts函数中调用了client.Lists ()方法依赖于一个gRPC连接方法。网络隔离等原因,无法建立 gRPC 连接,因此导致 ListPosts 函数执行失败。

下面,我们把代码更改成可测试的,:


包主

 

类型帖子结构{

名称 字符串

地址字符串

}

 

类型服务接口{

ListPosts() ([]*发布,错误)

}

 

func ListPosts (svc Service) ([]*Post, error) {

返回svc.ListPosts()

}

上面的中代码ListPosts函数输入依赖为Service接口类型,因此只要我们测试一个实现中了Service接口的实例ListPosts函数,我们就可以在单元中实现一个不运行的类型,可以在任何类型的服务器上运行。的假实例,并传给 ListPosts。 上述可测试代码的单元测试代码如下:


包主

 

导入“测试”

 

输入fakeService结构{

}

 

func NewFakeService ()服务{

返回&fakeService{}

}

 

func (s *fakeService) ListPosts () ([]*Post, error) {

帖子 :=制作([]*帖子, 0 )

帖子 =附加(帖子,&Post{

名称: “科林”,

地址:“深圳”,

})

帖子 =附加(帖子,&Post{

名称: “亚历克斯”,

地址:“北京”,

})

回帖,无

}

 

func TestListPosts (t *testing.T) {

假的 := NewFakeService()

if _, err := ListPosts(fake); 错误!=无{

t.Fatal( "列出帖子失败" )

}

}

当的代码几个可测的时候,就可以我们需要用一些Mock的工具来Mock之后的接口了。常用的工具,有这样的:

golang/mock,是官方提供的 Mock 工具包框架。它实现了基于接口的 Mock 工具包。接口的Mock源文件。

sqlmock,可以使用模拟数据库连接。数据库是项目中比较常见的依赖,在遇到数据库依赖的时候都可以用它。

httpmock,可以使用 Mock HTTP 请求。

bouk/monkey,猴子,通过修改函数可以通过替换函数的方式来任意函数的实现。可以这么说,猴子提供了一个单元测试方案,模拟了依赖的最终解决方案。

,我们再一起看看如何提高我们的单元测试覆盖率。

当我们编写了可测试的代码之后,接下来就需要编写测试用例,用来提高项目的测试覆盖率。这里我有以下两个建议供你参考:

使用 gotests 工具自动生成单元测试代码,减少编写测试用解放的工作量,将你从重复的中出来。

定期检查单元测试覆盖率。您可以通过以下方法来检查:


$ go test -race -cover -coverprofile=./coverage.out -timeout= 10 m -short -v ./...

$ go tool cover - func ./coverage . 出去

执行结果如下:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

在提高项目的单元测试覆盖率时,我们可以先提高单元单元的剩余测试覆盖率,然后再检查项目的测试覆盖率;如果项目的单元测试覆盖率指标的值,可以再次提高测试覆盖率低的函数的覆盖率,然后再检查。依次循环,最终将项目的单元测试覆盖率优化到预期的持续值。

这里要注意,对于一些经常会发生变化的单元测试,覆盖率要达到 100%。

说完单元测试,我们再看看如何通过 Code Review 来保证代码质量。

Code Review 提高代码质量、交叉排列一定的缺陷,并且团队查知识共享,是保证质量非常好的手段。在我们的开发中,要建立有效的促进代码审查项目内的代码审查机制。

但在我的研发生涯中,发现了很多有效的 Code Review 机制。但是这些团队都认可 Code Review 建立机制带来的好处,因为团队最终没有遵循,慢慢地 Code Review 就变成了形式,其实,建立Code Review机制很简单,主要有3点:

首先,确保我们使用类似的代码平台有相同的功能。

指导,制定制定 Code Review 规范,如何进行 Code Review。

最后,也是最重要的,每次代码发生变化,相关开发人员都要去代码审查机制,并形成习惯,直到最后形成团队文化。

到这里可以小代码结一下:组织一个合理的结构、编写代码符合我们 Go 代码规范的保证代码质量,在我看来这是 Go 的外功。那里面的代码是什么?软件设计方法。

编程哲学

在我看来,编程哲学,其实就是要编写 Go 语言设计哲学的代码。Go 语言有很多设计哲学,对代码质量影响比较大的,我认为有两个:接口编程和提供的“对象”编程。

我们先来看下面向接口编程。

去接口是一组方法的。任何类型,只要实现了该接口集,集合属于该类型,也称为实现了该接口。

接口的,其实就是为不同级别的模块提供一个定义好的职责。这样就不需要下游层的具体实现,充分地对上层进行了解关联。提供的接口编程的思想来实现。

我们看一个给定接口测试的例子。下面代码定义了一个Bird接口,Canary 和 Crow 类型实现了Bird。


包主

 

导入“fmt”

 

// 定义了一个动物

类型鸟接口{

飞()

类型()字符串

}

 

// 鸟类:金丝雀

类型金丝雀结构{

名称字符串

}

 

func (c *Canary) Fly () {

fmt.Printf( "我是%s,用黄色的翅膀飞\n" , c.Name)

}

func (c *Canary) Type () string {

返回c.Name

}

 

// 鸟类:乌鸦

类型乌鸦结构{

名称字符串

}

 

func (c *Crow)飞() {

fmt.Printf( "我是%s,我用黑色的翅膀飞\n" , c.Name)

}

 

func (c *Crow)类型()字符串{

返回c.Name

}

 

// 让鸟类飞一下

func LetItFly (鸟鸟) {

fmt.Printf( "让 %s 飞起来!\n" , bird.Type())

鸟.飞()

}

 

功能主要() {

LetItFly(&Canary{ "金丝雀" })

LetItFly(&Crow{ "乌鸦" })

}

Crow 调用代码中的方法因为都实现了的 Fly、Type 实现了 Bird 接口,可以在接口中说 Bird Crow 和 Bird 函数调用函数时,可以这样类型的鸟类。调用Bird内部提供的方法,来解接口接口Bird的具体实现。

,我们总结下使用接口的好处吧:

例如,同样的 Bird,有不同的实现。在开发中使用不同的操作是,将 RD 的抽象目标形成接口,可以实现同一个数据库的实现。

可以解耦上提供的实现。例如,LetItFly 只需要关注 Bird 是如何飞的,需要调用 Bird 的方法。

因为接口可以在实现单元测试时解耦接口上实现了依赖于系统/类型的,可以利用接口将耦合,实现假的。

代码更健壮、更稳定了。例如,如果要更改 Fly 的方法,只需要更改相关类型的 Fly 方法即可,完全影响 LetItFly 函数。

所以,我建议你,在去项目开发中,一定要多考虑,那些可能有实现的地方,要考虑使用接口。

,我们再看下面向“对象”编程。

给了很多对象编程(OOP,可以让我们的代码能够轻松维护、轻松扩展,并提高开发效率等,例如需要一个高质量的 Go 应用程序在的时候,也应该使用给对象的方法)编程叫“需要什么时候”呢?我们在开发这个时候有一个功能可以通过就近发生什么情况和自然方法的思考方式来解决,当时应该考虑使用对象的编程。

Go语言不支持给对象编程,但是却可以通过一些级别的特性来实现类似的效果。

给几个对象编程中类似,有核心特性:类实例、抽象几个程序、继承、多构造函数、解析函数、方法重载、这个指针。的效果:

类、抽象、通过封装结构体来实现。

实例通过结构体变量来实现。

在这里解释下叫组合:一个结构体组合到实现另一个结构体,包括一个什么样的结构体。例如,一个结构体结构体就说这个结构体了。

多态通过接口来实现。

构造函数、解析构造函数、方法重命名和这个指针等为了保持语言的性质,把这些特性去掉了。

Go中给的对象方法编程,见下图:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

我们通过一个示例,来具体下 Go 是如何实现给对象中的类、抽象、外观、继承和多态的。代码如下:


包主

 

导入“fmt”

 

// 基类:鸟

类型鸟结构{

输入字符串

}

 

// 鸟的类别

func (bird *Bird) Class () string {

返回bird.Type

}

 

// 定义了一个动物

类型鸟接口{

名称()字符串

类()字符串

}

 

// 鸟类:金丝雀

类型金丝雀结构{

鸟

名称字符串

}

 

func (c *Canary)名称()字符串{

返回c.name

}

 

// 鸟类:乌鸦

类型乌鸦结构{

鸟

名称字符串

}

 

func (c *Crow)名称()字符串{

返回c.name

}

 

func NewCrow (名称字符串) * Crow {

返回&乌鸦{

鸟:鸟{

类型:“乌鸦”,

},

姓名:姓名,

}

}

 

func NewCanary (名称字符串) * Canary {

返回&金丝雀{

鸟:鸟{

类型:“金丝雀”,

},

姓名:姓名,

}

}

 

func BirdInfo (鸟类鸟类) {

fmt.Printf( "我是%s,我属于%s鸟类!\n" ,birds.Name(),birds.Class())

}

 

功能主要() {

金丝雀 := NewCanary( "CanaryA" )

乌鸦:=新乌鸦(“乌鸦”)

鸟信息(金丝雀)

BirdInfo(乌鸦)

}

将上述代码保存在 oop.go 文件中,执行以下代码输出如下:


$去运行 oop。去

我是CanaryA,属于Canary鸟类!

我是CrowA,属于Crow鸟班!

在这个类的鸟中,分别通过金丝雀和乌鸦类的结构来定义了这个类的鸟,其中分别通过名称和名称来表示。鸟类,并封装了该鸟类的属性和方法。

在 Canary 的 Crow 结构结构中,有一个 Bird 类的字段和 D 类的父类,Canary 和 Crow 类的父类,Canary 和 Crow 类的属性和方法。通过这个字段实现了继承。

在主要函数NewCanary创建了Canary动物,并赋予BirdInfo函数通过结构体变量实例。

在方法中,将接口作为不同参数的鸟类信息,并在不同类型中调用鸟类,鸟类,这两个根据鸟类类别的名称和返回方法的名称和类别,名称和类别,名称和类别,名称和类别,名称和类别通过接口实现了多态。

软件设计方法

,继续学习编写代码的第二项内功,我们也让编写的代码符合一些同类软件的下来的,优秀的设计方法。

优秀的设计模式有很多,其中的设计模式有对我们代码质量的帮助,分别是设计模式(Design SOLID SOLID)。

在我看来,设计模式可以理解为某个场景出现了一些特定的总结的实现方式。需要我们的特点是解决的简单比较具体,实施原则比较;而 SOLID 的原则更替最佳,全面理解,并在编写代码时多思考和落地。

设计模式和SOLID原则,我是安排的:在第11讲,我会这样你学习去项目常用的告诉设计模式;至于SOLID原则,网上已经有很多关于优秀的文章了,所以我会简单的告诉你这个原则是啥,然后给你推荐一篇介绍文章。

我们先了解下有哪些设计模式。

在软件领域,沉淀了一些

返回首页的、可以解决常见软件设计问题的设计方案。这25种设计方案同样适用于Go语言开发的项目。

在这里,我将这张 25 种设计模式图,你可以先看看,找出一些大概的印象,对于在 Go 项目开发中常用的设计模式总结,我会在第 11 期讲详细介绍。

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

如果说设计解决方案是具体的场景,那么 SOLID 就是我们设计应用代码时的指导模式。

SOLID 原则,是由罗伯特·C·马丁在 21 世纪早期传承的,包括给对象编程和给对象设计的五个基本原则:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

遵循 SOLID 原则可以确保我们设计的代码是易维护、易扩展、易阅读的。SOLID 原则同样也适用于 Go 程序设计。

如果你需要更详细地了解 SOLID 原则,可以参考下SOLID 原则介绍文章。

如何到这里,就学“编写高质量的 Go 应用”这部分内容。接下来,我们再来学习下高效管理 Go 项目,以及完成编写优质的项目文档。这里面的内容,在此之前我们都有学习,因为是“如何写出优雅的Go 项目”的组成,所以,这里我重要的部分仍然会简单介绍下它们。

高效管理项目

一个优雅的去项目,还需要具备良好的项目管理特性。那么我们的项目如何高效管理呢?

不同团队、不同会采用不同的方法来管理,在我看来有 3 个重要的点,分别是项目和项目的高效开发流程、使用 Makefile 管理项目将比较管理自动化。我们可以通过生成代码、使用工具、方法等CI/CD系统来将项目管理自动化。具体见下图:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

高效的开发流程

您可以回想开发流程的知识,回去比较模糊了,习习下08的成本讲的内容,因为这部分很重要。

使用 Makefile 管理项目

为了更好地管理项目,除了一个高效的开发流程之外,Makefile 也很重要。Makefile 可以将项目管理的工作通过 Makefile 依赖的方式实现自动化,除了可以提高管理效率之外,还能够减少人为操作带来的效果,并统一操作方式,使规范。

IAM项目的所有操作均是通过Makefile来完成的,Makefile完成了如下操作:


build 为主机平台构建源代码。

build.multiarch 为多个平台构建源代码。请参阅选项平台。

image 为主机 arch 构建 docker 镜像。

image.multiarch 为多个平台构建 docker 镜像。请参阅选项平台。

push 为主机 arch 构建 docker 镜像并将镜像推送到注册表。

push.multiarch 为多个平台构建 docker 镜像并将镜像推送到注册表。

deploy 将更新的组件部署到开发环境。

clean 删除所有通过构建创建的文件。

lint 检查go源代码的语法和样式。

测试 运行单元测试。

覆盖运行单元测试并获得测试覆盖率。

发布发布iam

格式化 Gofmt(重新格式化)包源(如果存在,则排除供应商目录)。

verify -copyright 验证所有文件的样板标头。

add -copyright 确保源代码文件具有版权许可标头。

gen 生成所有必要的文件,例如错误代码文件。

ca为所有iam 组件 生成 CA 文件。

install安装iam系统及其所有组件。

swagger 生成 swagger 文档。

serve-swagger 服务 swagger 规范和文档。

依赖 项安装必要的依赖项。

tools 安装依赖工具。

check -updates 检查go项目的过时依赖项。

help显示此帮助信息。

自动生成代码

代码的理念现在越来越低流行。虽然低很多,但确实有很多优点,例如:

自动化生成代码,减少工作量,提高工作效率。

代码有既定规则,相比人工代码,更具有更高、更规范。

看着,自动生成代码的趋势,比如 Kubernetes 项目有很多代码自动生成的。我认为,想写出一个优雅的 Go 项目,你也应该考虑考虑哪些地方的代码现在可以自动生成。在IAM 中门课中生成大量参考的代码,这是自动的,我放项目在这里供你使用:

文档错误码、错误码说明。

自动生成文件的.go 。

示例使用 gotests 工具,自动生成单元测试用。

使用 Swagger 工具,自动生成 Swagger 文档。

使用 Mock 工具,自动生成接口的 Mock 实例。

善用工具

在开发项目的过程中,我们必须使用工具,来帮助我们完成一些工作。利用工具可以带来很多好处:

解放解放,提高工作效率。

使用工具的确定性,可以确保执行结果的一致性。例如,使用 golangci-lint 对代码进行检查,可以确保开发者开发的代码至少都遵循 golangci-lint 的代码检查规范。

有实现自动化的CI,可以将工具集成到/CD流程中,触发流水线自动执行。

去项目中,有哪些工具可以为我们所用吗?这里我给你整理了一些有用的工具,交给我们:

GO 语言项目开发实战 – 设计方法:如何写出优雅的 Go 项目?

所有这些工具都可以通过下面的方式安装。


$ cd $IAM_ROOT

$制作工具。安装

提高这些工具的项目项目使用程度,提高整个项目的自动化程度,提高维护效率。

配对 CI/CD

代码在合并入主干时,应该有一套CI/CD流程来自动化地对进行检查、编译、测试等,只有通过后的代码才能并入主干。通过CI/CD流程来保证代码的质量。目前比较流行的 CI/CD 工具有 Jenkins、GitLab、Argo、Github Actions、JenkinsX 等。在第51 和第 52 讲中,我会详细介绍 CI/CD 的原理和实战。

编写优秀的项目文档

最后,一个优雅的项目,还应该有完善的文档。例如README.md、安装文档、开发文档、文档、API接口文档、设计文档等。这些内容在第04期讲的文档规范部分有详细介绍,你可以去复习下。

总结

使用 Go 语言做项目开发,核心目的其实就是开发一个优雅的 Go 项目。如何开发一个优雅的 Go 项目呢?Go 项目包含三大项目,即 Go 应用、项目管理、项目文档,因此开发一个优雅的项目Go 项目,其实就是编写这些 Go 应用、高效管理项目和编写高质量的项目文档的方式。