How I Build and Ship a Client Project
After working with multiple clients across different industries, I’ve settled into a process that keeps projects on track without unnecessary overhead. Here’s how a typical project goes from first conversation to production.
Discovery First
Every project starts with a discovery phase. Before writing any code, I need to understand what we’re actually building and why.
This usually takes a few calls and some async back-and-forth:
- What problem are we solving?
- Who are the users?
- What does success look like?
- Are there existing systems this needs to integrate with?
- What’s the timeline and budget?
I document everything in a simple project brief — not a 50-page spec, just enough to align on scope. This saves hours of rework later. The biggest source of project failure isn’t bad code — it’s building the wrong thing.
Scoping and Proposal
Once I understand the project, I break it down into phases. Each phase has a clear deliverable. This makes it easier for the client to see progress and for me to manage expectations.
A typical breakdown:
- Phase 1 — Core functionality, MVP
- Phase 2 — Integrations, admin panel, polish
- Phase 3 — Testing, deployment, handoff
I quote per phase, not per hour. Hourly billing incentivizes slow work. Per-phase billing incentivizes shipping. I wrote more about this in why I use fixed pricing.
The proposal includes what’s in scope, what’s out of scope, timeline per phase, and payment terms. Clear scope prevents most disagreements.
Tech Stack Decisions
I pick the stack based on the project, not personal preference. That said, I have defaults that work for most cases:
- Backend — Node.js with TypeScript
- Database — PostgreSQL
- ORM — MikroORM
- Frontend — depends on the project (React, Astro, or plain HTML)
- Hosting — VPS with Docker, Caddy as reverse proxy
- CI/CD — Drone CI with git tags, Watchtower for auto-deploy
For most client projects, this stack is fast to build with, easy to maintain, and cheap to host. I avoid overcomplicating things — if the project doesn’t need microservices, it doesn’t get microservices.
Development
I work in short cycles. Typically:
- Build a feature
- Push to a staging environment
- Get feedback from the client
- Iterate
Clients see progress early and often. This catches misunderstandings before they become expensive. A staging link they can click on is worth more than any number of screenshots or Figma prototypes.
I use git tags for versioning. Every deployment maps to a version. If something breaks, rolling back is straightforward.
Communication During Development
I send regular updates — usually end-of-day or end-of-week summaries depending on the project pace. Each update covers:
- What was done
- What’s next
- Any blockers or decisions needed
This keeps the client informed without requiring constant calls. Most communication is async — a well-written message is almost always better than a meeting.
When calls are needed, I keep them short and focused. No status meetings for the sake of meetings.
Deployment
My deployment setup is lightweight:
- Push a git tag
- Drone CI builds the Docker image and pushes to the registry
- Watchtower detects the new image and restarts the container
- Caddy handles HTTPS and reverse proxy
The whole pipeline runs on a single VPS. No Kubernetes, no managed CI bills. For most client projects, this is more than enough.
I set up monitoring and make sure the client knows how to reach me if something goes down.
Handoff
When the project wraps up, the client gets:
- Access to the repository
- Documentation on how to deploy and maintain
- A walkthrough of the codebase
- Any credentials and access needed to manage the application
I write the code as if someone else will maintain it. Clear naming, simple structure, no clever tricks. The goal is that any competent developer can pick it up.
After Launch
I typically offer a support period after launch — usually a month. Bugs get fixed, small adjustments get made. This builds trust and often leads to follow-up work.
Most of my clients come back for additional features or new projects. Delivering well the first time is the best marketing.
If you have a project in mind, I’m available for freelance work. Get in touch — I typically respond within 24 hours.
Related
Building a Full-Stack App: From Idea to Production
A walkthrough of how I build a full-stack application — from architecture decisions to deployment, using TypeScript, PostgreSQL, and Docker.
Word Embeddings, Cross-Lingual Alignment, and Building CLEU
How word embeddings work, what cross-lingual alignment means, and why I built a tool to explore them with FAISS.
Git Tags, Drone CI, and Watchtower — A Simple Deployment Pipeline
How I moved from Jenkins to Drone CI with git tags and Watchtower for a lightweight, reliable deployment pipeline.