Write consistent, maintainable code that follows Akash Network standards.
This guide covers coding conventions for Go (node, provider) and JavaScript/TypeScript (console, docs) projects.
General Principles
Code Quality
- Readability first - Code is read more than written
- Simple over clever - Prefer clarity over cleverness
- Consistent style - Follow existing patterns
- Test your code - Write tests for new functionality
- Document complexity - Add comments for non-obvious logic
###YAGNI Principle
“You Aren’t Gonna Need It” - Don’t add functionality until it’s needed.
- Build what’s required now
- Avoid over-engineering
- Justify features with real use cases
- Defend proposals with data, not hypotheses
Go Conventions
Code Style
Follow the official Go Code Review Comments.
Formatting
# Use gofmt (required)gofmt -w .
# Or goimports (preferred - also manages imports)goimports -w .
# Project lintingmake lintProject Structure
cmd/ # Main applicationspkg/ # Library codeinternal/ # Private application codeapi/ # API definitionstestutil/ # Test utilitiesintegration/ # Integration testsNaming Conventions
Package Names
// **Good: Short, lowercase, no underscorespackage bidenginepackage deploymentpackage manifest
// **Bad: Mixed case, underscorespackage bidEnginepackage bid_engineVariables
// **Good: CamelCase for exported, camelCase for unexportedtype DeploymentManager struct { MaxBids int bidders []Bidder}
// **Bad: Inconsistent casingtype deploymentManager struct { max_bids int}Functions
// **Good: Descriptive, action-orientedfunc CreateDeployment(ctx context.Context, sdl []byte) (*Deployment, error)func ValidateManifest(m *Manifest) errorfunc (d *Deployment) Close() error
// **Bad: Vague or inconsistentfunc DoStuff() errorfunc deployment_create() errorError Handling
// **Good: Return errors, don't panicfunc ProcessBid(bid *Bid) (*Lease, error) { if bid == nil { return nil, errors.New("bid cannot be nil") }
lease, err := createLease(bid) if err != nil { return nil, fmt.Errorf("failed to create lease: %w", err) }
return lease, nil}
// **Good: Wrap errors with contextif err != nil { return fmt.Errorf("processing bid %s: %w", bid.ID, err)}
// **Bad: Ignoring errorslease, _ := createLease(bid)
// **Bad: Panicking in library codeif err != nil { panic(err)}Interfaces
// **Good: Small, focused interfacestype Bidder interface { PlaceBid(ctx context.Context, order Order) (*Bid, error)}
type LeaseManager interface { CreateLease(ctx context.Context, bid *Bid) (*Lease, error) CloseLease(ctx context.Context, leaseID LeaseID) error}
// **Good: Accept interfaces, return structsfunc ProcessBids(ctx context.Context, bidder Bidder, orders []Order) ([]*Bid, error)
// **Bad: Large interfaces with many methodstype Everything interface { DoBid() DoLease() DoManifest() DoEverything()}Testing
// **Good: Table-driven testsfunc TestCreateDeployment(t *testing.T) { tests := []struct { name string sdl []byte want *Deployment wantErr bool }{ { name: "valid SDL", sdl: []byte("version: '2.0'..."), want: &Deployment{...}, wantErr: false, }, { name: "invalid SDL", sdl: []byte("invalid"), want: nil, wantErr: true, }, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := CreateDeployment(context.Background(), tt.sdl) if (err != nil) != tt.wantErr { t.Errorf("CreateDeployment() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("CreateDeployment() = %v, want %v", got, tt.want) } }) }}Goroutine Leak Detection
// **Good: Use goleak to detect goroutine leaksimport "go.uber.org/goleak"
func TestMyFunction(t *testing.T) { defer goleak.VerifyNoLeaks(t)
// Your test code that uses goroutines}Comments
// **Good: Package documentation// Package bidengine provides bid placement and management for Akash providers.// It handles bid evaluation, placement, and lease creation.package bidengine
// **Good: Exported function documentation// CreateDeployment creates a new deployment from the provided SDL.// It validates the SDL, creates the deployment transaction, and returns// the deployment ID.//// Returns an error if the SDL is invalid or the transaction fails.func CreateDeployment(ctx context.Context, sdl []byte) (*Deployment, error)
// **Good: Explain complex logic// Calculate bid price using the formula:// price = (cpu * cpuPrice) + (memory * memPrice) + (storage * storagePrice)// Prices are in uakt per unit per block.price := calculateBidPrice(resources)
// **Bad: Obvious comments// Increment counter by 1counter++Context Usage
// **Good: Pass context as first parameterfunc CreateDeployment(ctx context.Context, sdl []byte) (*Deployment, error)
// **Good: Check context cancellationselect {case <-ctx.Done(): return ctx.Err()case result := <-ch: return result, nil}
// **Bad: Store context in structtype Manager struct { ctx context.Context // Don't do this}JavaScript/TypeScript Conventions
Code Style
Follow Airbnb JavaScript Style Guide and TypeScript best practices.
Formatting
# Use Prettier (required)npm run format
# Or Prettier with eslintnpm run lint:fixNaming Conventions
// **Good: PascalCase for components and classesexport const DeploymentCard: React.FC<Props> = ({ deployment }) => { }export class DeploymentManager { }
// **Good: camelCase for variables and functionsconst deploymentId = "123"function createDeployment(sdl: string) { }
// **Good: UPPER_SNAKE_CASE for constantsconst MAX_BID_AMOUNT = 1000const API_BASE_URL = "https://api.akash.network"
// **Bad: Inconsistent casingconst DeploymentId = "123" // Should be camelCasefunction CreateDeployment() { } // Should be camelCaseTypeScript Best Practices
// **Good: Explicit types for function parameters and returnsfunction createDeployment(sdl: string, deposit: number): Promise<Deployment> { return api.post<Deployment>("/deployments", { sdl, deposit })}
// **Good: Use interfaces for object shapesinterface Deployment { dseq: string owner: string state: DeploymentState createdAt: Date}
// **Good: Use enums for fixed valuesenum DeploymentState { Active = "active", Closed = "closed", Paused = "paused"}
// **Bad: Using `any`function processDeployment(data: any) { // Avoid `any` return data.something}
// **Better: Use proper types or `unknown`function processDeployment(data: unknown) { if (isDeployment(data)) { return data.dseq }}React Component Conventions
// **Good: Functional components with TypeScriptinterface DeploymentCardProps { deployment: Deployment onClose: (id: string) => void}
export const DeploymentCard: React.FC<DeploymentCardProps> = ({ deployment, onClose}) => { const handleClose = () => { onClose(deployment.dseq) }
return ( <div className="deployment-card"> <h3>{deployment.dseq}</h3> <button onClick={handleClose}>Close</button> </div> )}
// **Good: Custom hooks for reusable logicfunction useDeployments() { const [deployments, setDeployments] = useState<Deployment[]>([]) const [loading, setLoading] = useState(true)
useEffect(() => { fetchDeployments().then(setDeployments).finally(() => setLoading(false)) }, [])
return { deployments, loading }}
// **Bad: Prop drilling (use Context or state management)<ComponentA deployment={deployment}> <ComponentB deployment={deployment}> <ComponentC deployment={deployment} /> </ComponentB></ComponentA>Error Handling
// **Good: Try-catch with proper error handlingasync function createDeployment(sdl: string): Promise<Deployment> { try { const response = await api.post<Deployment>("/deployments", { sdl }) return response.data } catch (error) { if (axios.isAxiosError(error)) { throw new Error(`Failed to create deployment: ${error.message}`) } throw error }}
// **Good: Error boundaries for Reactclass ErrorBoundary extends React.Component<Props, State> { static getDerivedStateFromError(error: Error) { return { hasError: true, error } }
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { console.error("Error caught by boundary:", error, errorInfo) }
render() { if (this.state.hasError) { return <ErrorDisplay error={this.state.error} /> } return this.props.children }}Async/Await
// **Good: Use async/awaitasync function fetchDeployments(): Promise<Deployment[]> { const response = await api.get<Deployment[]>("/deployments") return response.data}
// **Good: Handle errorsasync function fetchDeployments(): Promise<Deployment[]> { try { const response = await api.get<Deployment[]>("/deployments") return response.data } catch (error) { console.error("Failed to fetch deployments:", error) return [] }}
// **Bad: Unhandled promise rejectionsfunction fetchDeployments() { return api.get("/deployments") // What if this fails?}Documentation Conventions
Markdown Style
<!-- **Good: Clear headings --># Main Title## Section### Subsection
<!-- **Good: Code blocks with language -->```typescriptconst deployment = await createDeployment(sdl)Use the provider-services CLI to create a deployment.
code here### Writing Style
- **Use active voice** - "The provider accepts the bid" not "The bid is accepted"- **Be concise** - Remove unnecessary words- **Use examples** - Show, don't just tell- **Link related content** - Help users navigate- **Update screenshots** - Keep visuals current
---
## Git Conventions
### Commit Messages
Follow [Conventional Commits](https://www.conventionalcommits.org/):
```bash# Format<type>: <subject>
<body>
<footer>Types:
feat:- New featurefix:- Bug fixdocs:- Documentationchore:- Maintenancetest:- Testsrefactor:- Code refactoringstyle:- Formattingperf:- Performance
Examples:
# Simple fixfix: resolve escrow calculation error
# Feature with descriptionfeat: add GPU resource filtering
Providers can now filter available GPUs by model and memory.This improves bid accuracy for GPU deployments.
Fixes #123
# Breaking changefeat!: change deployment state enum values
BREAKING CHANGE: Deployment states are now lowercase stringsinstead of integers. Update client code accordingly.Branch Names
# **Good: Descriptive branch namesfeature/gpu-bid-filteringfix/escrow-calculation-errordocs/update-cli-guidechore/bump-dependencies
# **Bad: Vague namesmy-changesfix-stuffupdateCode Review Checklist
Before Submitting PR
- Code follows project conventions
- All tests pass (
make testornpm test) - Linter passes (
make lintornpm run lint) - Added tests for new functionality
- Updated documentation if needed
- Commit messages follow convention
- Commits are signed-off (
git commit -s)
During Code Review
As Author:
- Respond to all comments
- Ask for clarification if needed
- Make requested changes promptly
- Learn from feedback
As Reviewer:
- Be constructive and respectful
- Explain the “why” behind suggestions
- Praise good code
- Focus on the code, not the person
Additional Resources
Go
JavaScript/TypeScript
General
Next Steps
- Pull Request Process - Submit your changes
- Getting Started - Make your first contribution
Questions? Ask in Discord #developers!