20 things to fix before a client touches your app
Twenty fixes that decide how a client demo goes, each with a copy-ready prompt and a way to prove it landed, plus the four no prompt can do for you.
Your client will not read your code. They cannot. The whole judgment happens in about ten minutes, on their own phone, with their thumb, and in those ten minutes a missing favicon counts for more than two weeks of careful architecture.
That is not unfair. It is the only evidence they have.
The awkward part is that the app already works. That is exactly what makes this list feel unnecessary right up until the moment it is not. It works on the machine it was built on, for the person who built it, with data that person typed. Your client is none of those three things.
Each item below has a prompt you can paste into your coding agent and, more importantly, a way to prove it actually landed. “I asked the agent to add empty states” and “there are no blank screens in this app” are different facts, and the gap between them is exactly where a client’s thumb lands. Four of the twenty cannot be done by a prompt at all. Those are marked.
The twenty
It has to look like a real app
- ☐ 1. Deployed to a real domain, not localhost and not a preview link
- ☐ 2. Realistic seed data: real names, real products, sensible prices
- ☐ 3. No placeholder text left anywhere
- ☐ 4. A favicon, a real page title and a link preview
- ☐ 5. A custom 404 page
- ☐ 6. The client’s logo and colors
It has to survive being touched
- ☐ 7. Empty states everywhere, never a blank screen
- ☐ 8. Loading states on every button and every page
- ☐ 9. Every screen opened on a real phone, yours
- ☐ 10. Checked in Safari on an actual iPhone
- ☐ 11. Error tracking, so you hear about breakage before they do
It has to be theirs
- ☐ 12. A login the client can use and reset without asking you
- ☐ 13. Your test accounts deleted
- ☐ 14. Every stray debug log removed
- ☐ 15. Outgoing email checked: what it says and who it is from
It has to exist on paper
- ☐ 16. A one-page handoff: what is built, what is not, what is next
- ☐ 17. Who owns what: domain, hosting, database, payments
- ☐ 18. Every environment variable written down, names only
- ☐ 19. A backup demo video, saved on the device
- ☐ 20. A scope document, so “can you also add” has an answer
And the one no prompt can do
- ☐ +1. One hour before, you click every single button yourself.
Items 9, 10, 19 and the +1 are yours. A prompt can make a layout responsive. Only your own thumb tells you the price column runs off the right edge.
If the demo is in an hour
Do not start at item 1 and work down. Do these six, in this order, and stop.
- Deploy to the real domain (item 1). Everything else is invisible if the link still looks temporary.
- Seed realistic data (item 2). Placeholder text undoes two weeks of work in one screenshot.
- Empty states on the three screens they open first (item 7). A white screen reads as broken, not as new.
- Open the whole app on your own phone (item 9). This finds more per minute than anything else on the list.
- Custom 404 (item 5). They will hit one, and a raw error page is the worst thing they can see.
- The +1. Click every button yourself with whatever minutes are left.
The other fourteen can wait until after the demo. These six are what a thumb finds in the first ninety seconds.
If you have ten minutes rather than an hour, do 4 and the +1. Open the app on your phone and use it like a stranger. Nothing else here finds as much per minute.
Make it look like a real app
1. Deploy to a real domain
A localhost in the address bar, or a forty-character preview link, tells a client this
is not finished before they have tapped anything. A domain says the thing exists in the
world.
Walk me through pointing a custom domain at this deployment, step by step,
assuming I bought the domain but have never done this before. Tell me what
to do at the hosting provider and what to do at the domain registrar,
separately. Tell me how long each change takes to take effect.
Verify: open the link on your phone with wifi off, on mobile data. The address bar shows the domain, the padlock is there, and nothing redirects to a long temporary URL.
2. Seed realistic data
Placeholder rows are the fastest way to make a working app look like a toy. Same screens, same code, completely different meeting.
Replace all placeholder data in this app with realistic seed data for a
[client's industry] business: real-sounding names, real product names,
sensible prices, and dates spread across the last few weeks so the lists
do not all show the same day. Keep the quantity close to what a real
account would hold, not three rows and not three thousand.
Verify: open the three busiest screens and read them out loud. If a line makes you wince, it will make the client wince.
3. Remove every piece of placeholder text
One paragraph of filler Latin on an About page and the client starts wondering what else is unfinished.
Search this entire project for placeholder text: "lorem ipsum", "TODO",
"FIXME", "coming soon", "placeholder", "your text here", and any repeated
dummy sentence. Show me every hit with its file and the screen it appears
on. Then write real copy for each one and show me the diff before you
apply it.
Verify: run the search again afterward and confirm zero hits outside code comments. Then look at the pages nobody visits: About, Terms, Settings, the footer. That is where filler survives.
4. Add a favicon, a title and a link preview
The blank page icon in the browser tab is small and nobody mentions it. It still reads as unfinished. The link preview matters more, because the first thing a client often does is forward the link to a partner.
Add a favicon to this app using the logo at [path], generating the sizes
modern browsers and mobile devices actually need. Also set the page title
and the social preview image, title and description, so a shared link does
not render as an empty card.
Verify: open the site in a new tab and look at the tab itself. Then paste the link into a messaging app and look at the preview card. That is what the client will see when they forward it.
5. Add a custom 404 page
They will mistype a URL or tap a stale link. A raw server error on a white background is the ugliest thing your app can show, and it is a few minutes of work.
Add a custom 404 page in this app's own branding, with a friendly line and
a button back to the home page. Also add a fallback error page for when
something genuinely breaks, with the same treatment. No raw stack traces
should ever be visible to a user.
Verify: on your phone, on the real domain, type a URL that does not exist. You should land somewhere designed, with a way home that works.
6. Put the client’s logo and colors in
This is one of the cheapest items here and one of the most noticeable. The app stops looking like a template and starts looking like their company.
Replace the default theme colors in this app with these brand colors:
[hex codes]. Apply them consistently to buttons, links, headers and
accents, and check that text still meets contrast guidelines on every
combination. Then put this logo in the header and on the login screen:
[path].
Verify: screenshot the dashboard and put it next to the client’s own website on the same screen. They should look related. Any leftover default framework blue stands out instantly in that comparison.
Make it survive being touched
7. Add empty states
A new account has nothing in it, so the first screen a client ever sees is the empty one. An empty screen with no explanation reads as broken rather than as new. This is one of the most common ways an app that works still loses a demo.
Find every screen, list, table and card in this app that can be empty,
including on a brand-new account. For each one, add an empty state: a short
line saying what goes here, and a button that starts the action. List the
screens you found before you change anything.
Verify: create a fresh account with nothing in it and walk the entire app. Every screen should tell you what it is for. Not one blank rectangle.
8. Add loading states
Without a spinner, someone taps a button, nothing visibly happens, and they tap it four more times. Now you have four orders and a client who thinks the app is broken.
Add loading states throughout this app: a spinner or disabled state on
every button that submits, a skeleton or spinner on every page and list
that fetches data, and a visible success or error message after each
action finishes. Disable buttons while a request is in flight so nothing
can be double-submitted.
Verify: throttle the network to a slow connection in your browser’s developer tools and use the app. Every tap should acknowledge you within a blink. That throttle is also roughly what a cafe’s wifi does to you on demo day.
9. Check every screen on a real phone
This one is yours. A prompt can make the layout responsive. Only your own thumb tells you whether the submit button sits under the keyboard.
Go through every screen in this app and make it work on a 375px-wide
phone: no horizontal scrolling, no text smaller than 14px, nothing cut
off, tap targets at least 44px, and any wide table restructured into
stacked cards. List every screen you changed and what you changed.
Verify: the real proof is your own phone, on the real domain, held the way a person holds a phone. Do the main task start to finish that way. Hunting for a back button with your thumb finds things no prompt will.
10. Check it in Safari on an iPhone
This one is yours. Your desktop browser is not the browser your client owns. Date pickers, file uploads and media playback are where rendering engines diverge most, and those tend to be a demo’s best moments.
Check this app for anything likely to behave differently in Safari and on
iOS: date and time inputs, file uploads, media playback, sticky
positioning, viewport height on mobile, and scroll behavior. Tell me what
is most likely to break before I go and test it.
Verify: open the app in Safari on an actual iPhone and do the main flow. Borrow one if you do not have one. Fifteen minutes removes the most embarrassing category of demo failure, the one where it works on your device and only on your device.
11. Add error tracking
Without it, the first person to know something is broken is your client. With it, you get an alert and can send a message before they have picked up their phone. This is the item that changes how the following months feel.
Add error tracking to this app. Recommend two services and explain the
tradeoffs before picking one. Capture both server errors and errors in the
browser, alert me on new issues, and make sure no passwords, tokens or
personal data end up in the reports. Then show me how to trigger a test
error so I can confirm it is working.
Verify: trigger a deliberate error on the live site and confirm the alert reaches your phone. An error tracker nobody has tested is usually a misconfigured one.
Make it theirs
12. Create a login the client controls
Handing over your own credentials makes them a guest in their own product. An account in their name, that they can reset without asking you, is the moment it becomes theirs.
Create an admin account for this app with the email
[client@their-domain.com]. Confirm password reset works end to end and
that the reset email actually arrives. Tell me how to hand over the first
password safely, and confirm they can change it themselves without
needing me.
Verify: log in as them in a private window, then log out and run a real password reset. If the reset email does not arrive, item 15 is already broken too.
13. Delete your test accounts
Rows like asdf@test.com and qwerty sitting in the user list of the app you just
handed over read as carelessness, even though they change nothing about how it works.
List every user account in this database with its email and creation date.
Mark which ones look like test accounts. Show me the delete statement for
those before running anything, and tell me whether deleting them breaks
any related records.
Verify: open the user list on the live site. Every row should be a real person or a deliberately seeded demo account. Keep one clean admin for yourself, with a real address.
14. Remove every stray debug log
Nobody opens developer tools during a demo. Then someone technical does, a week later,
and finds a river of here, hereeee and a dump of a customer record. Some of those
dumps contain things that should not leave your machine.
Find every console.log, console.debug and leftover debugging output in
this project. Show me the list with file names first. Flag any that print
user data, tokens, API keys or request bodies, because those matter most.
Then remove them and show me the diff.
Verify: open the live site, open the browser console, and use the app for a minute. It should stay quiet. Warnings from libraries are fine. Your own notes are not.
15. Check what email goes out, and who it is from
A welcome email arriving from a machine-generated address with the subject “Test” undoes item 6 entirely. Email leaves the app and lands in an inbox the client keeps.
List every email this app can send: what triggers it, the subject line,
the sender name, the sender address, and what the body says. Show me the
list before changing anything. Then update the sender details and subject
lines to the client's brand, and flag any email still carrying
placeholder text.
Verify: trigger each one to your own inbox and read them on a phone. Check the sender name, the subject, and whether it landed in spam. Spam is a real failure mode here, not a detail.
Put it on paper
16. Write a one-page handoff
Everything you say in the meeting is forgotten by Friday. The page stays. There is a template further down.
Read this whole project and write a one-page handoff in plain English for
a non-technical client, in three sections: what is built and working, what
is deliberately not built yet, and what should come next. No jargon, no
file names, no framework names. Describe what the client can do with it,
not how it is made.
Verify: show it to someone who cannot code. If they can tell you what the app does after reading it once, it is ready.
17. Write down who owns what
Domain, hosting, database, payments. The awkward version of this conversation happens months later, when they want to move and nobody remembers whose card the hosting sits on.
This one is a conversation, not a prompt. Have it before the demo, write it into the handoff, and be explicit about which accounts are in your name and what happens to them if the project ends.
Verify: every row of that table has a real person’s name in it, not “TBD”. The point is not who owns each thing. The point is that a name exists.
18. Document every environment variable, names only
So the next person, including future you, knows what the app needs in order to run.
List every environment variable this project reads, with the file it is
read in and one plain-English line on what it is for. Names only, never
print a value. Mark which ones the app cannot start without, and which
belong to third-party services the client will need their own account for.
Verify: the document contains no secrets. Check twice. Never paste it into a chat, an email thread or a shared document with values filled in. The names are documentation. The values are keys to the building.
19. Record a backup demo video
This one is yours. Wifi dies and phones drop to one bar. A video of the app working plays regardless, and turns a dead demo into a mild inconvenience.
Record your screen doing the main flow start to finish, two or three minutes, narration optional. Save it on the device, not only in the cloud, because a video that has to download is the same problem again. Put one screenshot of the main dashboard in your camera roll too, for the version of this where you have thirty seconds rather than three minutes.
Verify: put the phone in airplane mode and play it. If it plays, you are covered.
20. Prepare the scope document
“Can you also add” is not a threat, it is a buying signal. It only becomes a problem when the answer is an awkward yes. Template further down.
Based on this project, write a scope document with three lists: what is
included in the current agreement, what is explicitly not included, and a
phase two list of things a client is likely to ask for, with a rough
estimate in days for each. Write it for a non-technical reader.
Verify: name the three things this particular client is most likely to ask for. If all three are already on the document with a number beside them, you are prepared.
The walk you do yourself
One hour before the demo, you click every single button yourself. Not a test suite, not a prompt. You, on your own phone, on the real domain, moving through the app like a stranger who has never seen it.
Do it in this order, which is the order a client uses an app in, not the order you built it in.
- Open the link fresh. A new tab, or better, a private window. You have been logged in for two weeks. They have not.
- Sign up as a brand-new user. Not your existing account. The empty version of the app is the one they will meet.
- Do the one thing the app is for, start to finish, once.
- Then do it wrong. Submit the form empty. Type letters into the phone number field. Upload a file that is too large. Go back halfway through. This is what a nervous person in a meeting actually does.
- Tap every item in the navigation. Every one, including the ones you are sure about.
- Hit a URL that does not exist and find your way home from it.
- Log out and log back in. This is a common one to miss, because you are almost never logged out during development.
- Refresh on three different screens, especially any screen you reach by clicking through several steps.
Keep a piece of paper beside you and write down everything that looks off. Do not fix as you go, or you will lose the thread and stop testing. Finish the walk, then triage: anything they will touch in the first two minutes gets fixed now, and the rest goes on the handoff under “what is next”, where it becomes honest instead of hidden.
You will find something. That is not a sign the app is bad, it is the entire reason for the hour. The only version of this walk where you find nothing is the one where you did not really look.
The ten minutes in the room
Before you start. Have the app already open on your phone, logged in as them. Have the backup video one tap away. Ask for the wifi password when you sit down, not when you need it.
The first move. Hand them the phone. “Have a look, and I will explain as you go.” A client holding the phone is participating in something. A client watching you scroll is being sold to. This only works because you did the walk an hour earlier.
When they find something broken, and they might, say what it is, say when it will be fixed, and move on. “Good catch, that one is on the list for this week.” No apologizing twice and no explaining the cause. Clients are usually fine with unfinished. They are unsettled by someone who seems surprised by their own app.
When they ask “can you also add”, three sentences, in this order.
- “That is a good idea.” Because it usually is, and they need to hear that it was heard.
- “That is outside what we scoped for this phase, let me check the document.” Now you open the scope document in front of them. The document does the pushing back, not you.
- “I can price it as a second phase, want me to send that over?” You have just turned an unpaid request into a second invoice without saying no once.
Say all three. Skipping the first sounds defensive. Skipping the third leaves money on the table.
At the end, send the handoff sheet before you leave the table, while you are still sitting there. Not that evening and not tomorrow. It arrives while the demo is still the best thing they saw today, and it is the document their partner reads that night, the person who was not in the room and who will be deciding second-hand whether this went well.
The handoff sheet
One page, plain English, sent the same day. Copy this and fill in the blanks.
# [Project name] handoff
Prepared by [your name], [date]
## What this app does
One line, in the client's own words.
## Where it lives
Live at: [url]
Admin login: [email]
Password reset works from the login page. You can change yours at any
time without me.
## What is built and working
1.
2.
3.
## What is not built yet
This is deliberate, not forgotten.
1.
2.
## What I would suggest next
1. [item], roughly [n] days
2. [item], roughly [n] days
## Who owns what
| Thing | Account is in the name of | Who pays |
| --- | --- | --- |
| Domain | | |
| Hosting | | |
| Database | | |
| Payments | | |
| Email sending | | |
| Error tracking | | |
## What this app needs to run
Names of the services and settings only. I will never send you a password
in writing.
## If something breaks
Email me at [address]. I usually reply within [time].
I get an automatic alert when the app errors, so I often know before you do.
Three rules for filling it in. Write “what is not built yet” specifically, because a named gap is a plan and a vague one is a worry. Put a real number beside every “what is next” item, even a rough one, because an estimate is what turns an idea into a decision. And never leave a row of the ownership table blank, because “we will sort it later” is the sentence that becomes an argument months from now.
The scope document
The document you open in front of the client when they ask for something extra. It works because it existed before they asked. A scope document written afterward is an argument. One written beforehand is just a reference.
# [Project name] scope
Agreed [date], between [you] and [client]
## In this phase
1.
2.
3.
## Not in this phase
Not refused, scheduled.
1.
2.
## Phase two, ready to price when you are
| What | Rough effort | Rough cost |
| --- | --- | --- |
| | [n] days | |
| | [n] days | |
## How changes work
Anything not on the "in this phase" list is a phase two item. I will
always price it and never simply say no. I would rather build it properly
than squeeze it in.
Revisions included: [n] rounds
Delivery date: [date]
Payment: [n]% up front, [n]% on handoff
Two prompts before you close the laptop
The catch-all, the night before:
Read this whole project as if you were a client seeing it for the first
time on a phone, with a brand-new empty account. List everything that
would look unfinished or broken to a non-technical person: blank screens,
placeholder text, missing loading states, default framework styling, test
data, dead links, and error messages a normal person cannot understand.
Order the list by what they would notice first. Do not change any code yet.
That last sentence matters. You want the list, not a hundred edits at eleven at night before a demo.
Then, after you have fixed a few things:
I fixed [what you changed]. Tell me what else in this app has the same
problem somewhere else. List all of them.
Almost every item on this list is a pattern rather than an incident. One screen with no empty state is rarely the only one.
This post is about the ten minutes a client spends holding your app. It will not tell you whether the app is worth building, what to charge, or how to write the proposal that got you into the room. It is also not a launch checklist, because real users arriving at an app is a different set of problems.
The app already works. That is what makes this list feel unnecessary right until it is not. Your client is not evaluating whether it works, because they cannot see that. They are evaluating whether it feels like something real, and that judgment is mostly finished within about ninety seconds of them picking up the phone. Clients tend to forgive missing features. A demo that breaks in their hands is much harder to walk back.
Common questions
- My demo is in an hour and I have done none of this. What should I fix?
- Six things, in order. Deploy to a real domain, replace placeholder data with realistic data, add empty states to the three screens they will open first, open the whole app on your own phone, add a custom 404 page, then click every button yourself. The other fourteen can wait until after the demo.
- Why does an empty screen matter more than a missing feature?
- A client cannot tell the difference between a screen with no data yet and a screen that is broken. A new account starts empty, so the empty version is the first one they ever see. One line saying what goes there, plus a button that starts the action, turns a suspected bug into an obvious next step.
- The client asked for something outside the scope during the demo. What do I say?
- Acknowledge the idea, open the scope document in front of them, and offer to price it as a second phase. The document does the work of saying no, which keeps the conversation about scheduling rather than about whether you are being difficult.
- Do I have to test on a real phone, or is a simulator enough?
- A simulator catches layout problems and misses most of the rest. Real device testing is where you find tap targets that are too small, a keyboard that covers the submit button, and scrolling that only feels wrong under a thumb. Borrow a phone for fifteen minutes if you do not own the one your client uses.
Keep reading
20 things AI leaves out of the login it built you
The gaps AI leaves in auth, from email verification to password reset and sessions, each with a copy-ready prompt and a browser check that proves it works.
Your AI agent is stuck in a loop: 12 prompts that break it
Twelve copy-ready prompts for when your AI agent keeps saying it fixed the bug and ships the same fix, cheapest first, each with a way to tell it worked.
12 security holes AI leaves in your app, and how to close them
A copy-ready prompt to close each of the twelve security holes AI leaves in most apps, and a browser check under each one to prove it actually shut.