20 Collaborative Development Why Modularization Management Can Enhance Development Efficiency

20 Collaborative Development - Why Modularization Management Can Enhance Development Efficiency #

Any business evolves from simple to complex. And in the process of business evolution, technology evolves from monolithic to multi-module, multi-service. The core purpose of this evolution is to reuse code and improve efficiency. In this lecture, I will introduce how Go language improves development efficiency through modular management.

Packages in Go Language #

What are Packages #

When a business is very simple, you can even write the code in a single Go file. But as the business becomes more complex, you will find it difficult to maintain the code if it is all in one Go file. This is when you need to extract the code and put the code of the same business into a directory. In Go language, this directory is called a package.

In Go language, a package is defined using the package keyword, and the most common one is the main package, which is defined as follows:

package main

In addition, the fmt package that has been used frequently in the previous chapters is also declared using the package keyword.

A package is an independent space where you can define functions, structures, etc. We consider these functions and structures as belonging to this package.

Using Packages #

If you want to use the functions or structures in a package, you need to import the package before you can use them. For example, the commonly used fmt package is imported as follows:

package main

import "fmt"

func main() {

   fmt.Println("Import the fmt package before using it")

}

To import a package, you need to use the import keyword. If you need to import multiple packages at the same time, you can use parentheses. The example code is as follows:

import (

   "fmt"

   "os"

)

From the above examples, you can see that the example imports the fmt and os packages using parentheses, with one package per line.

Scope #

When it comes to importing and using packages, you have to mention the concept of scope because only functions that meet the scope requirements can be called.

  • In Java, the scope of a class is modified by public, private, and other modifiers.
  • However, in Go, there are no such scope modifiers. Instead, scope is determined by whether the first letter of the identifier is capitalized, which also reflects the simplicity of Go language.

Take the Println function in the fmt package mentioned above as an example:

  • Its first letter is capital P, so this function can be used in the main package.
  • If the first letter of the Println function is lowercase p, it can only be used within the fmt package and cannot be used across packages.

Here is a summary of the scope in Go language:

  • In Go language, if the first letter of a definition, such as a function, variable, structure, etc., is capitalized, it can be used by other packages.
  • Conversely, if the first letter is lowercase, it can only be used within the same package.

Custom Packages #

You can also customize your own packages to organize code of the same business or responsibility together. For example, if you have a util package to store some commonly used utility functions, the project structure is as follows:

ch20

├── main.go

└── util

    └── string.go

In Go language, a package corresponds to a folder, and the above project structure example verifies this. In this example, there is a util folder containing a string.go file, which is a Go language file belonging to the util package, and its package definition is as follows:

ch20/util/string.go

package util

As can be seen, a package in Go language is a way of organizing code. It categorizes code through packages, making it easier to maintain and be called by other packages, thus improving team collaboration efficiency.

init Function #

In addition to the special main function, Go language also has another special function called init, which can be used to implement some package-level initialization operations.

The init function has no return value and no parameters. It is executed before the main function, as shown in the following code:

func init() {

   fmt.Println("init in main.go")

}

A package can have multiple init functions, but their execution order is indeterminate. Therefore, if you define multiple init functions, you must ensure that they are independent and not dependent on each other.

So what is the purpose of the init function? In fact, when importing a package, you can perform some necessary initialization operations on this package, such as database connections and data checks, to ensure that we can use this package correctly.

Modules in Go Language #

If packages are a relatively low-level form of code organization, then modules are a higher level. In Go language, a module can contain many packages, so a module is a collection of related packages.

In Go language:

  • A module is usually a project, such as the gotour project used in this column example.
  • It can also be a framework, such as the commonly used web framework gin.

go mod #

Go language provides the go mod command to create a module (project). For example, to create a gotour module, you can use the following command: ➜ go mod init gotour

go: creating new go.mod: module gotour

After running this command, you will see a folder named gotour created, with a go.mod file inside, which contains the following contents:

module gotour

go 1.15
  • The first line is the module name of the project, which is gotour.
  • The second line indicates that the module needs at least Go 1.15 version of SDK to compile.

Tip: It is recommended to start the module name with your own domain name, such as flysnow.org/gotour, which can largely ensure the uniqueness of the module name and avoid naming conflicts with other modules.

Using Third-Party Modules #

Why does modularization improve development efficiency? The most important reason is that it reuses existing modules, and Go is no exception. For example, you can extract common code in your project into a module, which can be used by other projects without duplication. Similarly, on GitHub, there are many open-source Go projects, each of which is an independent module that can be directly used to improve our development efficiency, such as the web framework gin-gonic/gin.

As we all know, before using third-party modules, we need to set up the Go proxy, namely GOPROXY, so that we can obtain the third-party modules.

Here, I recommend using the proxy goproxy.io, which is very easy to use and has fast speed. To use this proxy, you need to set up the following environment variables:

go env -w GO111MODULE=on

go env -w GOPROXY=https://goproxy.io,direct

Open the terminal and enter this command to set it up successfully.

In actual project development, in addition to third-party modules, we also have modules developed by ourselves, which are hosted on our company’s GitLab. In this case, we need to exclude the domain name of the company’s Git code repository from the Go PROXY. For this, Go provides the GOPRIVATE environment variable to help us achieve this goal. The following command can be used to set up GOPRIVATE:

# Set private repositories that do not go through the proxy, separated by commas (optional)

go env -w GOPRIVATE=*.corp.example.com

The above domain name is just an example. In actual use, you need to change it to the domain name of your own company’s private repository.

Now that everything is ready, we can start using third-party modules. Suppose we want to use the Gin web framework. First, we need to install it. Use the following command to install the Gin web framework:

go get -u github.com/gin-gonic/gin

After the installation is successful, we can import it into our code using the import statement, just like the Go standard packages. The code is shown below:

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
)

func main() {
    fmt.Println("Import the fmt package first to use it")
    r := gin.Default()
    r.Run()
}

The above code cannot be compiled at this time because we haven’t synchronized the dependencies of the Gin module yet, which means we haven’t added it to the go.mod file. The following command can add the missing module:

go mod tidy

Running this command will add the missing module and remove any unnecessary modules. Now, if you check the go.mod file, you will see that its content has changed to:

module gotour

go 1.15

require (
    github.com/gin-gonic/gin v1.6.3
    github.com/golang/protobuf v1.4.2 // indirect
    github.com/google/go-cmp v0.5.2 // indirect
    github.com/kr/text v0.2.0 // indirect
    github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
    github.com/modern-go/reflect2 v1.0.1 // indirect
    github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
    github.com/stretchr/testify v1.6.1 // indirect
    golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect
    golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
    gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
    gopkg.in/yaml.v2 v2.3.0 // indirect
)

Therefore, we don’t need to manually modify the go.mod file. The Go toolchain, such as the go mod tidy command, can automatically maintain, add, or modify the contents of go.mod.

Summary #

In Go, a package is a collection of source files that are compiled together in the same directory. A package contains functions, types, variables, and constants. To call a package from another package, the first letter of the function, type, variable, or constant needs to be capitalized.

A module is a collection of related packages. It contains many packages that are used to implement the module. Modules can also be used to provide completed modules to other projects (modules), achieving code reuse and improving development efficiency.

So for your project (module), it has a three-layer structure of module ➡ package ➡ function/type. In the same module, code can be organized into packages to achieve code reuse. In different modules, they need to be imported to achieve this goal.

There is a saying in the programming community: Don’t reinvent the wheel. Using ready-made wheels can improve development efficiency and reduce the bug rate. The module and package capabilities provided by Go can help us use existing wheels and improve work efficiency in multi-person collaborative development.

Finally, here is some homework for you: based on the modular decomposition of your project, extract some common modules to be used by more projects. It is believed that this will greatly improve your development efficiency.