The 4 Cursor skills I use to ship a feature without breaking production
Cursor is happy to write a lot of code. That is the problem.
You ask it to add a flag, or to stop a checkout page from crashing, and it comes back with a diff that touches six files, reformats imports, and "improves" a helper you did not mention. Sometimes the feature works in the happy path. Then a webhook fires twice, or a missing priceId is optional-chained into an empty string, and production is quietly wrong.
I got tired of that, so I stopped pasting raw requests into the agent. I keep four short SKILL.md playbooks and I call them by name. They do not make the model smarter. They make the next step smaller, and they make it refuse a few of the shortcuts that break prod.
1. Ship a feature in slices, not as one heroic diff
The ship-feature skill starts by reading, not writing. It has to name the closest existing feature in the repo and copy that pattern: auth, validation, data access, tests. If it cannot find a pattern, it does not invent a new architecture.
Then it shows a plan of ten lines or fewer:
- Goal: ...
- Slices:
- migration: ...
- server: ...
- ui (flagged): ...
- tests: ...
- Assumptions: ...
- Out of scope: ...
Two stop conditions matter more than the plan. If the change needs more than eight files, or a new external service, it proposes a smaller v1 and waits. If it needs a secret that is not already in the environment, or a paid third-party account, it stops and says so. It does not hardcode a key "just for now."
Implementation is slice by slice. Migrations are additive only - no DROP, no RENAME, no new NOT NULL on a column that already has rows, unless there is a backfill and a rollback. Every new server entry point gets an auth and ownership check. User-visible work goes behind a flag when it is not trivial.
Then run the project's type-check, lint, and tests, and add three tests if they are missing - happy path, auth or ownership failure, invalid input. The hand-off lists what shipped, how to verify, the flag, the migration, and anything left as DEBT.
2. Fix the bug at the cause, not at the crash line
The failure mode I see most often without a playbook looks like this. Request: TypeError: Cannot read properties of undefined (reading 'priceId') at the checkout page. "Fix it."
Without a process, the agent writes:
- const priceId = user.subscription.priceId;
+ const priceId = user?.subscription?.priceId ?? "";
The page stops crashing. Checkout now sends an empty price ID to Stripe. You have traded a loud error for a silent, more expensive one.
The fix-bug skill does not start at the assignment. It writes the bug down first (expected, actual, trigger, environment, first frame in our code). Then it has to reproduce, preferably with a failing test, before it changes production code. If it cannot reproduce, it lists the facts it still needs and stops.
After that: at most three hypotheses, most likely first, each with a cheap check - read the caller, log a value, run a query. The skill tells the agent to look one level up the stack, because the bad value usually originates there.
In the checkout example, the question is not "how do I stop this line from throwing?" It is "why is user.subscription missing for this customer, and what should checkout do in that state?"
The fix has to be the smallest diff that addresses that cause. No drive-by refactors, no formatting churn. If null is a legitimate state, handle it and leave a comment saying why. If the proper fix is large, ship a guarded mitigation and record the real fix as DEBT.
A few refusals are written down: do not swallow the error in an empty try/catch; do not use @ts-ignore as the fix; do not bump a timeout without explaining the race; do not skip the failing test.
When it is done, it reports the root cause in one sentence, the files, the regression test, any similar risk, and a confidence level.
3. Review your own pull request like someone else wrote it
I work alone, so there is no second reviewer. The pr-review skill is how I pretend there is one, on the same branch, before I merge. It loads the diff and restates the intent in one sentence. If the diff does not match the intent, that is the first finding.
Then a fast risk scan, before anyone argues about style. It greps for a fixed list:
- new routes without an auth and ownership check
- raw SQL,
exec,eval,dangerouslySetInnerHTML fetch(on a user-supplied URL- migrations that
DROP,RENAME, or addNOT NULL - payment or webhook code missing idempotency or signature verification
- secrets and PII in logs
- removed tests and lint escapes
Only after that does it walk the logic: empty and null inputs, missing await, shared state, error paths that show nothing to the user, queries inside loops.
The write-up has a forced shape - verdict, intent, counts, biggest risk, then comments tagged [blocker], [should], [nit] (at most three nits), each with a concrete fix. Inventing nits to look thorough is banned. "No blockers found" is a valid result.
I treat blockers as merge gates, even when I am the only author. That is cheaper than an incident on a Sunday.
4. When the feature is actually live, write the announcement from the repo
Shipping is not only the deploy. The fourth skill, write-launch-copy, is for the message you post after the flag comes off. The important part is what it is forbidden to do.
It has to read the README, the landing, the pricing config, and the changelog, and fill a fact sheet. Any field it cannot find stays as a placeholder. It must not invent user counts, testimonials, or revenue. If there is no proof yet, the copy says there is no proof yet.
The one-liner it aims for is dull on purpose: <Product> helps <who> <outcome> without <pain>.
From there it only produces the assets you asked for - a landing hero, a Product Hunt first comment, an X thread, a Reddit post, a short launch email. A Reddit post has to stand if you delete the last paragraph, where the product is mentioned. No "revolutionary," no "seamless," no hashtag spray.
This skill does not keep production up. It keeps you from announcing a feature with a claim the repo cannot support, which is a different way to break trust.
How I actually invoke them
I do not rely on the agent noticing the right skill every time. I say it in the chat: "Use the fix-bug skill; here is the stack trace." Or: "Use ship-feature; add a resend-receipt endpoint, no new dependencies."
If the editor version does not support skills, @-mention the SKILL.md file. The text is the same playbook either way.
I also keep a small always-on project rule: smallest change that solves the problem, no drive-by refactors, secrets from env vars, and a short done summary (what changed, how to verify, risks). The value is that the agent is no longer inventing a process in the same moment it is inventing the code.
If you want these four skills as drop-in SKILL.md files, plus the project rules that sit next to them, I put the pack on ShipStack. The steps above are the core of it; you can also just copy them into your own repo and tidy the wording.
Comments
No comments yet. Start the discussion.