How to Start a Go Project
Create a Go module, write your first program, run tests, manage dependencies, and know when to use a workspace.
To start a Go project: install Go, create a directory, initialize a module with go mod init, add a package main program, and run go run .. Add dependencies with imports and go mod tidy, put tests in _test.go files, and use go build or go install when you need a compiled command.
What you need before creating a Go project
Install Go, a text editor, and a terminal. The official Go tutorial lists VS Code, GoLand, and Vim as editors with Go support. See the official getting-started tutorial for installation guidance.
Start a one-module Go project
1. Create and enter a directory
mkdir hello-go
cd hello-go
Keep the project root as the directory where you will place go.mod. Running Go commands from this directory lets the toolchain discover the module automatically.
2. Initialize the module
go mod init example.com/yourname/hello-go
Replace the example path with the repository or module path you will use. The command creates go.mod, which records the module path and Go version and later records dependencies. The official module tutorial explains this workflow.
A simple go.mod looks like this:
module example.com/yourname/hello-go
go 1.23
The exact Go version line depends on the installed toolchain and the version selected when the module is initialized.
3. Add an executable program
Create main.go:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Executable commands must use package main, and the main function is the entry point. See the Go code guide.
4. Run the project
go run .
Expected output:
Hello, World!
The dot means “the package in the current module directory.” You can also run a named file with go run main.go, but go run . is safer once your program contains multiple files in the same package.
Understand the files in a Go project
| File or directory | Purpose |
|---|---|
go.mod |
Declares the module path, Go version, and required modules. |
go.sum |
Stores checksums used to verify downloaded module content. It appears after dependencies are resolved. |
main.go |
Contains the executable’s package main and main function. |
*_test.go |
Contains tests recognized by the go test command. |
internal/ |
Optional packages that can only be imported by code within the parent tree. |
cmd/ |
Optional convention for repositories containing multiple executable commands. |
For a small program, one module with main.go at the root is enough. Add directories when your packages or commands need a clear boundary.
Add a dependency
Import the package in Go source, then tidy the module:
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Go())
}
go mod tidy
go run .
go mod tidy updates go.mod and go.sum so they match the packages used by the module. The Go Modules Reference documents this command and module behavior.
Commit go.mod and go.sum to version control. Do not hand-edit dependency versions unless you understand the module graph; prefer commands such as go get example.com/module@version, followed by go mod tidy.
Write and run tests
Create main_test.go:
package main
import "testing"
func TestExample(t *testing.T) {
want := 2
got := 1 + 1
if got != want {
t.Fatalf("got %d, want %d", got, want)
}
}
Run the current package:
go test
Run every package in the module:
go test ./...
Test files must end in _test.go. The official testing tutorial shows the built-in testing workflow.
Build or install the command
Build
go build
This compiles the package in the current directory. To choose an output path:
go build -o bin/hello-go .
Install
go install
go install builds and installs a command in the configured Go binary directory. Ensure that directory is on your PATH if you want to invoke the command by name. The Go code guide covers both commands.
Choose a useful project layout
Start with the smallest layout that matches the program:
hello-go/
├── go.mod
├── go.sum # appears when dependencies need it
├── main.go
└── main_test.go
For several commands and reusable packages, a common expansion is:
project/
├── go.mod
├── cmd/
│ ├── api/main.go
│ └── worker/main.go
├── internal/
│ └── config/config.go
└── pkg/
└── client/client.go
These directory names are conventions, not required by Go. Package declarations and import paths determine how code is organized.
When to use a Go workspace
A normal project needs one go.mod. Use a workspace when a repository contains multiple modules that must be developed together, such as an application and a separately versioned library.
mkdir workspace
cd workspace
go work init ./module-a ./module-b
This creates go.work and lists the modules that should be used together. You can add another module later:
go work use ./module-c
The official workspace tutorial describes this multi-module workflow. Do not add go.work merely because a single-module project has dependencies; go.mod already handles those.
Capture a Go project’s documentation or demo pages
If your Go project has a public README, documentation site, or rendered example that you need to archive or include in a release workflow, you can capture the page yourself with a browser. For repeatable automation, a screenshot API removes browser setup.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. It accepts one GET request and returns a PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; each response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for the complete option list. A minimal Go program is:
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.screenshotneo.com/v1/shot?access_key=YOUR_API_KEY&url=https%3A%2F%2Fstripe.com", nil)
if err != nil {
panic(err)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode >= 300 {
body, _ := io.ReadAll(res.Body)
panic(fmt.Sprintf("ScreenshotNeo returned %s: %s", res.Status, body))
}
out, err := os.Create("shot.webp")
if err != nil {
panic(err)
}
defer out.Close()
if _, err := io.Copy(out, res.Body); err != nil {
panic(err)
}
}
Equivalent requests:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
You can configure full-page capture with lazy images loaded, CSS element capture, dark mode, device presets or custom viewports, retina scale, PDF paper settings and page ranges, custom CSS and JavaScript, clicks, waits, hidden selectors, blocked ads or resource types, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, and usage reporting. The MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
There is a free plan with 1,000 screenshots per month and no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan. Create a free ScreenshotNeo account.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
go: command not found |
Go is not installed or its binary directory is not on PATH. |
Install Go from the official distribution and reopen the terminal. |
go: cannot find main module |
You ran a module command outside the directory containing go.mod. |
Change to the project root or initialize the module there. |
no required module provides package |
An import is missing from the module graph. | Check the import path, run go get for the intended module, then run go mod tidy. |
package command-line-arguments is not a main package |
The executable source does not declare package main. |
Use package main and define func main(). |
| Tests are not discovered | The file does not end in _test.go, or the test function has the wrong signature. |
Use func TestName(t *testing.T) in a _test.go file. |
| Imports are formatted or reordered unexpectedly | Go formatting is opinionated. | Run gofmt -w . or use editor save formatting. |
| A ScreenshotNeo response is not an image | The URL may have failed, timed out, shown a bot check, or returned another page verdict. | Inspect the HTTP status and X-Page-Verdict/X-Billed headers before saving the body as an image. |
Performance, reliability, and cost considerations
- Build speed: keep packages focused and avoid unnecessary dependencies. Go’s module cache prevents repeated downloads on the same machine.
- Reproducibility: commit
go.modandgo.sum, run tests withgo test ./..., and build from the module root. - Workspace scope: use
go.workonly while coordinating multiple modules; publish and version each module through its owngo.mod. - Screenshot cost: ScreenshotNeo bills only clean shots. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response headers show the verdict and billing result.
- Capture throughput: use caching with a chosen TTL, asynchronous jobs with signed webhooks, or bulk capture for up to 100 URLs per call when your workflow needs many pages.
FAQ
Do I need a repository before running go mod init?
No. You can begin locally with a module path you control and change it later if the project is published.
Should I run go mod tidy after every edit?
Run it after changing imports or dependency versions. It keeps module metadata aligned with the source.
Is go run . a production build?
No. It compiles and runs for development. Use go build to produce a binary you can package or deploy.
Can one Go module contain several commands?
Yes. Put separate main packages in directories such as cmd/api and cmd/worker, then build each directory.
When should I create go.work?
Create it when multiple independent modules need local development together. A single module does not need a workspace.


