Thank you for your interest in contributing to stampchain.io! This guide will help you understand our development workflow, coding standards, and quality requirements.
- Fork and clone the repository
- Install dependencies:
deno install(if using external dependencies) - Install Git hooks:
./scripts/install-git-hooks.sh - Verify setup:
deno task validate
Our automated pre-commit hooks enforce several quality standards:
- Code formatting:
deno fmt --check - Linting:
deno lint --quiet - Type checking:
deno check main.ts - Import pattern validation:
deno task check:imports:ci
Install hooks with:
./scripts/install-git-hooks.shstampchain.io enforces strict import patterns to maintain code quality and architectural consistency.
// Domain-specific aliases (strongly preferred)
import type { StampData } from "$types/api.d.ts";
import { formatCurrency } from "$utils/formatUtils.ts";
import { StampService } from "$server/services/stampService.ts";
// External dependencies
import { serve } from "$fresh/server.ts";
import dayjs from "dayjs";// CRITICAL: Will fail CI
import type { StampData } from "$globals";
// WARNING: Discouraged
import { formatCurrency } from "../../lib/utils/formatUtils.ts";Important: The CI system will automatically reject pull requests with critical import pattern violations.
See docs/IMPORT_PATTERNS.md for complete guidelines.
# Development server
deno task dev
# Code quality checks
deno task validate # Format, check, and test
deno task validate:ci # CI-style validation (includes import patterns)
deno task check:imports # Import pattern validation only
# Testing
deno task test:unit # Unit tests
deno task test:integration # Integration tests
deno task test:api # API tests with Newman- Strict mode enabled: All code must pass
deno check - Explicit types: Prefer explicit type annotations
- No
anytypes: Use proper typing orunknown - Optional properties: Use exact syntax (
property?: Type)
Organize imports in this order:
- External dependencies (Deno standard library, npm packages)
- Framework imports (Fresh, Preact)
- Domain imports (Using $aliases)
- Relative imports (Same directory only)
Example:
// External dependencies
import { assertEquals } from "@std/assert";
import dayjs from "dayjs";
// Framework
import { Head } from "$fresh/runtime.ts";
import { PageProps } from "$fresh/server.ts";
// Domain imports
import type { StampData } from "$types/api.d.ts";
import { formatCurrency } from "$utils/formatUtils.ts";
import { StampService } from "$server/services/stampService.ts";
// Same directory
import { helper } from "./helper.ts";Follow the established directory structure:
├── client/ # Client-side utilities and hooks
├── components/ # Reusable UI components
├── islands/ # Interactive Preact components
├── lib/
│ ├── constants/ # Application constants
│ ├── types/ # TypeScript type definitions
│ └── utils/ # Utility functions (organized by domain)
├── routes/ # Fresh routes and API endpoints
├── server/ # Server-side logic
│ ├── controller/ # API controllers
│ ├── database/ # Database access layer
│ ├── services/ # Business logic services
│ └── types/ # Server-specific types
└── tests/ # Test files
stampchain.io uses a staging → production branch model. There are two long-lived branches, each mapped to an environment:
| Branch | Role | Default | Deploys to | Protection & merge method |
|---|---|---|---|---|
dev |
Integration / staging — where all work lands first | ✅ yes | Preview / staging (Deno preview deploys on PRs) | PR + linear history; no force-push or deletion. Feature PRs land via squash. |
main |
Production — the live site | no | Production — production-deploy.yml deploys on every push |
PR + 1 approving review; no force-push or deletion. Promotions land via a merge commit (the only method enabled on main). |
- Branch off
dev:git checkout dev && git pull && git checkout -b feature/description - Open your PR against
dev(the default base). CI and a preview deploy run on the PR. - Once it's approved and green, squash-merge into
dev.devkeeps a clean, linear history; your feature branch collapses to a single commit.
Production is released only by landing dev → main:
- Open a PR
dev→main, e.g.release: promote dev to production (YYYY-MM-DD). - Review the diff — it is exactly what will go live.
- Merge it — do NOT squash.
mainaccepts only the "Create a merge commit" method, so the promotion bringsdev's actual commits ontomain. The push tomaintriggersproduction-deploy.yml, which deploys to production and runs post-deploy validation.
Because promotions are merge commits (not squashes), main always stays an
ancestor of — i.e. fully contained in — dev. The two histories never diverge,
so there are no phantom conflicts and no manual main → dev reconciliation
after a release. (Squash promotions used to create a commit on main that did
not exist on dev, which forced a reconciliation merge after every release; that
is why main's ruleset no longer requires linear history and is pinned to
merge-commit-only.)
There are no version tags — this is continuous deployment. main always
reflects what is currently live; dev is everything staged for the next
promotion.
⚠️ mainis the production branch. Never push to it directly (the branch ruleset blocks it) — every production change goes through a revieweddev → mainpromotion PR, merged as a merge commit.
- Create feature branch:
git checkout -b feature/description - Make changes following coding standards
- Run validation:
deno task validate:ci - Commit with clear messages (see template in
.gitmessage) - Push and create PR
All pull requests must pass:
- ✅ Code formatting (
deno fmt --check) - ✅ Linting (
deno lint) - ✅ Type checking (
deno check) - ✅ Import pattern validation (no $globals imports)
- ✅ Unit tests (
deno task test:unit) - ✅ Integration tests (for API changes)
Use the conventional commit format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Examples:
feat(stamps): add new stamp validation endpoint
fix(api): resolve type error in SRC20 balance calculation
docs(import): update import pattern guidelines
refactor(types): migrate from $globals to domain imports
- Location:
tests/unit/ - Command:
deno task test:unit - Coverage: Aim for >80% coverage on new code
- Mocking: Use test doubles for external dependencies
- Location:
tests/integration/ - Command:
deno task test:integration - Database: Use test database with fixtures
- API: Test actual HTTP endpoints
- Tool: Newman (Postman CLI)
- Command:
deno task test:api - Coverage: All public API endpoints
- Environments: Development and staging
If CI fails with import pattern violations:
- Run local validation:
deno task check:imports - See detailed output: Check console for specific files and lines
- Fix violations: Replace $globals imports with domain aliases
- Verify fix:
deno task check:imports:ci
For TypeScript errors:
- Check types:
deno check main.ts - Update imports: Ensure proper type imports from
$types/ - Fix strict mode issues: Address any
undefinedoranytypes
If hooks prevent commits:
- Run validation locally:
deno task validate:ci - Fix all issues before attempting commit
- Emergency bypass:
git commit --no-verify(not recommended)
- Prefer aliases: Use
$utils/formatUtils.tsover relative paths - Avoid deep imports: Don't import from nested barrel exports
- Tree shaking: Explicit imports help bundling optimization
- Lazy loading: Use dynamic imports for large dependencies
- Preact optimization: Follow Fresh performance guidelines
- Database queries: Optimize with indexes and caching
stampchain.io follows domain-specific architecture:
- Types: Domain-specific type definitions in
lib/types/ - Services: Business logic separated by domain (stamps, SRC20, etc.)
- Controllers: Thin API layer delegating to services
- Components: Reusable UI components with clear interfaces
Client Request → Route → Controller → Service → Repository → Database
↓
Client Response ← API Response ← Business Logic ← Data Access
- API errors: Use structured error responses
- Client errors: Graceful degradation with user-friendly messages
- Logging: Comprehensive logging for debugging
- Import patterns: docs/IMPORT_PATTERNS.md
- API documentation: Generated Swagger/OpenAPI docs
- Architecture decisions: Check Git history and PR discussions
- Issues: GitHub Issues for bugs and feature requests
- Discussions: GitHub Discussions for questions
- Pull requests: Code review and feedback
# Check current setup
deno task validate
# Test import patterns
deno task check:imports
# Run specific test suites
deno task test:unit
deno task test:api
# Performance profiling
deno task monitor:localstampchain.io uses semantic versioning:
- Major: Breaking API changes
- Minor: New features, backward compatible
- Patch: Bug fixes, no API changes
- Development: Feature branches and PRs
- Staging: Integration testing on staging environment
- Production: Automated deployment after approval
Each deployment must pass:
- ✅ All automated tests
- ✅ Import pattern validation
- ✅ Performance benchmarks
- ✅ Security scans
- ✅ Manual QA approval
Thank you for contributing to stampchain.io! Your adherence to these guidelines helps maintain code quality and ensures a smooth development experience for everyone.