The practices FlowKy checks
13 standards and 65 checks. Published in full, because a checklist nobody can read is a checklist nobody agreed to β and because you should be able to disagree with one before you install anything.
These are defaults, not rules
Run a skill once and its standard is written to .flowky/skills/<id>.md in your project. Edit that file and the report follows it β the copy in your repository wins, it is reviewed like any other change, and it travels with the project rather than living in someoneβs editor settings. A check you disagree with can be turned off there too, and the report says which ones you disabled rather than quietly omitting them.
Advertising & tracking
4 checksPixels, consent gating, attribution and third-party script cost.
- No tracking pixel fires before consent in any jurisdiction that requires it.
A pixel in the document head runs before your banner has rendered.
- Third-party scripts load deferred or after interaction. Each one blocking
the head delays first paint for every visitor, including the ones who
blocked it.
- Conversion events are named in one place. Two spellings of the same event
split every report that matters.
- UTM parameters are captured at first touch and kept, or attribution
silently credits the last thing the visitor clicked.
- A consent rejection is honoured for the whole session, not re-asked on the
next page.
API
5 checksValidation, authorisation, limits and error shape on your endpoints.
- Every endpoint validates its input against a schema. A handler that reads
body.x directly trusts whatever arrives.
- Writes take an explicit ALLOW-LIST of fields. Spreading a request body into
an update lets a caller set columns you never meant to expose β role and
status among them.
- Authorisation is in the query, not a check before it. `WHERE id = ? AND
owner = ?` has no window between deciding and acting.
- Errors are the same shape everywhere, and never leak a stack trace or a SQL
string to the caller.
- List endpoints paginate. An unbounded list is a denial of service you wrote
yourself.
- Anything unauthenticated that costs money, sends email or writes rows is rate
limited.
Database
6 checksMigrations, indexes, connection handling and query shape.
- Schema changes are versioned files applied by the deploy, not a command
someone remembers to run. Without that, code ships and the table does not.
- Migrations are expand-only when they run before the deploy: add columns and
tables, never drop, or the currently running version breaks mid-release.
- Every foreign key has an index. Postgres does not create one for you, and the
join that seems fine at ten thousand rows is not fine at ten million.
- Money is stored in integer minor units, never a float.
- Check-then-write is a race. Put the condition in the WHERE clause and check
how many rows you actually changed.
- Credentials come from the environment. A connection string in the repository
is a credential in every clone and every fork.
Design system
5 checksTokens, theming, responsiveness and accessibility basics.
- Colour, spacing and radius come from tokens. A hex code typed into a
component is one the theme can never reach.
- Dark mode is defined by redefining tokens, not by scattering
dark:
overrides β and a component with a hardcoded white cannot be fixed centrally.
- Every interactive element has a visible focus state. Removing the outline
without replacing it makes the product unusable by keyboard.
- Nothing conveys meaning by colour alone: about one man in twelve cannot
separate red from green.
- Layouts are responsive by default. A fixed pixel width wider than 360 makes
the page scroll sideways on a phone.
- Images declare dimensions, so the page does not jump as they load.
DevOps
5 checksCI, deploys, rollback, secrets and whether anyone would notice an outage.
- One deploy definition. If CI and a shell script both know how to ship, they
will disagree on the day it matters.
- CI runs lint, types and tests BEFORE the build, so a failure costs seconds
rather than a full compile.
- A green pipeline that deploys nothing is worse than a red one. Any step that
can silently skip must say so loudly.
- Rollback is a traffic switch, not a rebuild. Rebuilding to roll back means
the outage lasts as long as a build.
- Secrets come from a secret manager, are never printed, and never reach the
build context.
- There is a health endpoint that reports the running version, so "what is
live" is answerable without guessing.
Landing pages
4 checksFirst paint, the offer above the fold, and mobile behaviour.
- One clear action per page. A page with five equal buttons has no call to
action, it has a menu.
- The headline says what the product does for the reader, not what category it
belongs to.
- Nothing wider than the viewport. One overflowing element makes the whole page
scroll sideways, and on a phone that reads as broken.
- The viewport meta tag exists and does not disable zoom. Blocking zoom fails
accessibility outright.
- Fonts and hero images do not block first paint.
- Social proof is specific and true. A logo wall of companies who never paid
you is a liability, not a conversion.
Login & auth
7 checksPassword handling, sessions, and the paths that lead to account takeover.
- Passwords are hashed with bcrypt, scrypt or argon2 β never a general-purpose
hash like SHA-256, which a GPU tries by the billion.
- Sign-in, registration and password reset are rate limited. These are the
three endpoints attackers hit hardest.
- Session cookies are httpOnly, secure and sameSite. A token readable from
JavaScript is a token an XSS steals.
- Failure messages never distinguish "no such account" from "wrong password".
The difference is an account enumeration oracle.
- Password reset links are single-use and expire. A reset link in an inbox is a
standing credential otherwise.
- A banned, suspended or deleted account is refused on every path β including
API tokens issued before the ban.
- OAuth account linking is not automatic on matching email unless that email is
verified by the provider.
MCP
4 checksDeclared MCP servers, their transports, and the secrets they carry.
- MCP server configuration is committed so the team shares one set of tools,
but the credentials in it come from the environment.
- Every server declares an explicit command or URL. A server that resolves
through a package manager at run time is a supply chain you did not choose.
- Tools that write β file edits, shell, deploys β are separated from tools that
only read, so a read-only session can be granted without the rest.
- A server that cannot be reached fails loudly. Reporting a connection that
did not happen is worse than reporting none, because the caller then waits
for tool calls that can never arrive.
Referral & partners
4 checksAttribution, self-referral, payout races and the reward ledger.
- A payout is claimed in ONE statement whose WHERE clause carries the
condition, and the code acts on the rows it actually changed. Read, check,
then write lets two concurrent requests both pass β and that is a double
payment.
- Nobody can refer themselves. Check both the account and the email, because
the second account is free to create.
- A reward is 'approved' and 'paid' separately. Marking money paid before it
moved makes the ledger lie.
- Referral codes are unique and not guessable in sequence.
- The attribution window is written down and enforced in one place.
- Every reward row records what earned it, so a dispute has an answer.
SEO
7 checksWhat a search engine can reach, read and represent.
- Every indexable page has a unique
<title>and meta description. Duplicates
across pages are worse than missing ones: they tell a crawler the pages are
the same.
robots.txtand a sitemap exist and agree with each other.- One
<h1>per page, describing that page rather than the product. - Canonical URLs on anything reachable by more than one path β pagination,
query parameters, trailing slashes.
- Open Graph and Twitter card tags, because a link shared without them renders
as a bare URL and loses most of its clicks.
- Images carry meaningful
alttext. "image" and "photo" are not meaningful. - Structured data where the content has a type search engines understand
(Article, Product, FAQ, Organisation).
Silent failures
5 checksFallbacks that hide breakage instead of reporting it.
- A
catchblock either handles the error, logs it, or rethrows. An empty
catch is a decision to never find out.
- A data loader that returns an empty array on failure must distinguish "there
is nothing" from "I could not read it". They look identical on the page and
mean opposite things.
- A health or status check calls the dependency. Confirming that a key, a token
or an environment variable EXISTS proves the configuration, not the service β
and the two come apart for the most ordinary reasons, like an unpaid invoice.
- A degraded state is visible. If the product silently serves something worse,
a human has to be able to tell from the outside that it is doing so.
- Fallback content is distinguishable from real content, in the logs if not on
the screen. Canned answers that look like generated ones hide an outage.
- Anything with a graceful fallback deserves a test that exercises the FAILURE
path, because the success path is the one that gets exercised by accident.
- If a module has no callers, delete it or wire it up. Dead code that a page
reads from is a promise the page cannot keep.
Texts & wording
5 checksProduct copy: tone, consistency, placeholders and untranslated strings.
- Error messages say what happened and what to do next. "Something went wrong"
tells the reader only that you also do not know.
- No "simply", "just" or "obviously". If it were simple the reader would not be
stuck; the word only suggests the difficulty is their fault.
- The product name is spelled one way, everywhere.
- User-facing strings live in the translation layer, not inline in components,
or a second language becomes a rewrite.
- No placeholder text in shipped surfaces. Lorem ipsum in production is a
visible sign nobody looked.
- Buttons say what they do. "Send the quote" beats "Submit".
Social media
4 checksShare previews, profile links and what a pasted URL looks like.
pasted without them is a bare URL, and a bare URL is scrolled past.
than a headline disappears in a feed.
small, permanent embarrassment.
every article looks identical in a timeline.