Development Workflow¶
This document describes the development workflow for the Open Codelabs project.
Git Workflow¶
Branch Strategy¶
Creating branches¶
# Get the latest code from main
git checkout main
git pull upstream main
# Create a feature branch
git checkout -b feat/add-feedback-export
# Bug fix branch
git checkout -b fix/websocket-reconnect
Development Process¶
1. Create or Select an Issue¶
On GitHub Issues: - Create a new Issue or - Select an existing Issue - Set yourself as the Assignee
2. Local Development¶
# Backend development
cd backend
cargo watch -x run
# Frontend development (in a separate terminal)
cd frontend
bun run dev
# Test in browser
open http://localhost:5173
3. Writing Code¶
Backend (Rust)¶
// src/handlers/new_feature.rs
use axum::{Json, extract::State};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct NewFeatureRequest {
pub data: String,
}
#[derive(Debug, Serialize)]
pub struct NewFeatureResponse {
pub result: String,
}
pub async fn new_feature_handler(
State(state): State<Arc<AppState>>,
Json(payload): Json<NewFeatureRequest>,
) -> Result<Json<NewFeatureResponse>, StatusCode> {
// Implementation
Ok(Json(NewFeatureResponse {
result: "success".to_string(),
}))
}
Add routes (main.rs):
Frontend (Svelte)¶
<!-- src/routes/new-feature/+page.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import { newFeatureApi } from '$lib/api';
let data = '';
let result = '';
async function handleSubmit() {
try {
result = await newFeatureApi(data);
} catch (err) {
console.error('Failed:', err);
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<input bind:value={data} placeholder="Enter data" />
<button type="submit">Submit</button>
</form>
{#if result}
<p>Result: {result}</p>
{/if}
API Client (lib/api.ts):
export async function newFeatureApi(data: string): Promise<string> {
const res = await fetch(`${API_URL}/new-feature`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data })
});
if (!res.ok) {
throw new Error('API call failed');
}
const json = await res.json();
return json.result;
}
4. Testing¶
Backend Testing¶
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_new_feature() {
let request = NewFeatureRequest {
data: "test".to_string(),
};
let result = new_feature_handler(request).await;
assert!(result.is_ok());
}
}
Execution:
Frontend Testing¶
// src/routes/new-feature/+page.test.ts
import { describe, it, expect } from 'bun:test';
import { render } from '@testing-library/svelte';
import NewFeature from './+page.svelte';
describe('NewFeature', () => {
it('renders form', () => {
const { getByPlaceholderText } = render(NewFeature);
expect(getByPlaceholderText('Enter data')).toBeTruthy();
});
});
Execution:
5. Code Quality Check¶
# Backend
cd backend
cargo fmt --check
cargo clippy
cargo test
# Frontend
cd frontend
bun run check
bunx prettier --check src
Commit Rules¶
Commit Message Format¶
Examples¶
feat(backend): add feedback export API
Implement GET /api/codelabs/:id/feedback/export endpoint
that exports feedback data as CSV
Closes #123
---
fix(frontend): fix WebSocket reconnection
WebSocket now properly reconnects on connection loss
with exponential backoff
Fixes #124
---
docs: update installation guide
Add M1 Mac specific instructions
---
refactor(backend): simplify help request handler
Extract common logic into helper function
No functional changes
Commit Checklist¶
- [ ] Contains only one logical change
- [ ] Clear commit message
- [ ] Tests passed
- [ ] Code formatted
Pull Requests¶
Creating a PR¶
PR Title¶
Same format as commit message:
PR Description¶
Template:
## Changes
- Added feedback export API
- Download available in CSV format
## Motivation
Administrators need to export feedback data to Excel for analysis.
## Testing
- [x] Unit tests added
- [x] Manual testing complete
- [x] Tested with 100 feedback data items
## Screenshots

## Checklist
- [x] Code formatting complete
- [x] Lint passed
- [x] Tests passed
- [x] Documentation updated
- [x] Self-review complete
Closes #123
PR Review Process¶
- CI Check: Automated build and tests pass
- Code Review: Maintainer reviews
- Feedback Reflecting: Request issues fixed
- Approval: Approved
- Merge: Squash and Merge
Reflecting Review Feedback¶
# Commit fixes
git add .
git commit -m "fix: address review comments"
git push
# Or modify existing commit
git add .
git commit --amend --no-edit
git push --force-with-lease
Release Process¶
Version Management¶
Use Calendar Versioning:
YYYY.M.D- e.g.,
2026.3.7
Preparing a Release¶
# Update and sync the shared version
node ./scripts/set-version.mjs 2026.3.7
# Create tag
git tag -a v2026.3.7 -m "Release version 2026.3.7"
git push origin v2026.3.7
GitHub Release¶
- Click "Releases" on GitHub.
- Click "Draft a new release".
- Select tag:
v2026.3.7. - Write release notes:
## What's New in v2026.3.7
### Features
- Add feedback export API (#123)
- Add dark mode support (#125)
### Bug Fixes
- Fix WebSocket reconnection (#124)
- Fix image upload on mobile (#126)
### Improvements
- Improve performance of Step rendering
- Update dependencies
## Breaking Changes
None
## Upgrade Guide
Just pull the latest image:
\`\`\`bash
docker compose pull
docker compose up -d
\`\`\`
CI/CD (Future)¶
GitHub Actions¶
.github/workflows/ci.yml:
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Check
run: cargo check
- name: Test
run: cargo test
- name: Clippy
run: cargo clippy -- -D warnings
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- name: Install
run: bun install
- name: Check
run: bun run check
- name: Test
run: bun test
Tips¶
Efficient Development¶
# Multi-panel with Tmux
tmux new -s dev
# Ctrl-B % (vertical split)
# Ctrl-B " (horizontal split)
# Panel 1: Backend
cargo watch -x run
# Panel 2: Frontend
bun run dev
# Panel 3: Terminal
Hot Reload¶
Backend:
Frontend handles this by default (Vite HMR).
Debugging¶
Backend:
// Add logs
tracing::debug!("Processing request: {:?}", request);
tracing::info!("Created codelab: {}", codelab.id);
tracing::warn!("Slow query: {}ms", duration);
tracing::error!("Failed to save: {}", err);
Frontend:
Next Steps¶
- Contribution Guide - How to contribute
- Code Style - Coding rules
- Local Development - Development environment