Skip to main content

Open Source Languages โ€” Software Development Blueprint

A complete development guide from kickoff to deployment Team size: 6 ยท Timeline: 6 weeks ยท Course: COMS3011A


Table of Contentsโ€‹

  1. Project Architecture
  2. Software Development Lifecycle
  3. Development Workflow
  4. Database-First Approach
  5. Backend Development Pipeline
  6. API Design
  7. Frontend Development Pipeline
  8. Full Request Lifecycle
  9. Authentication Flow
  10. Testing Strategy
  11. Git Workflow
  12. CI/CD Pipeline
  13. Project Folder Structure
  14. Coding Standards
  15. Team Workflow
  16. Security Considerations
  17. Performance Optimization
  18. Deployment
  19. Maintenance
  20. Project Timeline

1. Project Architectureโ€‹

Purposeโ€‹

Establish, before anyone writes a feature, exactly which process talks to which โ€” so six people can build in parallel without stepping on each other.

The shape of the systemโ€‹

This project is a microservices-oriented backend behind a hand-written API, plus a fully separate frontend and a static docs site โ€” not one monolithic app doing everything. That split isn't a style choice; it's a project requirement (non-monolithic frontend/backend, hand-written API), and the microservices split within the backend is what lets multiple people own distinct domains (content, moderation, assessments, conversation-spaces) without one giant Express app becoming a bottleneck.

Why this split mattersโ€‹

  • The frontend never talks to the database directly. It only ever calls the Express API.
  • The backend never renders HTML. It only ever returns JSON.
  • Within the backend, each domain (content, moderation, assessments, conversation-spaces) is built as its own service module with a clear boundary โ€” this is the microservices principle applied at whatever granularity a 6-week project can realistically support. Modules can be split into fully separate deployables later without a rewrite, because they don't share internal state.
  • This means the frontend, backend gateway, and each service module can be developed and tested independently โ€” which is exactly what lets 6 people work in parallel without a merge-conflict nightmare in one file tree.

Component interaction diagramโ€‹

Exit criteria before moving to Phase 2โ€‹

  • Everyone agrees the frontend, backend gateway, and each service module are independent units
  • Everyone can name which module they own
  • The diagrams above are understood by the whole team, not just the lead

2. Software Development Lifecycleโ€‹

Given a 6-week timeline, phases overlap rather than running strictly sequentially โ€” but each phase has a clear owner and a clear exit gate.

PhasePurposePrimary owner(s)Depends onExit checklist
Requirement analysisTurn the brief into a feature listWhole teamโ€”Feature list agreed, tiers (Basic/Intermediate/Advanced) mapped to weeks
PlanningAssign ownership, set conventionsLeadRequirementsOwnership table filled, API convention doc written
System designArchitecture + module boundariesLeadPlanningDiagrams in Section 1 approved
Database designSchema, ERD, migrationsLead + backend teamSystem designSchema merged, migrations run cleanly
API designEndpoint contractsBackend teamDatabase designEndpoint list with request/response shapes documented
Backend developmentImplement service modulesBackend team (3)API designAll Basic-tier endpoints pass integration tests
Frontend developmentImplement UI against real APIFrontend team (2-3)Backend endpoints exist (even stubbed)All Basic-tier screens functional
TestingUnit/integration/E2E coverageWhole teamFeatures existCI green on main
DeploymentShip to Cloudflare Pages + RenderLead + CI ownerTestingApp reachable at public URL
MonitoringConfirm it stays upCI ownerDeploymentHealth check + basic logging in place
MaintenanceBug fixes, small iterationWhole teamDeploymentOngoing until submission
Common mistake

Treating "requirement analysis" and "planning" as a full week of meetings. Timebox this to 2-3 days โ€” the brief is already detailed; the job is translating it into your ownership table and schema, not re-deriving it from scratch.


3. Development Workflowโ€‹

Every developer, on every task, follows the same loop:

Best practicesโ€‹

  • No one pushes directly to develop or main โ€” everything goes through a PR, even for a one-line fix.
  • A PR that touches schema.prisma gets reviewed by the lead specifically, not just any teammate (see Section 11).
  • Keep PRs small โ€” one feature or one bug, not a week of accumulated work. Small PRs get reviewed faster and break less.
  • Husky runs ESLint + Prettier on every commit locally, so style and obvious lint issues never even reach a PR.
Common mistakes
  • Merging your own PR without review "because it's just a small fix" โ€” this is how silent breakage creeps into develop.
  • Writing tests after the PR is already up, as an afterthought to satisfy CI โ€” write them alongside the feature so they actually catch bugs.

4. Database-First Approachโ€‹

From requirements to entitiesโ€‹

Read the brief's feature list and pull out every noun that persists: course, lesson, user, suggestion, flag, moderator action, assessment, result, fork, conversation space, session. Each becomes a table. Verbs between them (authors, forks, targets, resolves) become foreign keys or join tables.

Entity Relationship Diagramโ€‹

Normalization notesโ€‹

  • flags.target_id is polymorphic (a flag can point at a course, a lesson, or a conversation space) โ€” pair it with target_type rather than a strict FK, and enforce validity in application code, not the DB constraint.
  • courses.forked_from_id self-references courses โ€” this single column is the entire fork mechanism. Lineage tracing (Advanced tier) means walking this chain recursively.

Migration strategyโ€‹

  • Database is hosted on Supabase (managed PostgreSQL); the app only ever reaches it through Prisma, never through Supabase's auto-generated data API.
  • One migration per schema change, committed alongside the code that needs it โ€” never hand-edit the database directly, even in development.
  • The lead is the only person who merges changes to the shared schema file; everyone else proposes additions via PR (see Section 11).
  • Seed a small fixture dataset (a handful of users, courses, lessons) so every developer has consistent local data to test against.
Exit criteria
  • ERD reviewed and agreed by the whole team
  • Migrations run cleanly from empty on every teammate's machine
  • Seed script produces usable local test data

5. Backend Development Pipelineโ€‹

Build bottom-up โ€” nothing above a layer should exist before the layer below it is real. Each service module (content, moderation, assessments, conversation-spaces) follows this same pipeline independently.

Why this orderโ€‹

  • Repositories before services: a service that queries the DB directly is untestable without a real database. A repository layer lets you mock data access in service tests.
  • Services before controllers: business logic (e.g. "can this user accept this suggestion") should not live inside a controller โ€” controllers only translate HTTP in and out.
  • Validation and auth as middleware, not inline checks: keeps every route consistent and means you write the check once, not per-endpoint. Validation is handled by Zod schemas shared across a module; auth is verified once via Supabase Auth's JWT.

Example folder structure (backend)โ€‹

backend/
src/
config/ # env loading, constants
db/ # Prisma client, migrations
models/ # Prisma schema
services/ # domain service modules (content, moderation, assessments, convo)
content/
repositories/
services/
controllers/
routes/
moderation/
...
middleware/ # auth (Supabase), Zod validation, error handling
utils/
tests/
unit/
integration/
Dockerfile
Common mistakes
  • Putting SQL/Prisma calls directly in controllers "to save time" โ€” this is exactly what makes fork-merge and moderation logic (the two hardest features) unmanageable once they grow.
  • Skipping the repository layer for "simple" entities and then needing it later once a feature turns out non-trivial (forking always turns out non-trivial).
  • Letting service modules reach into each other's repositories directly instead of going through the gateway โ€” this quietly turns the microservices split back into a monolith.

6. API Designโ€‹

Principlesโ€‹

  • REST, resource-oriented: /courses, /courses/:id/lessons, /courses/:id/suggestions
  • Standard HTTP verbs: GET (read), POST (create), PATCH (partial update), DELETE (remove)
  • Consistent status codes: 200/201 success, 400 validation error, 401 unauthenticated, 403 unauthorized, 404 not found, 409 conflict, 500 server error
  • Every request body validated with Zod before it reaches a controller; a failed validation returns a consistent error shape
  • Consistent error shape across every endpoint:
{ "error": { "code": "VALIDATION_ERROR", "message": "title is required", "fields": ["title"] } }
  • Pagination via ?page=1&limit=20, filtering via query params (?language=xhosa&level=beginner), sorting via ?sort=-created_at
  • Versioning: prefix routes with /api/v1 from day one โ€” free to add now, painful to retrofit
  • Rate limiting: apply per-user limits scaled by reputation (ties into the trust engine from the moderation design)

Request flowโ€‹

Exit criteria
  • Every Basic-tier endpoint documented (method, path, request body, response shape, error cases) before implementation starts
  • Error shape and status-code conventions agreed by the whole team

7. Frontend Development Pipelineโ€‹

Build in this order โ€” each step needs the one before it to make sense:

  1. Initialize React + Vite + TypeScript โ€” scaffolding first
  2. Configure project โ€” env vars, API base URL, ESLint/Prettier, Husky hooks
  3. Folder structure โ€” routes, components, hooks (see Section 13)
  4. Layout โ€” shared shell (nav, footer) before any page content
  5. Routing โ€” page structure mapped to features (reader, workspace, mod queue), via a client-side router
  6. Authentication โ€” Supabase Auth session state available globally before building anything gated
  7. API layer โ€” a thin typed client wrapping fetch to the Express API, used by every page โ€” never call fetch ad hoc in components
  8. Reusable components โ€” buttons, form fields, cards, each with its own CSS Module โ€” before building pages that need them repeatedly
  9. State management โ€” React Context or a light library (e.g. Zustand) for auth/session state; server data via React Query/SWR rather than manual useEffect fetching
  10. Pages โ€” build screen by screen, against the real backend
  11. Forms + validation โ€” reuse the same Zod schemas as the backend where practical
  12. Loading and error states โ€” every data-fetching screen needs both, not just the happy path
  13. Caching โ€” via React Query's built-in cache, invalidated on mutation
  14. Optimization โ€” code splitting, image optimization, only once functionality is stable
  15. Testing โ€” Vitest + React Testing Library component tests as you go, E2E once flows are stable

Why this order mattersโ€‹

Building pages before you have a real API client means you either mock everything (and rebuild it all when the mock diverges from reality) or hardcode fetch calls per component (impossible to maintain auth/error handling consistently). The API layer is infrastructure, not a feature โ€” build it once, early.

Data flow diagramโ€‹


8. Full Request Lifecycleโ€‹

Example: a learner submits a correction to a lesson.


9. Authentication Flowโ€‹

Per the requirements: you use an established auth library โ€” Supabase Auth โ€” you do not write your own password hashing, token issuance, or session logic.

Required flows (all four are graded explicitly)
  • Sign up
  • Sign in
  • Password reset
  • Account deletion

Account deletion is the one teams forget โ€” build and test it in Week 1 alongside the others, not at the end.

Authorization on top of authenticationโ€‹

Authentication answers "who is this." Authorization (can this user moderate, can this user edit this course) is your own logic, sitting in the service layer, driven by reputation_score and ownership checks โ€” this part you do write yourselves, and it's a core project feature, not boilerplate.


10. Testing Strategyโ€‹

Test typeWhat it coversToolingWritten byWhen
UnitIndividual functions (services, utils)VitestFeature ownerAlongside the code
IntegrationAPI endpoint behavior against a real test DBVitest + SupertestFeature ownerOnce endpoint works
ComponentIndividual React components in isolationVitest + React Testing LibraryFrontend ownerAlongside the component
End-to-endFull user flows (sign up โ†’ create course โ†’ suggest edit)Vitest (or Playwright)Whole team, rotatingOnce a full flow exists
RegressionRe-run of the above on every PRCI (GitHub Actions)AutomaticallyEvery push

Why Vitest + Supertestโ€‹

Both frontend and backend share Vitest as the single test runner โ€” one config style, one set of commands, one thing for new contributors to learn. Supertest layers on top for the backend specifically, letting integration tests hit real Express routes in-process without booting an actual server per test, which keeps the suite fast enough to run on every commit via Husky and every PR via CI.

Coverage expectationsโ€‹

Aim for meaningful coverage on services and controllers (the logic that can actually break), not 100% coverage on trivial getters. A moderation-decision function with no tests is a bigger risk than an untested formatDate helper.

CI integrationโ€‹

Every PR runs lint โ†’ unit โ†’ integration tests automatically (see Section 12). A PR cannot merge if any of these fail.


11. Git Workflowโ€‹

Branch namingโ€‹

  • feature/short-description โ€” new functionality
  • bugfix/short-description โ€” non-urgent fix
  • hotfix/short-description โ€” urgent fix to main

Commit conventionsโ€‹

Use Conventional Commits: feat:, fix:, test:, docs:, chore: โ€” e.g. feat: add suggestion review endpoint. This makes the history readable, lets you generate a changelog for free, and maps cleanly onto Semantic Versioning (a feat: implies a MINOR bump, a fix: implies a PATCH, a breaking change implies MAJOR).

Versioning: SemVerโ€‹

Tags follow MAJOR.MINOR.PATCH:

  • MAJOR โ€” breaking API changes (rare during the project, reserved for real contract breaks)
  • MINOR โ€” new backwards-compatible features (e.g. a new endpoint or tier of functionality)
  • PATCH โ€” bug fixes, no behavior change to the contract

Tag milestones as they're reached, e.g. v0.1.0 at end of Basic tier, v0.2.0 at end of Intermediate tier โ€” this gives an honest, inspectable history of what worked at each stage.

Merge strategyโ€‹

  • Feature branches merge into develop via PR, squash-merged to keep history clean
  • develop merges into main at each milestone (end of Basic tier, end of Intermediate tier, final submission), tagged with the corresponding SemVer version
  • The lead is the required reviewer on any PR touching schema.prisma or shared middleware/config
Pull request checklist
  • Tests written and passing (Vitest / Supertest)
  • Husky pre-commit hooks passed (lint + format)
  • No direct pushes to develop or main
  • Description explains what changed and why

12. CI/CD Pipelineโ€‹

Example GitHub Actions workflow (backend)โ€‹

name: backend-ci
on:
pull_request:
paths: ["backend/**"]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: test
ports: ["5432:5432"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
working-directory: backend
- run: npm run lint
working-directory: backend
- run: npm run test
working-directory: backend
- name: Build Docker image
run: docker build -t backend:ci ./backend

Notesโ€‹

  • Run frontend and backend CI as separate jobs, triggered only by changes in their respective folders โ€” keeps CI fast and relevant.
  • Backend CI builds the Docker image as part of the pipeline, so a broken image is caught before it reaches Render.
  • Deployment triggers only on merge to main, never on every PR push.
  • Cloudflare Pages gives free preview deployments per PR out of the box for the frontend โ€” no extra CI config needed for that part.
  • Add a rollback step (redeploy the previous successful build) so a bad deploy doesn't take the app down for the rest of the team during a demo prep week.

13. Project Folder Structureโ€‹

open-source-languages/
frontend/
src/
routes/ # page-level components
components/
hooks/
lib/ # API client, utils
styles/ # global CSS + shared CSS Modules
tests/
vite.config.ts
vitest.config.ts
backend/
src/
config/
db/ # Prisma client, migrations
models/ # Prisma schema
services/ # microservice modules (content, moderation, assessments, convo)
middleware/ # Supabase Auth, Zod validation, error handling
utils/
tests/
Dockerfile
docs/ # Docusaurus site source
.github/
workflows/ # CI/CD definitions
scripts/ # one-off dev/seed scripts
.husky/ # git hooks

Each folder maps to a section of this document โ€” if you're unsure where a file goes, it should mirror the layer it belongs to in Section 5 or 7.


14. Coding Standardsโ€‹

AreaConvention
Fileskebab-case.ts, React components PascalCase.tsx, CSS Modules ComponentName.module.css
Variables/functionscamelCase
Types/interfacesPascalCase, prefer type for unions, interface for object shapes
Database tables/columnssnake_case, plural table names (courses, mod_actions)
API routesplural nouns, lowercase, hyphenated, versioned (/api/v1/courses/:id/suggestions)
React componentsone component per file, named exports preferred, styled via its own CSS Module
ValidationZod schemas colocated with the module they validate, shared between frontend and backend where practical
TypeScriptstrict: true in tsconfig.json from day one โ€” retrofitting strict mode later is far more painful
FormattingEnforced automatically by Prettier + ESLint, checked by Husky on commit and CI on push
Documentationevery service function gets a one-line JSDoc comment explaining intent, not restating the code

15. Team Workflowโ€‹

  • Sprint length: weekly, aligned to the tier boundaries in Section 20
  • Daily: a 10-minute async standup (Slack/Discord message: yesterday, today, blockers) โ€” six people, six weeks, don't spend real meeting time on this
  • Feature ownership: one primary owner per service module (Section 1's ownership table), but anyone can review any PR
  • Definition of Done: code merged to develop, tests passing in CI, Husky hooks clean, docs updated if the change affects setup or API contracts
  • Conflict resolution: schema and shared-config conflicts go to the lead; feature-level disagreements get a 15-minute sync call rather than a long thread

16. Security Considerationsโ€‹

  • Authentication: delegated entirely to Supabase Auth (Section 9) โ€” never store or hash passwords yourselves
  • Authorization: enforced server-side in the service layer on every mutating endpoint, never trusted from the frontend
  • Input validation: every request body validated via Zod before it reaches a controller
  • SQL injection: mitigated by using Prisma's parameterized queries exclusively โ€” never string-concatenate raw SQL
  • XSS: sanitize any user-generated content rendered as HTML (course/lesson content); React escapes by default, but be careful with any dangerouslySetInnerHTML
  • CSRF: use SameSite cookies or bearer tokens rather than cookie-based sessions without CSRF tokens
  • Secrets management: all keys/secrets in environment variables, never committed โ€” use .env.example with placeholder values in the repo, and configure real values in Render's/Cloudflare's secret manager
  • Rate limiting: apply at the API gateway layer, scaled by user reputation, to blunt abuse (ties into the trust-engine design)
  • Container hygiene: keep the backend's Docker base image minimal and patched, since it's what actually runs in production on Render
  • HTTPS: enforced by Cloudflare Pages and Render by default โ€” confirm it, don't assume it

17. Performance Optimizationโ€‹

LayerTechniques
DatabaseIndexes on foreign keys and frequently filtered columns (courses.language, courses.status)
BackendPagination on all list endpoints, avoid N+1 queries (use Prisma's include deliberately)
FrontendVite's automatic code splitting per route, optimized image assets
CachingReact Query cache on the frontend; Cloudflare's edge cache for static assets; consider a Redis cache only if a specific endpoint proves slow โ€” don't add it speculatively
Real-timeLimit conversation-space payload size, throttle presence updates rather than broadcasting on every keystroke

Optimize once something is measurably slow โ€” don't spend Week 2 hours on caching a feature that doesn't exist yet.


18. Deploymentโ€‹

Connect the GitHub repo, deploy the Vite build output on merge to main, and get automatic preview deployments on every PR for free. Set the API base URL as an environment variable so staging and production point at different backends.

  • Health checks: a /health endpoint returning 200 + basic status, checked by Render and by CI post-deploy
  • Logging: structured logs (e.g. via pino) shipped to Render's log viewer โ€” sufficient for a project this size, no need for a dedicated logging service
  • Rollback: redeploy the last known-good build via Render's or Cloudflare's dashboard if a deploy breaks something close to a demo

19. Maintenanceโ€‹

  • Bug fixes: triaged the same way user-submitted flags are โ€” a lightweight issue board, not a formal process
  • Dependency updates: run npm audit weekly, update non-breaking patches as you go rather than in one risky batch at the end
  • Database migrations: every schema change ships with a migration file, tested against the seed dataset before merging
  • Versioning: tag releases at each milestone using SemVer (v0.1.0 โ€” Basic tier, v0.2.0 โ€” Intermediate tier) so you can always point to "what worked" if a later change breaks something
  • Future enhancements: keep a running "Advanced tier โ€” deferred" list (fork conflict resolution depth, real sockpuppet detection, elected moderators) so your final documentation can honestly state what's implemented vs. designed-only

20. Project Timelineโ€‹

WeekFocusKey deliverables
1FoundationFrontend and backend wired together over HTTP, auth (signup/signin/reset/delete) working end-to-end via Supabase Auth, CI running lint+test, Docusaurus site live (even empty), schema + migrations merged, API convention doc agreed, Dockerfile building cleanly
2Basic tier (backend)Course CRUD, lessons, suggestions, flags endpoints across their service modules; conversation-space WebSocket spike started
3Basic tier (frontend) + integrationReader, workspace, profile UI against real backend; checkpoint: Basic tier demoable end-to-end, tagged v0.1.0
4Intermediate tier, part 1Assessments, forking, moderation workflow (queue, multi-mod decisions, appeals)
5Intermediate tier, part 2 + Advanced tier selectionReputation scoring, feeds/notifications, turn-managed conversation rooms; pick 2-3 Advanced features to actually build, document the rest; tag v0.2.0
6Advanced tier + hardeningChosen Advanced features complete; accessibility/responsiveness pass; docs site finished (architecture, setup, API reference); integration freeze in final 3-4 days; demo script rehearsed; final tag v1.0.0

Milestonesโ€‹

  • End of Week 1: foundation live, nothing user-facing yet, but every piece of infrastructure is real
  • End of Week 3: Basic tier fully working, deployed, demoable (v0.1.0)
  • End of Week 5: Intermediate tier fully working (v0.2.0)
  • End of Week 6: submission โ€” Advanced tier partially implemented and honestly documented, full test suite green in CI, docs site complete (v1.0.0)

This document is a living guide โ€” update it as decisions change, rather than letting the team's actual practice drift away from what's written here.