Computer Science Student | Aspiring DevOps Engineer
"Hey, we need a Dev Lead for the new integrated project. Why not vote for PSST?" (actually happened)
Me (inner mind): "Sure! Sounds fun."
Opens project brief
Smart City Hub. Healthcare. Transportation. Emergency services. Events. Waste management. Finance. Real-time data. Multiple teams. Polyrepo architecture.
Me: "...what have I done?"
This isn't a solo project. This isn't even a small team project. This is:
No pressure.
Before teams start building, I need to decide: what are we building with?
Why this?
// This is what sold me
import { OpenAPIHono } from "@hono/zod-openapi";
const app = new OpenAPIHono();
// Routes + validation + docs in one place
app.openapi(createUserRoute, async (c) => {
const body = c.req.valid("json"); // Already validated!
return c.json({ user }, 201);
});Type-safe. Auto-documented. Fast. Perfect for teams who need to move quickly without breaking things.
The database team is setting up PostgreSQL. Probably PostGIS for maps. Going to be a lot of tables.
Prisma makes sense here:
model users {
id Int @id @default(autoincrement())
email String @unique
// maybe even these
profiles user_profiles?
wallets wallets[]
events events[]
}One schema file. Everyone knows the structure. TypeScript types auto-generated. No SQL string typos.
We're going polyrepo, not monorepo. Each major component is its own repo under https://github.com/CSC290-2025/
CSC290-2025/
├── backend/ # Hono API
├── frontend/ # React app
└── docs/ # Shared documentation
Why polyrepo? Different deployment cycles. Different teams. Less merge hell. But we'll need strong contracts between repos (that's where OpenAPI comes in).
Every endpoint starts with an OpenAPI definition:
// just an example, I actually used a helper
// to make it easy for others
const createUserRoute = createRoute({
method: "post",
path: "/users",
request: {
body: {
content: {
"application/json": {
schema: z.object({
name: z.string().min(2),
email: z.string().email(),
}),
},
},
},
},
responses: {
201: { description: "User created" },
},
});Frontend team can see this at /swagger. No guessing what fields to send.
All API responses follow the same structure:
// Success
{
"success": true,
"data": { /* actual data */ },
"message": "Operation successful",
"timestamp": "2025-01-15T10:00:00Z"
}
// Error
{
"success": false,
"error": {
"name": "ValidationError",
"message": "Email is required",
"statusCode": 400
},
"timestamp": "2025-01-15T10:00:00Z"
}Frontend never has to guess the response shape.
Frontend, backend, shared types. No JavaScript. TypeScript catches bugs before they reach production.
Faster than npm. More efficient than yarn. Done.
Frontend needs to know when backend API changes. Solution: OpenAPI spec in a shared location. Frontend generates types from it. (Or maybe just check the backend swagger)
Some teams will move fast. Others slower. Need to make sure fast teams don't block slow teams.
When we finally connect all the repos... that's when the fun begins. Planning integration milestones early.
Can't review every PR. Need automated checks:
Day 1-2: Backend repo setup
Day 3-4: Documentation
Spring [0]: First integration
But also:
Yes, it's overwhelming. But also? It's exciting.
We're building a Smart City platform. Real features. Multiple domains integrated together.
And I get to shape how it's built from day one.
Next post: Setting up the backend repo from scratch, choosing the module structure, and getting that first endpoint working.
Wish me luck. I'll need it.
Follow along as I document this journey. Next up: Backend architecture decisions and the first working API.