Startup grants × an anonymous builder community
Commit
Summary
A daily pipeline that collects and cleans government startup grants, with an anonymous builder community on top – so finding a grant, testing an idea and logging the work happen in one mobile-first app. Designed and built by a designer, alone, with AI pair-programming.
- 28 days
- 11,757
- ₩0
Overview
Commit treats a grant announcement as a post written by the agency's account. The grant list and the community feed share one skeleton, and an agency's page is drawn with the same component as a person's profile. Finding a grant, getting an idea tested anonymously and recording the work continue along one timeline.
Live: commit.ai.kr
Problem
Even finding grants is hard.
- Scattered sources – 1,722 different ways of writing agency names in the production database.
- Messy originals – duplicated agency names, broken "?" characters, and prefixes like "(re-posted)(extended)".
- No amounts – the grant API has no funding amount; it lives only in a separate programme API.
- Wrong order – most lists sort by posting date, when what matters is the deadline.
- Nothing after finding – half-formed ideas are hard to post in a real-name community.
Research
K-Startup, Bizinfo, contest sites, startup media, Disquiet, Indie Hackers and Product Hunt, and Notion went into one comparison table. The conclusion wasn't "nobody does this" but "nobody connects it": there are places that collect grants, places that write startup news and places to share building in public – none that links the three.
Looking at the data itself mattered too: the founding-period and age fields often listed "every possible range", so tagging them as-is put every tag on every grant.
Key decisions
Treat a grant as a post by the agency's account
Then there's no reason to build grant search and the community separately – people and agencies post, get followed and get saved by the same rules.
The grant list and the feed share one component skeleton; agency pages reuse the profile header, and follow, bookmark and report work the same way.
Pivot from community-first to grants-first
People arriving from search were looking for grants. A community needs people to exist; grants only need a crawler.
Mentors, a demand-validation gate, build scores and ad slots were removed. The home is public without login, which is only asked for at actions like posting – which also fixed search indexing.
Accuracy over coverage
One wrong amount costs more trust than many right ones earn.
Amounts show only where the match is certain – 2,595 of 11,757 grants (22.1%). Logos likewise: a favicon service returned blank icons as “success”, so only 116 hand-checked agency logos are used.
Guarantee anonymity in the schema, not in a promise
An anonymous community is only trustworthy if even the operator can't expose who wrote what.
Row-level security on every table from day one; posts are read only through a public view without author identifiers; personal columns are locked with column-level grants. Posts reported by several users hide automatically.
Design
"Toss's information hierarchy + Threads' timeline." Blue means action, green means alive, and no arbitrary values.
- Tokens – a 7-step type scale (11 / 12 / 13 / 15 / 17 / 20 / 24), buttons at 36 / 44 / 48, radii 8 / 12 / 16 / full, 15 colour tokens × light and dark, one typeface (Pretendard) at −0.02em.
- Action colour and link colour split – after changing the accent blue, blue links inside body text became hard to read, so the button colour and the in-text link colour were separated.
- Three responsive layouts – under 768px / 768px+ / 1280px+, with a 640px centre column and a 360px right rail. Mobile tap targets grew from 36px to 44px.
Build
The collection pipeline
A Python crawler collects grants daily and cleans them in seven steps. Titles pass through five regex stages, applied retroactively to past data too. Notifications are deduplicated by a unique key, every run writes a heartbeat, and an alert fires if nothing has been collected for 36 hours. It runs on GitHub Actions at no extra cost.
Database and search
Supabase (PostgreSQL): 24 tables and 12 views and RPCs. Search is built in three layers: typo-tolerant trigram search → pgvector semantic search (with the built-in gte-small embedding model) → autocomplete and personal weighting. If an RPC fails, client-side results take over.
The limits are written down too: there are no LLM calls – grant summaries and eligibility checks are rule-based. With a paid external API, cost alone would have kept search out of the product.
Scale and lessons learned
In 28 days, 938 files changed (+27,927 / −6,233 lines); the code is 118 files and 10,536 lines with 47 components. Thirteen mistakes were logged – a feed limit that showed only four days of posts, dates off between UTC and Korean time, an ad script loaded only as a preload so ownership verification failed.
Ship · results
- 11,757
- 250
- 22.1%
Where a person steps in
- By itself – a Python crawler gathers notices daily through seven steps and records a heartbeat on every run. It says so when nothing has arrived for 36 hours.
- By hand – on money, accuracy beat coverage. Where the rules are not sure, the figure stays empty and the reader is sent to the original (22.1%).
- Never handed over – no model is called at runtime. Removing the place where it can be wrong costs less than correcting wrong answers.
Reflection
- Row-level security controls which rows; which columns is a job for grants. Learned while making anonymity a property of the schema.
- When an external dependency dies, the screen should live. Search, logos and amount matching all had their failure states designed first.
- Next – from 10 users to 100. And the debt: zero tests, a remote database that migrations alone can't reproduce, and a single grant source (K-Startup).