<!doctype html><metacharset=utf-8><metaname=viewportcontent="width=device-width,initial-scale=1"><linkrel=stylesheethref=https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown-dark.css><style>.markdown-body{box-sizing:border-box;min-width:200px;max-width:980px;margin:0auto;padding:45px}@media(max-width:767px){.markdown-body{padding:15px}}</style><bodyclass=markdown-body><ahref=index>Index</a><h1><aid=user-content-bootstrap-goclass=anchoraria-hidden=truehref=#bootstrap-go><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Bootstrap Go</h1><p>It is hard to write bootstrap tool to quickly create Go service.
So I write this guide instead.
This is a quick checklist for me every damn time I need to write a Go service from scratch.
Also, this is my personal opinion, so feel free to comment.<h2><aid=user-content-structureclass=anchoraria-hidden=truehref=#structure><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Structure</h2><divclass="highlight highlight-text-adblock"><pre>main.go
<spanclass=pl-k>|</span> | models.go</pre></div><p>All business codes are inside <code>internal</code>.
Each business has a different directory <code>business</code>.<p>Inside each business, there are 2 handlers: <code>http</code>, <code>grpc</code>:<ul><li><code>http</code> is for public APIs (Android, iOS, ... are clients).<li><code>grpc</code> is for internal APIs (other services are clients).<li><code>consumer</code> is for consuming messages from queue (Kafka, RabbitMQ, ...).</ul><p>For each handler, there are usually 3 layers: <code>handler</code>, <code>service</code>, <code>repository</code>:<ul><li><code>handler</code> interacts directly with gRPC, REST or consumer using specific codes (cookies, ...) In case gRPC, there are frameworks outside handle for us so we can write business/logic codes here too. But remember, gRPC only.<li><code>service</code> is where we write business/logic codes, and only business/logic codes is written here.<li><code>repository</code> is where we write codes which interacts with database/cache like MySQL, Redis, ...<li><code>models</code> is where we put all request, response, data models.</ul><p>Location:<ul><li><code>handler</code> must exist inside <code>grpc</code>, <code>http</code>, <code>consumer</code>.<li><code>service</code>, <code>models</code> can exist directly inside of <code>business</code> if both <code>grpc</code>, <code>http</code>, <code>consumer</code> has same business/logic.<li><code>repository</code> should be placed directly inside of <code>business</code>.</ul><h2><aid=user-content-do-not-repeatclass=anchoraria-hidden=truehref=#do-not-repeat><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Do not repeat!</h2><p>If we have too many services, some of the logic will be overlapped.<p>For example, service A and service B both need to make POST call API to service C.
If service A and service B both have libs to call service C to do that API, we need to move the libs to some common pkg libs.
So in the future, service D which needs to call C will not need to copy libs to handle service C api but only need to import from common pkg libs.<p>Another bad practice is adapter service.
No need to write a new service if what we need is just common pkg libs.<h2><aid=user-content-taste-on-style-guideclass=anchoraria-hidden=truehref=#taste-on-style-guide><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Taste on style guide</h2><h3><aid=user-content-stop-using-global-varclass=anchoraria-hidden=truehref=#stop-using-global-var><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Stop using global var</h3><p>If I see someone using global var, I swear I will shoot them twice in the face.<p>Why?<ul><li>Can not write unit test.<li>Is not thread safe.</ul><h3><aid=user-content-use-functional-options-but-dont-overuse-itclass=anchoraria-hidden=truehref=#use-functional-options-but-dont-overuse-it><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use functional options, but don't overuse it!</h3><p>For simple struct with 1 or 2 fields, no need to use functional options.<p><ahref=https://go.dev/play/p/0XnOLiHuoz3rel=nofollow>Example</a>:<divclass="highlight highlight-source-go"><pre><spanclass=pl-k>func</span><spanclass=pl-en>main</span>() {
}</pre></div><p>In above example, I construct <code>s</code> with <code>WithA</code> and <code>WithB</code> option.
No need to pass direct field inside <code>s</code>.<h3><aid=user-content-use-errgroup-as-much-as-possibleclass=anchoraria-hidden=truehref=#use-errgroup-as-much-as-possible><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use <ahref=https://pkg.go.dev/golang.org/x/sync/errgrouprel=nofollow>errgroup</a> as much as possible</h3><p>If business logic involves calling too many APIs, but they are not depend on each other.
We can fire them parallel :)<p>Personally, I prefer <code>errgroup</code> to <code>WaitGroup</code> (<ahref=https://pkg.go.dev/sync#WaitGrouprel=nofollow>https://pkg.go.dev/sync#WaitGroup</a>).
Because I always need deal with error.<p>Example:<divclass="highlight highlight-source-go"><pre><spanclass=pl-s1>eg</span>, <spanclass=pl-s1>egCtx</span><spanclass=pl-c1>:=</span><spanclass=pl-s1>errgroup</span>.<spanclass=pl-en>WithContext</span>(<spanclass=pl-s1>ctx</span>)
}</pre></div><h3><aid=user-content-use-semaphore-when-need-to-implement-workerpoolclass=anchoraria-hidden=truehref=#use-semaphore-when-need-to-implement-workerpool><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use <ahref=https://pkg.go.dev/golang.org/x/sync/semaphorerel=nofollow>semaphore</a> when need to implement WorkerPool</h3><p>Please don't use external libs for WorkerPool, I don't want to deal with dependency hell.<h2><aid=user-content-external-libsclass=anchoraria-hidden=truehref=#external-libs><spanaria-hidden=trueclass="octicon octicon-link"></span></a>External libs</h2><h3><aid=user-content-no-need-vendorclass=anchoraria-hidden=truehref=#no-need-vendor><spanaria-hidden=trueclass="octicon octicon-link"></span></a>No need <code>vendor</code></h3><p>Only need if you need something from <code>vendor</code>, to generate mock or something else.<h3><aid=user-content-use-buildgo-to-include-build-tools-in-gomodclass=anchoraria-hidden=truehref=#use-buildgo-to-include-build-tools-in-gomod><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use <code>build.go</code> to include build tools in go.mod</h3><p>To easily control version of build tools.<p>For example <code>build.go</code>:<divclass="highlight highlight-source-go"><pre><spanclass=pl-c>//go:build tools</span>
)</pre></div><p>And then in <code>Makefile</code>:<divclass="highlight highlight-source-makefile"><pre><spanclass=pl-en>build</span>:
go install github.com/golang/protobuf/protoc-gen-go</pre></div><p>We always get the version of build tools in <code>go.mod</code> each time we install it.
Future contributors will not cry anymore.<h3><aid=user-content-dont-use-cli-libs-spf13cobra-urfavecli-just-for-go-serviceclass=anchoraria-hidden=truehref=#dont-use-cli-libs-spf13cobra-urfavecli-just-for-go-service><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Don't use cli libs (<ahref=https://github.com/spf13/cobra>spf13/cobra</a>, <ahref=https://github.com/urfave/cli>urfave/cli</a>) just for Go service</h3><p>What is the point to pass many params (<code>do-it</code>, <code>--abc</code>, <code>--xyz</code>) when what we only need is start service?<p>In my case, service starts with only config, and config should be read from file or environment like <ahref=https://12factor.net/rel=nofollow>The Twelve Factors</a> guide.<h3><aid=user-content-dont-use-grpc-ecosystemgrpc-gatewayclass=anchoraria-hidden=truehref=#dont-use-grpc-ecosystemgrpc-gateway><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Don't use <ahref=https://github.com/grpc-ecosystem/grpc-gateway>grpc-ecosystem/grpc-gateway</a></h3><p>Just don't.<p>Use <ahref=https://github.com/protocolbuffers/protobuf-go>protocolbuffers/protobuf-go</a>, <ahref=https://github.com/grpc/grpc-go>grpc/grpc-go</a> for gRPC.<p>Write 1 for both gRPC, REST sounds good, but in the end, it is not worth it.<h3><aid=user-content-dont-use-uberprototool-use-bufbuildbufclass=anchoraria-hidden=truehref=#dont-use-uberprototool-use-bufbuildbuf><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Don't use <ahref=https://github.com/uber/prototool>uber/prototool</a>, use <ahref=https://github.com/bufbuild/buf>bufbuild/buf</a></h3><p>prototool is deprecated, and buf can generate, lint, format as good as prototool.<h3><aid=user-content-use-gin-gonicgin-for-restclass=anchoraria-hidden=truehref=#use-gin-gonicgin-for-rest><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use <ahref=https://github.com/gin-gonic/gin>gin-gonic/gin</a> for REST.</h3><p>Don't use <code>gin.Context</code> when pass context from handler layer to service layer, use <code>gin.Context.Request.Context()</code> instead.<h3><aid=user-content-if-you-want-log-just-use-uber-gozapclass=anchoraria-hidden=truehref=#if-you-want-log-just-use-uber-gozap><spanaria-hidden=trueclass="octicon octicon-link"></span></a>If you want log, just use <ahref=https://github.com/uber-go/zap>uber-go/zap</a></h3><p>It is fast!<ul><li>Don't overuse <code>func (*Logger) With</code>. Because if log line is too long, there is a possibility that we can lost it.<li>Use <code>MarshalLogObject</code> when we need to hide some field of object when log (field is long or has sensitive value)<li>Don't use <code>Panic</code>. Use <code>Fatal</code> for errors when start service to check dependencies. If you really need panic level, use <code>DPanic</code>.<li>If doubt, use <code>zap.Any</code>.<li>Use <code>contextID</code> or <code>traceID</code> in every log lines for easily debug.</ul><h3><aid=user-content-to-read-config-use-spf13viperclass=anchoraria-hidden=truehref=#to-read-config-use-spf13viper><spanaria-hidden=trueclass="octicon octicon-link"></span></a>To read config, use <ahref=https://github.com/spf13/viper>spf13/viper</a></h3><p>Only init config in main or cmd layer.
Do not use <code>viper.Get...</code> in business layer or inside business layer.<p>Why?<ul><li>Hard to mock and test<li>Put all config in single place for easily tracking</ul><p>Also, be careful if config value is empty.
You should decide to continue or stop the service if there is no config.<h3><aid=user-content-dont-overuse-orm-libs-no-need-to-handle-another-layer-above-sqlclass=anchoraria-hidden=truehref=#dont-overuse-orm-libs-no-need-to-handle-another-layer-above-sql><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Don't overuse ORM libs, no need to handle another layer above SQL.</h3><p>Each ORM libs has each different syntax.
To learn and use those libs correctly is time consuming.
So just stick to plain SQL.
It is easier to debug when something is wrong.<p>But <code>database/sql</code> has its own limit.
For example, it is hard to get primary key after insert/update.
So may be you want to use ORM for those cases.
I hear that <ahref=https://github.com/go-gorm/gorm>go-gorm/gorm</a>, <ahref=https://github.com/ent/ent>ent/ent</a> is good.<h3><aid=user-content-if-you-want-test-just-use-stretchrtestifyclass=anchoraria-hidden=truehref=#if-you-want-test-just-use-stretchrtestify><spanaria-hidden=trueclass="octicon octicon-link"></span></a>If you want test, just use <ahref=https://github.com/stretchr/testify>stretchr/testify</a>.</h3><p>It is easy to write a suite test, thanks to testify.
Also, for mocking, there are many options out there.
Pick 1 then sleep peacefully.<h3><aid=user-content-if-need-to-mock-choose-matryermoq-or-golangmockclass=anchoraria-hidden=truehref=#if-need-to-mock-choose-matryermoq-or-golangmock><spanaria-hidden=trueclass="octicon octicon-link"></span></a>If need to mock, choose <ahref=https://github.com/matryer/moq>matryer/moq</a> or <ahref=https://github.com/golang/mock>golang/mock</a></h3><p>The first is easy to use but not powerful as the later.
If you want to make sure mock func is called with correct times, use the later.<p>Example with <code>matryer/moq</code>:<divclass="highlight highlight-source-go"><pre><spanclass=pl-c>// Only gen mock if source code file is newer than mock file</span>
<spanclass=pl-s1>a</span><spanclass=pl-c1>:=</span><spanclass=pl-en>int32</span>(<spanclass=pl-s1>servicev1</span>.<spanclass=pl-c1>ReasonCode_ABC</span>)</pre></div><h3><aid=user-content-use-stringer-if-you-want-your-type-enum-can-be-print-as-stringclass=anchoraria-hidden=truehref=#use-stringer-if-you-want-your-type-enum-can-be-print-as-string><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use <ahref=https://pkg.go.dev/golang.org/x/tools/cmd/stringerrel=nofollow>stringer</a> if you want your type enum can be print as string</h3><divclass="highlight highlight-source-go"><pre><spanclass=pl-k>type</span><spanclass=pl-smi>Drink</span><spanclass=pl-smi>int</span>
<spanclass=pl-c><spanclass=pl-c>#</span> Run inside directory which contains Drink</span>
stringer -type=Drink</pre></div><h3><aid=user-content-dont-waste-your-time-rewrite-rate-limiter-if-your-use-case-is-simple-use-rate-or-go-redisredis_rateclass=anchoraria-hidden=truehref=#dont-waste-your-time-rewrite-rate-limiter-if-your-use-case-is-simple-use-rate-or-go-redisredis_rate><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Don't waste your time rewrite rate limiter if your use case is simple, use <ahref=https://pkg.go.dev/golang.org/x/time/raterel=nofollow>rate</a> or <ahref=https://github.com/go-redis/redis_rate>go-redis/redis_rate</a></h3><p>rate if you want rate limiter locally in your single instance of service.
redis_rate if you want rate limiter distributed across all your instances of service.<h3><aid=user-content-replace-go-fmt-goimports-with-mvdangofumptclass=anchoraria-hidden=truehref=#replace-go-fmt-goimports-with-mvdangofumpt><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Replace <code>go fmt</code>, <code>goimports</code> with <ahref=https://github.com/mvdan/gofumpt>mvdan/gofumpt</a>.</h3><p><code>gofumpt</code> provides more rules when format Go codes.<h3><aid=user-content-use-golangcigolangci-lintclass=anchoraria-hidden=truehref=#use-golangcigolangci-lint><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Use <ahref=https://github.com/golangci/golangci-lint>golangci/golangci-lint</a>.</h3><p>No need to say more.
Lint or get the f out!<p>If you get <code>fieldalignment</code> error, use <ahref=https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignmentrel=nofollow>fieldalignment</a> to fix them.<divclass="highlight highlight-source-shell"><pre><spanclass=pl-c><spanclass=pl-c>#</span> Install</span>
fieldalignment -fix ./internal/business/<spanclass=pl-k>*</span>.go</pre></div><h2><aid=user-content-thanksclass=anchoraria-hidden=truehref=#thanks><spanaria-hidden=trueclass="octicon octicon-link"></span></a>Thanks</h2><ul><li><ahref=https://github.com/uber-go/guide/blob/master/style.md>Uber Go Style Guide</a><li><ahref=https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apisrel=nofollow>Functional options for friendly APIs</a><li><ahref=https://google.github.io/styleguide/go/indexrel=nofollow>Google Go Style</a></ul><ahref=mailto:hauvipapro+posts@gmail.com>Feel free to ask me via email</a>