
Introduction
Welcome to the first article of the Golang Backend Real-World Series.
In this series, we will build backend applications using real-world engineering practices, including:
- Clean folder structure
- Modular design
- Dependency injection
- Config management
- Logging & monitoring
- REST API & middleware
- Database, caching, background jobs
- CI/CD, Docker, and production deployment
This first part focuses on the foundation of every scalable backend project:
👉 A clean, well-organized project structure.
A messy folder structure will make your code hard to maintain, impossible to scale, and painful for future developers.
A clean structure does the opposite — it boosts clarity, consistency, testability, and long-term growth.
Why Folder Structure Matters
When a project grows beyond a few files, bad structure causes:
- Harder debugging
- Tight coupling
- Difficult onboarding for new developers
- Code duplication
- Complex refactoring
A clean layout helps:
- Organize source code by functionality
- Make testing easier
- Improve long-term maintainability
- Follow industry conventions
- Support microservices or modular expansion later
Recommended Folder Structure
Here is a widely used structure for production-ready Golang services:
your-project/
│
├── cmd/
│ └── api/
│ └── main.go
│
├── internal/
│ ├── config/
│ ├── server/
│ ├── user/
│ │ ├── handler.go
│ │ ├── service.go
│ │ └── repository.go
│ ├── auth/
│ └── ...
│
├── pkg/
│ ├── logger/
│ ├── response/
│ └── middleware/
│
├── configs/
│ └── config.yaml
│
├── scripts/
│
├── test/
│
├── go.mod
└── go.sum
Folder Breakdown
1. /cmd
Contains the entry point(s) of the application.
Example:
// cmd/api/main.go
package main
import (
"your-project/internal/server"
)
func main() {
server.Run()
}
2. /internal
The most important folder.
Contains application logic that should not be imported by other services.
Structure suggestion:
config/→ load environment variables or YAML/JSON configserver/→ bootstrap Gin/Fiber/Echo serveruser/,auth/, etc. → domain modules- Each module has:
handler.go(HTTP layer)service.go(business logic)repository.go(database)
3. /pkg
Reusable code that can be imported by other Go services.
Examples:
- Logging wrapper
- Custom middleware
- Standardized API response
4. /configs
Store external configs (YAML/JSON), ideal for multiple environments:
configs/
├── config.dev.yaml
├── config.staging.yaml
└── config.prod.yaml
Install Required Dependencies
For this series, we’ll use Gin for the HTTP framework.
go get -u github.com/gin-gonic/gin
Basic server:
// internal/server/server.go
package server
import "github.com/gin-gonic/gin"
func Run() {
r := gin.Default()
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "OK"})
})
r.Run(":8080")
}
Run your server:
go run cmd/api/main.go
API Response Standardization
Create a small helper in /pkg/response.
package response
import "github.com/gin-gonic/gin"
func Success(c *gin.Context, data interface{}) {
c.JSON(200, gin.H{
"success": true,
"data": data,
})
}
func Error(c *gin.Context, message string) {
c.JSON(400, gin.H{
"success": false,
"error": message,
})
}
This makes your API consistent and easy for frontend/mobile to consume.
Best Practices You Should Follow Now
✔ Use internal/ for domain logic
✔ Group modules by feature, not by technical layer
✔ Create clear boundaries between handler → service → repository
✔ Keep reusable utilities inside pkg/
✔ Do not place random files in root directory
✔ Keep main.go small — only for startup, configs, DI
Conclusion
Your project is now ready with a clean, scalable, production-friendly structure.
This foundation will make the next steps easy:
- Building user modules
- Connecting to MySQL/Postgres
- Adding caching (Redis)
- JWT authentication
- Docker & deployment
Coming Next (Part 2)
👉 Implementing the User Module (handler, service, repository)
With working APIs, schema, and DB migrations.
