A high-performance, cost-effective email validation service designed for indie hackers and small startups. The service validates email addresses in real-time, checking syntax, domain existence, MX records, and detecting disposable email providers.
- ✅ Syntax validation
- 🌐 Domain existence check
- 📨 MX record validation
- 🚫 Disposable email detection
- 👥 Role-based email detection
- ✍️ Typo suggestions
- 📊 Real-time monitoring
- 🔄 Batch processing support
- Go 1.21+
- Prometheus (metrics)
- Grafana (monitoring)
- Docker & Docker Compose
- Go 1.21 or higher
- Docker and Docker Compose
- Git
- VSCode (recommended)
# Using Homebrew
brew install go
# Verify installation
go version # Should show go version 1.21 or higher
# Add Go repository
sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt update
# Install Go
sudo apt install golang-go
# Verify installation
go version
- Download the installer from Go Downloads
- Run the installer
- Open Command Prompt and verify:
go version
Add these to your shell profile (~/.bashrc
, ~/.zshrc
, etc.):
# Go environment variables
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
Reload your shell or run:
source ~/.bashrc # or ~/.zshrc
- Download Docker Desktop for Mac
- Install and start Docker Desktop
- Verify installation:
docker --version
docker-compose --version
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Install Docker Compose
sudo apt install docker-compose
# Start Docker
sudo systemctl start docker
# Add your user to docker group (optional)
sudo usermod -aG docker $USER
# Verify installation
docker --version
docker-compose --version
- Download and install Docker Desktop for Windows
- Enable WSL 2 if prompted
- Start Docker Desktop
- Verify in Command Prompt:
docker --version
docker-compose --version
-
Install VSCode
- Download from Visual Studio Code
- Install for your platform
-
Install Required Extensions
# Essential Extensions - Go (by Go Team at Google) - Docker (by Microsoft) - GitLens (by GitKraken) - Remote Development (by Microsoft) # Recommended Extensions - Error Lens - Go Test Explorer - YAML - Markdown All in One
-
Configure Go Extension After installing the Go extension:
- Open Command Palette (Cmd/Ctrl + Shift + P)
- Type "Go: Install/Update Tools"
- Select all tools and click OK
This will install:
- gopls (Go language server)
- dlv (debugger)
- golangci-lint (linter)
- goimports (code formatter)
- and other essential Go tools
-
VSCode Settings Create or update
.vscode/settings.json
:{ "go.useLanguageServer": true, "go.lintTool": "golangci-lint", "go.lintFlags": ["--fast"], "editor.formatOnSave": true, "[go]": { "editor.defaultFormatter": "golang.go", "editor.codeActionsOnSave": { "source.organizeImports": true } } }
-
Debugging Setup Create
.vscode/launch.json
:{ "version": "0.2.0", "configurations": [ { "name": "Launch Email Validator", "type": "go", "request": "launch", "mode": "auto", "program": "${workspaceFolder}", "env": {}, "args": [] } ] }
# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Install air for hot reload (optional)
go install github.com/cosmtrek/air@latest
# Install mockgen for testing
go install github.com/golang/mock/mockgen@latest
- Clone the Repository
git clone https://github.com/yourusername/email-verifier.git
cd email-verifier
- Install Dependencies
go mod tidy
- Start Monitoring Stack
docker-compose up -d
- Build and Run the Service
go build
./emailvalidator
The service will be available at:
- API: http://localhost:8080
- Metrics: http://localhost:8080/metrics
- Grafana: http://localhost:3000 (admin/admin)
- Prometheus: http://localhost:9090
.
├── cmd/ # Command line tools
├── internal/
│ ├── api/ # HTTP handlers
│ ├── model/ # Data models
│ └── service/ # Business logic
├── pkg/
│ ├── validator/ # Email validation logic
│ └── monitoring/ # Metrics and monitoring
├── test/ # Load and integration tests
└── config/ # Configuration files
- Run All Tests (excluding load tests)
go test ./... -v -skip "Load"
- Run Tests with Race Detection
go test -race ./... -skip "Load"
Race detection is crucial for identifying potential data races in concurrent code. It's automatically run in CI/CD pipelines and should be run locally before submitting changes.
Common race conditions to watch for:
- Concurrent map access
- Shared variable access without proper synchronization
- Channel operations
- Goroutine lifecycle management
- Run Load Tests
go test ./test/load_test.go -v
- Run Linter
golangci-lint run
- Format Code
go fmt ./...
- Check for Common Mistakes
go vet ./...
The project uses GitHub Actions for CI/CD with the following checks:
- Unit tests with race detection
- Integration tests
- Code linting
- Code formatting
- Security scanning
- Dependency updates
All these checks must pass before code can be merged into the main branch.
go test -v ./... -skip "TestLoadGeneration"
go test -v ./... -skip "TestLoadGeneration" -cover
- The email validator service is running (
./emailvalidator
) - The monitoring stack is up (
docker-compose up -d
) - You can access the service at http://localhost:8080/status
- Start Prerequisites (in separate terminal windows)
# Terminal 1: Start monitoring stack
docker-compose up -d
# Terminal 2: Build and start the service
go build
./emailvalidator
# Terminal 3: Verify service is running
curl http://localhost:8080/status
- Run Load Tests (in a new terminal)
Quick Local Test (30s):
go test ./test -v -run TestLoadGeneration
Custom Load Test:
go test ./test -v -run TestLoadGeneration \
-duration 2m \
-concurrent-users 5 \
-short=false
-
Connection Refused Errors
dial tcp [::1]:8080: connect: connection refused
Even if you think the service is running, verify:
a. Check Service Status
# Check if the process is actually running ps aux | grep emailvalidator # Verify the port is actually listening netstat -an | grep 8080 # or lsof -i :8080
b. Test Service Connectivity
# Try different ways to connect curl http://localhost:8080/status curl http://127.0.0.1:8080/status wget http://localhost:8080/status
c. Check Service Logs
# If running in foreground, check the terminal # If running in background, find and check logs: ps aux | grep emailvalidator
d. Common Solutions:
- If service shows running but won't connect:
# Kill and restart the service pkill emailvalidator ./emailvalidator
- If localhost isn't resolving:
# Add to /etc/hosts if missing: 127.0.0.1 localhost ::1 localhost
- Try explicit IP:
# Modify test/load_test.go to use "http://127.0.0.1:8080" instead of "http://localhost:8080"
- If service shows running but won't connect:
-
Continuous Connection Errors If you're getting repeated connection errors:
a. Check Service Stability
# Run with verbose logging ./emailvalidator -v # Monitor service stability watch -n1 "curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/status"
b. Test Service Load
# Start with minimal load go test ./test -v -run TestLoadGeneration -concurrent-users 1 -duration 10s # If successful, gradually increase load
-
Docker Network Issues If using Docker and still having issues:
# Check Docker network docker network ls docker network inspect bridge # Ensure host.docker.internal is working docker run --rm alpine ping host.docker.internal # Check Prometheus connectivity curl http://localhost:9090/api/v1/targets
-
Quick Verification Script Create a file
verify.sh
:#!/bin/bash echo "Checking service status..." curl -s http://localhost:8080/status || echo "Service not responding" echo -e "\nChecking Docker containers..." docker-compose ps echo -e "\nChecking port usage..." lsof -i :8080 echo -e "\nChecking Prometheus targets..." curl -s http://localhost:9090/api/v1/targets
Run it:
chmod +x verify.sh ./verify.sh
-
Debug Mode Run the service with debug logging:
# Set environment variable for debug mode export DEBUG=true # Run service ./emailvalidator
-
Gradual Testing Start with minimal load and increase gradually:
# 1. Single user, short duration go test ./test -v -run TestLoadGeneration -concurrent-users 1 -duration 10s # 2. If successful, increase duration go test ./test -v -run TestLoadGeneration -concurrent-users 1 -duration 30s # 3. If successful, increase users go test ./test -v -run TestLoadGeneration -concurrent-users 2 -duration 30s
-
Monitoring During Tests Open these in separate terminals:
# Watch service status watch -n1 curl -s http://localhost:8080/status # Monitor system resources top -p $(pgrep emailvalidator) # Watch Docker containers watch -n1 docker-compose ps
-
Access Grafana
- URL: http://localhost:3000
- Username: admin
- Password: admin
-
Import Dashboard
- Go to "+" -> "Import"
- Upload
grafana-dashboard.json
-
View Metrics
- Request rates
- Response times
- Success/failure rates
- Cache performance
- System metrics
The project includes automated workflows for:
- Load Testing
- Runs weekly (Sunday midnight)
- Can be triggered manually
- Customizable duration and concurrent users
- Generates detailed reports
To run manual load test:
- Go to GitHub Actions
- Select "Load Testing"
- Click "Run workflow"
- Configure:
- Test duration (seconds)
- Number of concurrent users
Load test results are available as:
- GitHub Actions logs
- Downloadable artifacts
- PR comments (when run on pull requests)
curl -X POST http://localhost:8080/validate \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
curl -X POST http://localhost:8080/validate/batch \
-H "Content-Type: application/json" \
-d '{"emails": ["[email protected]", "[email protected]"]}'
curl -X POST http://localhost:8080/typo-suggestions \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
curl http://localhost:8080/status
-
Application Health
- Endpoint uptime
- Response times (p95, p99)
- Error rates
- Request volume
-
Business Metrics
- Validation success rates
- Cache hit ratios
- Most used endpoints
- Average validation scores
-
System Metrics
- CPU usage
- Memory usage
- Goroutine count
- GC metrics
-
Service Won't Start
- Check port 8080 is free
- Verify Go version (
go version
) - Check error logs
-
Monitoring Issues
- Ensure Docker is running
- Check container logs (
docker-compose logs
) - Verify Prometheus targets
-
Load Test Failures
- Verify service is running
- Check system resources
- Review test logs
- Fork the repository
- Create your feature branch
- Run tests and linter
- Submit a pull request
MIT License - see LICENSE file for details
golangci-lint is used for code quality checks in this project. It runs multiple linters concurrently and has integrations with popular editors.
# Using Homebrew
brew install golangci-lint
# Verify installation
golangci-lint --version
# Binary installation
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
# Verify installation
golangci-lint --version
The project uses golangci-lint for code quality checks. To run the linter:
# Run all linters
golangci-lint run
# Run specific linters
golangci-lint run --disable-all -E errcheck,gosimple,govet,ineffassign,staticcheck,typecheck,unused
# Run linters with auto-fix
golangci-lint run --fix
-
G107: Potential HTTP request made with variable url
// Incorrect resp, err := http.Get(url) // Correct req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return err } resp, err := http.DefaultClient.Do(req)
-
ineffectual assignment to err
// Incorrect req, err := http.NewRequest("GET", url, nil) resp, err = client.Do(req) // err from NewRequest is lost // Correct req, reqErr := http.NewRequest("GET", url, nil) if reqErr != nil { return reqErr } resp, err = client.Do(req)
-
unused variable/parameter
// Incorrect func process(ctx context.Context, data string) error { return nil // ctx is unused } // Correct func process(_ context.Context, data string) error { return nil }
The linter is integrated into our CI/CD pipeline in .github/workflows/tests.yml
:
- name: Run linter
uses: golangci/golangci-lint-action@v4
with:
version: latest
args: --timeout=5m
skip-cache: false
skip-pkg-cache: false
skip-build-cache: false
only-new-issues: true
- Create
.git/hooks/pre-commit
:
#!/bin/sh
# Run golangci-lint before commit
golangci-lint run
# Check the exit code
if [ $? -ne 0 ]; then
echo "Linting failed! Please fix the issues before committing."
exit 1
fi
- Make it executable:
chmod +x .git/hooks/pre-commit
Our .golangci.yml
enforces:
-
Enabled Linters
errcheck
: Find unchecked errorsgosimple
: Simplify codegovet
: Report suspicious codeineffassign
: Detect ineffectual assignmentsstaticcheck
: State of the art checkstypecheck
: Type-checkingunused
: Find unused code
-
Custom Rules
linters-settings: govet: check-shadowing: true errcheck: check-type-assertions: true gosimple: checks: ["all"] staticcheck: checks: ["all"]
-
Ignored Issues
EXC0001
: Complexity checks for test filesST1000
: Package comment style
{
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"],
"go.lintOnSave": "package"
}
- Go to Settings → Go → Go Linter
- Select 'golangci-lint'
- Set "--fast" in "Arguments"
Add to your config:
let g:go_metalinter_command = "golangci-lint"
let g:go_metalinter_autosave = 1
-
Linter is too slow
# Use fast mode golangci-lint run --fast # Run only on changed files golangci-lint run --new-from-rev=HEAD~1
-
Memory issues
# Limit memory usage GOGC=50 golangci-lint run
-
Cache issues
# Clear cache golangci-lint cache clean
To modify linter settings, edit the .golangci.yml
file in the project root.