Friction Points
A friction point is a specific moment in the journey where the user is most likely to stop and leave. This page lists the four biggest ones. They are not guessed. They come from the Singapore conversion funnels for jodapp.com, measured in GA4 over 90 days, and from a code audit of the current apply flow. A funnel is the set of steps a user goes through to reach a goal, where some people leave at each step. GA4 is Google Analytics 4, the tool that records what users do on the site. All numbers are locked to jodapp.com and Singapore, except where we name the separate gig-partners.jodapp.com site explicitly — every gig application completes there.
We rank the friction points by size, which means by how many users we lose at each step. Fixing the largest one first gives the biggest return, because the same percentage improvement is worth more when it is applied to more users.
The IA concepts behind this page
- Funnel drop-off analysis comes from conversion-rate-optimisation practice: rank the steps by how many users are lost, and fix the largest loss first.
- The reasons we give for each drop-off use heuristic evaluation (Nielsen & Molich, 1990), which checks an interface against known usability principles.
For the full list of concepts and sources, see Theory & Method Foundations.
The funnels, and where we lose people
There are two funnels, not one, because the two job types complete their applications in two different places. Gig jobs carry about 75% of the job-detail traffic, and their applications complete on the separate gig-partners site. Careers jobs carry about 25% of the job-detail traffic, and their applications complete on jodapp.com. The method and the full numbers are in the Overview, Finding 1.
Here are the four friction points ranked by the number of users lost.
| Rank | Friction point | Users who leave | Why it is costly |
|---|---|---|---|
| 1 | From the job list to opening a job | 53% (desktop 67%) | It happens on the most-used page, so it affects the most users |
| 2 | From viewing a job to the next tracked step | 76% (measured with a proxy event — see below) | The user already showed strong interest, so this loss is expensive |
| 3 | From wanting to apply to a recorded application | Careers: 99.3% (0.68% complete). Gig: about 95.6% (about 4.4% complete, on gig-partners) | The user already decided to apply; every loss here is a lost applicant |
| 4 | From a search to a result the user trusts | not a single funnel number, but it feeds the steps above | The user cannot see or control the ranking, so relevant jobs stay hidden |
Friction 1 — From the job list to opening a job
This is the largest drop-off. Of the people who reach the job list, 53% never open a single job. On desktop this rises to 67%. Because the /jobs list is the most-used page on the whole site (35,914 views in 90 days), this is the most valuable place to improve. The list feeds both funnels: about 75% of the detail traffic below it is gig, about 25% is careers.
GA4, jodapp.com, Singapore, 90 days:
- Of 3,011 users who reached the list, only 1,417 opened a job.
- 53% opened none.
- 67% opened none on desktop.
Why users leave here:
- The job card does not clearly answer the user's first question, which is "is this one worth opening?" The card is incomplete rather than empty. Gig cards do show pay — "
{payFrom}daily" — because the gig sync always marks pay as visible. Careers cards show pay only when the employer chooses to show it. No card shows the outlet name: the location is only the last part of the area path, for example "Tampines", never "FairPrice Tampines Mall". And no daily-pay badge exists, even though "daily pay" is our best-clicking search phrase. - An advertisement is inserted after every fifth card, as an extra item in the list — one per page of ten cards; no job card is removed. The slot is a paid campaign placement (
job_list_inline) that falls back to Google AdSense when no campaign is served. Either way, it interrupts scanning in the middle of the list. - There is no way to browse by category, and the only sort options are "Latest Created" and "Latest Updated". There is no "Best match" label and no "Highest pay" sort. So the user cannot bring the best-paying jobs to the top, and cannot even see that search results are already ranked by relevance (see Friction 4).
- The search box placeholder says "Search by Title, Category, Company, Location or Description". Telling the user that search also matches descriptions lowers confidence that the results will be relevant.
- On desktop, the filter column on the left is hard to use, so many users do not filter at all. They scroll through a general list, do not find a job that fits, and leave. (This last reason is our assessment, not a measured fact.)
What to change: redesign the job card so pay and outlet stand out — the daily-pay badge depends on one business rule, see the Prioritization Roadmap; resolve the ad slot with billing instead of deleting it (see the advertising section below); add the category filter; and expose the "Best match" and "Highest pay" sorts. These are described in Taxonomy & Filters and Primary Flows (Flow 2 and Flow 3).
Friction 2 — From viewing a job to the next tracked step
Of the people who open a job from the list, 76% never reach the next step we can track. This loss is expensive because these users already showed strong interest by opening the job.
GA4, jodapp.com, Singapore, 90 days:
- Of 1,417 users who opened a job from the list, only 347 reached the next tracked step — 76% did not.
- Honest caveat: that step is the
form_startevent, which fires on the search, login, and sign-up forms, never on the job page itself. - So it is a proxy for "reached a gate before applying", not a true apply-form start.
Why users leave here:
- The job detail page is dense, especially on a mobile phone, which is where about 3 in 4 users are (74% of sessions). Important information is hard to scan quickly.
- An advertisement slot sits inside the decision zone. On the careers detail page it sits directly between the job summary and the Apply button — verified in the code. On the gig detail page the slot sits before the shift lists, which is where the Apply links live. Like the list slot, this is a paid campaign placement (
job_detail_inline) with an AdSense fallback. - On mobile, no Apply control stays visible while the user scrolls. On the careers page there is a single Apply button in the middle of the page, so a user who scrolls past it may not find it again. The gig page is different: it has one Apply link per shift group, not one button, so a sticky control needs a product decision first — for example a sticky "See shifts" button that scrolls to the shift list.
- Many users arrive on a single job page directly from Google, with no earlier context (25.2% of sessions land this way). If that one job is not a perfect fit, there is nothing on the page to keep them, because there are no "similar jobs" and no "more jobs from this employer".
What to change: resolve the ad slot with billing (below); make the careers Apply button sticky — genuinely low effort, because the button already exists and the sticky pattern already exists on the home page bottom navigation; decide the sticky pattern for gig; and add "similar jobs" and "more from this employer" so users who arrive from Google have somewhere to go. These are described in Primary Flows (Flow 1).
Friction 3 — From wanting to apply to a recorded application
This is the deepest problem. The user has already decided to apply. What happens next is different for the two job types, and both paths lose almost everyone — but at very different rates, and the difference is the most useful fact on this page.
GA4, Singapore, 90 days:
- Careers: 18 of 2,646 careers detail users completed an application on jodapp.com (0.68%).
- Gig: 146 users completed on gig-partners.jodapp.com, about 4.4% of the 3,334 gig detail users.
- A cross-host funnel traces roughly 60–65% of them (about 97 users) back to the jodapp.com job list.
Compare the two rates. The gig path sends the user to a different website, and still about 4.4% complete. The careers path stays on our own site, and only 0.68% complete — about 6 times worse. When staying on-site performs worse than leaving the site, the on-site path itself is the problem.
The careers path, step by step (verified in the code):
- The user taps Apply and is asked to log in.
- A new user signs up, which includes a 6-digit email one-time password.
- After sign-up, the user is not logged in. The site bounces them back to the login form, and they must sign in again with the details they just created. This is a newly found friction — the earlier version of this page missed it.
- Next comes the
/talent/setupwizard with 6 steps: Talent Information (including salary expectation), Work Experiences, Certificates, Skills, Educations, and Confirmation. 3 of the steps are hard gates — salary, education, and skills cannot be skipped. Experiences and certificates can be skipped. - Only after all of this does the user land back on the job page, and the application is recorded.
The result: 18 users completed this path in 90 days (33 completion events from 18 users site-wide; only 7 of the 18 came through the full list → detail → form sequence).
The gig path: every Apply link leaves the site for gig-partners.jodapp.com, and the application is recorded there. jodapp.com itself never records it, which is why the site cannot see its own core success event.
What to change:
- Record the application first, using the lightest possible identity step, and ask the user to complete the profile afterwards, in small steps. Then ask 1–3 employer-defined screening questions, after the application is recorded, so the employer still learns enough about each applicant.
- Fix the sign-up → login bounce, so a new user is logged in the moment their OTP is confirmed. This is high impact for low effort.
- Align the login UI with the Fluid Identity model that our analytics architecture already documents as Principle D (
docs/20-29-backend/27-analytics/architecture/): there is oneIdentities::User, with gig and careers personas on top. The two-account split only exists against the legacy gig-partners Laravel app, which has its own user ids. Today the login page shows two login cards stacked vertically; the doctrine says the user is one person, so the UI should show one login. - Add a jodapp.com-native apply event, so a completed application can be measured on the site at all.
This is the redesigned apply flow in Primary Flows (Flow 5), and it targets this loss directly.
Friction 4 — From a search to a result the user trusts
This friction point does not appear as a single number in the funnel, but it makes the steps above worse. It is also the friction point the earlier version of this page described wrongly, so this section starts with a correction.
The correction. The earlier version said: "the result ordering is set to newest-first at all times, and this overrides the relevance ranking." We checked the code, and that claim is false. Inside the full-text search, pg_search puts the relevance rank first in the ordering (rank DESC). The newest-first ordering chained after it can never take effect while a search term is present. So when the user types a query, relevance already wins. The fix we previously proposed — "fix the ordering" — is not needed.
What is actually true (verified in the code):
- Browsing without a search term is newest-first. That is a reasonable default and needs no fix.
- When two results have equal relevance, the tie is resolved toward the older job (the tiebreak is ascending id).
- The interface exposes no sort choices beyond "Latest Created" and "Latest Updated". There is no "Best match" label, so the user cannot see that relevance ranking is active, and there is no "Highest pay" sort at all.
- A search for "cashier" can still return jobs where the word appears only in the description. The relevance ranking pushes those weak matches down, but the user gets no signal that this is happening.
So the real friction is not broken ordering. It is invisible ordering: the system ranks by relevance, but the interface hides that fact and gives the user no control.
What to change: expose a "Best match" sort label, shown as the active sort whenever a search term is present — this needs no backend change, because the behaviour already exists. And add a "Highest pay" sort option — a small backend change, one more allowed order_by value. This is described in Taxonomy & Filters and in Primary Flows (Flow 3).
One more cause that sits across the funnels: advertising placement
The audit found advertisement slots in three positions, each in a place where users make decisions:
- Above the main heading on the home page (placement key
home_hero). - Inserted after every fifth card in the job list, one per page of ten cards (
job_list_inline). No job card is removed; the ad is an extra item. - Inside the job detail decision zone (
job_detail_inline): between the summary and the Apply button on careers pages, and before the shift lists on gig pages.
The earlier version of this page called these "AdSense ads earning cents per day" and proposed removing them as a low-effort cleanup. That was half right, and the half that was wrong matters. All three slots render AdsCampaignPlacementDisplay, which is our own self-serve paid campaign placement — the same inventory the billing Phase 2 work monetises (see 38-ads, billing-integration-phase-2). Google AdSense appears only as a fallback, when no campaign is served or the request fails. Deleting the slots would delete sellable inventory.
So the correct action is a decision with billing, not a deletion:
- Confirm with billing which placement keys currently have sold campaigns.
- Remove the AdSense fallback from the job-list and job-detail slots. The fallback is the part that earns only cents per day, and it harms the funnel for almost nothing in return.
- Keep the paid-placement capability. It becomes the featured-distribution product later — the thing we actually want to sell. See the Prioritization Roadmap.
This is an open decision, not a settled one. Owner: billing/product.
Three of the four friction points share one root cause. At each step, the product puts something between the user and their goal: an incomplete card and an inserted ad before opening a job; an ad slot and a non-sticky button before starting an application; and a login, a sign-up bounce, and a 6-step wizard before finishing one. The single most valuable change across the whole product is to remove these blocks and let the user move straight toward applying. This is why the Prioritization Roadmap starts with a "measure and unblock" phase. But removing blocks is only one of the two levers: an unblocked funnel still needs visitors per post, which is why the roadmap pairs these fixes with distribution — see the Overview, Finding 2.