How I Built JobLink Philippines: A Solo Developer's Journey from Frustration to a Full-Stack Platform

Rey Mark Taparβ€’
How I Built JobLink Philippines: A Solo Developer's Journey from Frustration to a Full-Stack Platform

Building a full-stack web application from the ground up is one of the most challengingβ€”and rewardingβ€”things I have ever taken on. When that project is a two-sided hiring platform like JobLink Philippines, every decision carries real weight. Jobseekers and employers depend on clear communication, reliable notifications, and honest matchmaking.

Mabuhay!

I'm Rey Mark Tapar, the technical founder of JobLink Philippines. In this article, I want to share my personal journey as a solo developer: why I started, how I designed the architecture, how I built the background worker pipeline, the real mistakes I made along the way, how I made AI practical without blowing my budget, and how the entire platform currently runs for less than the price of a cup of coffee each month.


1. The Spark: Why I Decided to Build JobLink Philippines

Like many professionals in the Philippines, I have experienced the frustration of job hunting firsthand. You spend hours polishing your resume, fill out repetitive application forms, and hit submit β€” only for your application to disappear into a black hole. Weeks pass without a single update, leaving you wondering if anyone ever even looked at your profile.

On the other side of the table, hiring managers and small business owners face an equally exhausting challenge. They post a job opening and receive hundreds of unformatted resumes. Sifting through PDFs manually takes hours, and keeping track of interviews across scattered email threads, Viber messages, and spreadsheets quickly turns into chaos.

Most legacy job portals in the Philippines share the same underlying problems:

  1. Static, keyword-only filtering: Qualified candidates get screened out simply because their resume didn't use an exact synonym, while recruiters spend hours skimming files manually.
  2. Fragmented communication: Status updates, interview schedules, and candidate chats are spread across disconnected tools.
  3. Cluttered, sluggish interfaces: Many older portals feel heavy, filled with distracting ads, and difficult to navigate on a mobile data connection.

I asked myself a straightforward question: Can I build a cleaner, fairer, and more transparent recruitment platform tailored specifically for Filipino jobseekers and employers?

That question became the starting point for JobLink Philippines.


2. Setting My Priorities as a Solo Developer

When you are building alone β€” without a large engineering team or outside funding β€” you cannot afford to over-engineer things or waste time on unnecessary complexity. I set four clear guiding principles from Day 1:

  • Mobile-First Accessibility: In the Philippines, a large portion of web traffic comes from mobile phones. The platform had to load cleanly and feel natural on a phone screen, even on a modest mobile data connection.
  • Clarity Over Gimmicks: Features had to solve real everyday problems. Candidates needed clear application tracking (Applied β†’ Screening β†’ Interview β†’ Offer), and employers needed a simple pipeline to manage candidates.
  • Decoupled, Resilient Architecture: Heavy tasks β€” like parsing multi-page PDFs, sending batch email alerts, and handling employer outreach β€” needed to run in dedicated background queues rather than blocking web requests and risking timeouts.
  • Budget-Conscious Infrastructure: The infrastructure needed to scale gracefully without high fixed monthly server bills, especially in the early stages.

3. The Architecture: Why I Chose a Hybrid Model

Rather than forcing everything into one monolithic database or cramming all long-running tasks into serverless web functions, JobLink Philippines runs on a decoupled hybrid architecture across four dedicated layers:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    1. Web Core & UI Tier                    β”‚
β”‚            Next.js App Router (React 19 Server)             β”‚
β”‚        β€’ Server Components  β€’ Server Actions  β€’ REST API    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚                              β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β–Ό                   β–Ό          β–Ό                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 2A. Real-Time   β”‚ β”‚ 2B. SQL DB  β”‚ β”‚ 3. Worker Engineβ”‚ β”‚ 4. AI Engineβ”‚
β”‚ Firebase Engine β”‚ β”‚ Neon Postgresβ”‚ β”‚ Express + Redis β”‚ β”‚ Gemini/Groq β”‚
β”‚ β€’ Live Chat     β”‚ β”‚ β€’ Logs      β”‚ β”‚ β€’ BullMQ Queues β”‚ β”‚ β€’ Match AI  β”‚
β”‚ β€’ App Pipelines β”‚ β”‚ β€’ Analytics β”‚ β”‚ β€’ Auto-Retry    β”‚ β”‚ β€’ Roaster   β”‚
β”‚ β€’ Client Auth   β”‚ β”‚ β€’ DrizzleORMβ”‚ β”‚ β€’ Bull-Board UI β”‚ β”‚ β€’ Fallback  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Here is how each layer fits into the overall system:

1. The Web Core (Next.js & React 19)

The main user-facing application uses Next.js App Router with React 19. Public job listings are statically rendered for fast loading and search engine indexing. Candidate dashboards, employer Kanban boards, and admin moderation tools run on dynamic Server Components and Server Actions.

A PWA (Progressive Web App) manifest and service worker are registered in production, so users on mobile can optionally install the app to their home screen and receive push notifications even when the browser tab is closed.

The application is built with TypeScript throughout β€” from the frontend components down to the database schemas and the background worker service β€” which helped catch many bugs at compile time instead of in production.

2. The Real-Time Layer (Firebase Firestore & Auth)

Hiring is interactive. When an applicant submits a proposal, a recruiter sends a direct chat message, or an interview is scheduled, Firestore's real-time listeners update both parties instantly without manual page refreshes.

Firebase also handles authentication β€” including Google OAuth β€” through a set of rewrites in next.config.ts that proxy Firebase's auth endpoints through the same domain, keeping the experience consistent.

3. The Relational Analytics Layer (Neon Serverless Postgres + Drizzle ORM)

For structured business records, administrative audit logs, blog engagement metrics, outreach lead tracking, system notifications, and subscriber management, PostgreSQL provides type-safe relational schemas via Drizzle ORM.

Because Neon is serverless, the compute scales down to zero when idle, which keeps running costs minimal. The database also stores resume templates, donation records, and global announcement data β€” anything that benefits from structured queries and joins rather than document-based storage.


4. Asynchronous Processing: Express.js + Redis + BullMQ

Serverless web functions are great for instant HTTP responses, but they have hard timeout limits β€” typically between 10 and 60 seconds depending on the platform. When a user uploads a multi-page resume PDF, or when an employer publishes a job and email alerts need dispatching to hundreds of matching subscribers, running those tasks inside a web request risks function timeouts and a sluggish user interface.

To solve this, I built a dedicated Express.js microservice β€” hosted separately from the Next.js app on Render β€” backed by Redis (via Upstash) and BullMQ for reliable, asynchronous background job processing. This service is containerized using a Dockerfile and a render.yaml configuration file.

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚ Next.js Web Application β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚ Enqueues Job Payload
                                 β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚   Redis Cache / Store   β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚ Dispatches to Queue
                                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Express.js Background Worker (BullMQ)              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚   Resume Queue    β”‚     Subscriber Queue    β”‚   Outreach Queue  β”‚
β”‚ β€’ PDF Text Extractβ”‚ β€’ Job Alert Emails      β”‚ β€’ B2B Outreach    β”‚
β”‚ β€’ AI Entity Parse β”‚ β€’ Rate-Limited Batches  β”‚ β€’ Concurrency: 3  β”‚
β”‚ β€’ 10m Stuck Auto- β”‚ β€’ 10 emails/sec max     β”‚ β€’ Exp. Backoff    β”‚
β”‚   Reconciliation  β”‚ β€’ Concurrency: 5        β”‚ β€’ Dead-Letter     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                 β”‚
                                 β–Ό
                Webhook Updates back to Firestore & DB

The 3 Core BullMQ Queues in Production

1. resumeQueue β€” AI Resume Parsing & Auto-Recovery

When a jobseeker uploads a resume, the file is enqueued immediately. The worker:

  • Downloads the PDF from Vercel Blob storage (or accepts a base64-encoded buffer directly)
  • Extracts raw text using pdf-parse and pdfjs-dist
  • Sends the extracted text β€” capped at 8,000 characters to stay within token limits β€” to the AI provider with a structured JSON schema
  • Parses the AI response into typed fields: personal info, work experience, education, skills, projects, certifications, and languages
  • Assigns stable numeric IDs to each array entry so the frontend state matches expected shapes
  • Posts the result back to Next.js via a secure callback URL, which then writes the parsed data to Firestore

The queue is configured with 3 concurrent workers, exponential backoff retries (3 attempts), and a drainDelay of 30 seconds between polls when the queue is empty β€” which reduces unnecessary Redis command usage on the free tier.

A repeatable reconciliation job runs every 10 minutes inside the same queue. It calls the /api/cron/process-resume endpoint on the Next.js side to detect and recover any resumes that got stuck in a processing state β€” for example, if the worker crashed mid-job.

2. subscriberQueue β€” Smart Job Alert Emails

Whenever a new job is approved and published, this worker queries active subscribers whose search preferences match the position and dispatches individual email notifications. It runs with:

  • 5 concurrent workers
  • A rate limiter capped at 10 emails per second to stay within Resend's API safe limits
  • Exponential backoff retries and a 48-hour retention window for failed jobs

3. outreachQueue β€” Employer & B2B Outreach

Handles verified employer notifications and B2B communications. Configured with 3 concurrent workers, a 5-per-second rate limiter, and exponential backoff retries. Failed deliveries are retained in Redis for 24 hours and visible in the dashboard for manual review.

4. Real-Time Bull-Board Dashboard (/admin/queues)

To keep full visibility over background workers, I mounted @bull-board directly inside the Express service. It gives a real-time web dashboard showing active, waiting, completed, and failed jobs β€” with one-click manual retries for anything that needs attention.

Vercel Cron Jobs β€” The Scheduled Maintenance Layer

In addition to the BullMQ queues, the platform uses Vercel's built-in cron job runner for scheduled maintenance tasks that run on a fixed schedule rather than being triggered by user events. These are defined in vercel.json:

Cron PathSchedulePurpose
/api/cron/hard-deleteDaily at midnightPermanently remove soft-deleted records
/api/cron/remind-jobseekersMondays at 9amWeekly engagement reminders
/api/cron/recommended-jobs-digestWed & Fri at 9amPersonalized job digest emails
/api/cron/remind-onboardingDaily at 10amPrompt incomplete user profiles
/api/cron/subscriber-job-alertsTue & Thu at 9amTrigger subscriber alert batches
/api/cron/clean-notificationsDaily at 3amClean up stale notification records
/api/cron/send-candidate-portfolioDaily at 11amShare candidate profiles with employers
/api/cron/process-resumeDaily at 2amReconcile stuck resume parsing tasks
/api/cron/cleanup-otpsDaily at 4amRemove expired OTP codes

This two-layer approach β€” BullMQ for event-triggered jobs, Vercel Cron for time-scheduled maintenance β€” keeps each concern in the right place.


5. Making AI Practical: The Multi-Tier Fallback Engine

AI is often presented as a silver bullet, but in production it comes with real challenges: API rate limits, temporary provider outages, and unexpected latency spikes. If an employer is trying to draft a job posting or a candidate is analyzing their resume, an API issue shouldn't break the entire page.

To handle this, I built a cascading fallback engine that tries multiple providers and multiple model variants within each provider, in sequence:

[User Request]
      β”‚
      β–Ό
1. Primary: Google Gemini
   (gemini-3.6-flash β†’ gemini-3.5-flash β†’ gemini-3.1-flash-lite β†’ gemini-flash-latest)
      β”‚ (If rate limited or down)
      β–Ό
2. Secondary: Groq
   (llama-3.3-70b-versatile β†’ llama-3.1-8b-instant)
      β”‚ (If busy or throttled)
      β–Ό
3. Tertiary: SambaNova
   (Meta-Llama-3.3-70B-Instruct β†’ DeepSeek-R1-Distill-Llama-70B)
      β”‚ (Final safety net)
      β–Ό
4. Quaternary: OpenRouter
   (llama-3.3-70b β†’ deepseek-chat β†’ gemini-2.0-flash β†’ gpt-4o-mini)

Within each provider, multiple model variants are tried in order. This means the system can handle a partial outage β€” for example, if one Gemini model is rate-limited, it tries the next Gemini model before moving to Groq.

Every fetch call uses an AbortSignal timeout, so a slow provider does not hold up the entire request. Each call is wrapped in a try/catch, and the engine moves to the next option without surfacing errors to the user. If all four providers fail, the feature gracefully degrades rather than showing an unhandled error.

This approach powers several features on JobLink Philippines:

  • AI Resume Roaster & Analyzer: Provides jobseekers with constructive, section-by-section feedback in a friendly Taglish tone to help them spot resume weaknesses before applying.
  • JobMatch Engine: Analyzes job requirements alongside candidate profiles to suggest relevant matches based on actual skills and semantic similarity.
  • Automated Job Post Assistant: Helps busy recruiters and small business owners convert rough notes into structured, professional listings.
  • Skill Extraction & ATS Scoring: Standardizes free-form text descriptions into clear, searchable competencies and gives candidates an ATS readiness score.
  • Cover Letter Generator & Interview Template Generator: Additional tools accessible from the employer dashboard to reduce repetitive writing tasks.

A Note on How Costs Stay Low

Every AI call in the system uses a low temperature (typically 0.1 to 0.3) to reduce token usage on deterministic tasks like JSON extraction. Resume text is capped at 8,000 characters before being sent to the model. Embeddings and match results are cached in a server-side in-memory cache (1-hour TTL, 1,000-entry cap) to avoid redundant API calls for the same queries.


6. Semantic Matching: Going Beyond Keyword Search

One of the biggest limitations of older job portals is that they rely on exact keyword matching. A candidate who writes "client-facing" might get filtered out of a role that requires "customer-facing communication" β€” even though both describe essentially the same skill.

JobLink Philippines uses vector embeddings to get around this. When a match is computed between a candidate and a job posting:

  1. The candidate's profile (title, skills, summary, experience) and the job posting (title, category, skills, requirements, description) are each formatted into a plain-text representation.
  2. Both texts are converted into 768-dimensional embedding vectors using Google's gemini-embedding-001 model.
  3. Cosine similarity is computed between the two vectors β€” a number that reflects how semantically related the two texts are, regardless of exact word choice.
  4. The cosine score is scaled to 0–100 and blended with a direct skill overlap score (what percentage of the job's required skills appear in the candidate's listed skills).
  5. An AI audit is then run using the fallback engine to generate a written summary, a list of strengths, identified gaps, and actionable recommendations for the candidate.

Transparent Match Scoring

On JobLink Philippines, the final match score is a blend of two signals:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   JobMatch Scoring Breakdown                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Semantic Similarity (55%)         β”‚ Skill Overlap (45%)         β”‚
β”‚ Cosine distance between           β”‚ Direct comparison of listed β”‚
β”‚ embedding vectors for candidate   β”‚ candidate skills vs. job    β”‚
β”‚ profile & job description         β”‚ required skills             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Instead of just showing a percentage number, the interface highlights matching skills in green and missing skills in red. Employers immediately understand a candidate's strengths, and applicants receive practical feedback on what to work on next. The AI-generated summary explains the reasoning in plain language so the score doesn't feel like a black box.


7. Standardized Resumes with Built-in PDF Export

Recruiters regularly receive resumes in wildly varying formats β€” multi-column designs that confuse screening software, scanned low-resolution images, and inconsistently formatted Word documents.

To make life easier for both sides, JobLink Philippines includes a built-in Resume Builder. Candidates enter their work history, education, and skills once into clean form fields. The platform then renders a standardized, ATS-friendly PDF dynamically using @react-pdf/renderer β€” a library that converts React component trees into actual PDF files on the server side.

Applicants get a clean resume they can download at any time. Recruiters get documents that format consistently across every candidate. The resume data is stored in Firestore and synced with the user's profile, so it feeds directly into the job match engine without the candidate needing to re-enter anything.


8. The Email Delivery Layer

Email is one of those systems that seems simple until it breaks at the wrong moment. JobLink Philippines handles transactional emails β€” interview invites, application status updates, job alerts β€” through a dual-provider fallback built into the Express worker's email service.

The worker tries:

  1. Resend API (primary) β€” a modern transactional email API with a clean SDK and reliable delivery
  2. Nodemailer over SMTP (secondary fallback) β€” a Gmail SMTP route that activates automatically if Resend encounters an error

If both fail, the job is marked as failed and retried with exponential backoff. Failed jobs remain visible in the Bull-Board dashboard and can be manually retried.

Email templates are built using @react-email/components β€” React components that render to HTML strings specifically optimized for email clients, including Gmail, Outlook, and mobile mail apps.


9. Real Mistakes and What They Taught Me

Building in public means being honest about the hurdles. Here are three real situations I ran into during development and how I worked through them:

1. The Runaway API Loop

  • What happened: During early search testing, an unintended React re-render triggered consecutive AI calls in quick succession.
  • The fix: I added client-side debouncing, token-budget safeguards, and caching in Redis and Firestore so identical queries are served from cache without repeating external API calls.

2. Unbounded Database Scans in Scheduled Tasks

  • What happened: Early scheduled cleanup scripts scanned entire database collections without cursor pagination. This would have caused performance issues as user records grew.
  • The fix: I moved heavy batch tasks into the BullMQ worker and refactored scheduled jobs to process records in small, cursor-paginated batches with automatic retry logic.

3. Email Notification Reliability

  • What happened: Relying on a single third-party email provider meant that if their API had an occasional issue, interview invites could be delayed or dropped.
  • The fix: I built the dual-delivery setup described above: Resend as primary, SMTP as fallback, with a dead-letter retry queue for anything that slips through.

10. What Does It Cost to Run? (The $1–$5 / Month Reality)

One common assumption is that running a full-stack platform with background queues and AI requires significant cloud infrastructure spend. Thanks to modern serverless platforms and generous free tiers, JobLink Philippines currently runs with very low fixed monthly overhead:

ServiceRole & PurposeMonthly Cost (Current)Notes
VercelNext.js Frontend & Server Actions$0.00Generous tier covers all current traffic needs
Express Microservice (Render)Background Worker Hosting$0.00Free container tier for BullMQ queue processing
Redis (Upstash)BullMQ Message Broker & Cache$0.00Free tier; drainDelay configured to reduce command usage
Neon PostgreSQLServerless Relational Database$0.00Scales to zero when idle; 0.5 GB storage included
Firebase / Google CloudReal-Time Messaging & Authentication$0.00 – $1.00Free quota handles tens of thousands of daily operations
Vercel BlobFile Storage for Resume PDFs$0.00Included in Vercel free tier for current storage levels
AI ProvidersResume Feedback & Match Processing$0.00 – $3.00Pay-per-token; costs pennies per batch at current usage
ResendTransactional Emails & Notifications$0.003,000 monthly emails included in free tier
PayMongoLocal Payments (GCash, Maya, Cards)$0.00 fixedStandard per-transaction fee; no monthly cost
Domain NameCustom .com Domain~$1.00 – $2.00 / moAnnual renewal spread across 12 months
TOTAL RUNNING COST~$1.00 – $5.00 / monthZero heavy fixed servers; sustainable from Day 1.

One thing worth noting: every BullMQ worker is configured with a drainDelay of 30 seconds β€” meaning when the queue is empty, the worker waits 30 seconds before polling again instead of continuously hitting Redis. This single configuration choice significantly reduces the number of Redis commands used daily and helps stay within the free tier limits.


11. Key Lessons from Building as a Solo Developer

Reflecting on the entire journey, a few things stand out:

  1. Decouple heavy work early. Offloading resume parsing and batch emails to an Express + Redis + BullMQ worker kept the main web application fast and protected from serverless timeout limits.
  2. Nail the core loop first. Before adding complex features, make sure the fundamental experience β€” finding a job, applying, and messaging β€” is smooth and dependable.
  3. Use the right tool for the job. Pairing Firebase for real-time interactions with Postgres for structured records, and keeping background jobs in a separate process, allowed building quickly without awkward architectural compromises.
  4. Plan for provider downtime. Never assume a third-party API will be available 100% of the time. Multi-tier fallbacks β€” both at the AI layer and the email layer β€” made a real difference.
  5. Design for local realities. Supporting mobile screens, low-bandwidth connections, and familiar payment options like GCash and Maya isn't optional in the Philippines β€” it's essential.
  6. Transparency builds trust. Whether it's clear candidate status updates or an understandable match score breakdown, users appreciate honesty over opaque black-box promises.
  7. Watch your free tier limits carefully. Free tiers are genuinely generous today, but small configuration decisions β€” like drainDelay on BullMQ or capping AI prompt lengths β€” can be the difference between staying within the free tier and getting an unexpected bill.

12. What's Next for JobLink Philippines?

JobLink Philippines is continuously evolving. Here is what I'm actively working on next:

  • Interactive Pre-Screening: Optional, conversational interview practice tools to help jobseekers prepare before speaking with recruiters.
  • Verified Practical Skills: Lightweight, objective skill assessments that allow candidates to demonstrate their actual capabilities directly on their profiles.
  • Deeper Local Integrations: Expanding partnerships with local companies, startups, and remote teams looking to hire Filipino talent.

Final Thoughts

Building JobLink Philippines has been a clear demonstration that with the right modern tools, discipline, and a genuine understanding of the problem, a solo developer can build a platform that delivers real value.

It is not magic β€” there were slow days, confusing bugs, and plenty of moments where I had to step back and rethink an approach. But every piece of the architecture described here is real, running in production, and solving actual problems for real users.

If you are currently looking for career opportunities in the Philippines, or if you are an employer looking for great talent, I invite you to explore JobLink Philippines and try tools like our Resume Roaster and JobMatch.

Thank you for reading, and I hope my experience encourages other builders to turn their ideas into reality!

0

Community Discussion0

Join the conversation with Filipino jobseekers, remote professionals, and employers.

Commenting as Anonymous(Guest)