DEV Community

Naz
Naz

Posted on

The scope creep problem isn't the client. It's that you never wrote it down.

Three years into freelancing I did an honest audit of one project. Quoted €4,000 for a marketing site. Delivered in nine weeks instead of five. Actual hours worked: 118.

My effective rate was €34/hour. I'd quoted assuming €80.

The client wasn't the villain. He never asked for anything outrageous. He asked for eleven small things, spread across nine weeks, each of which sounded like nothing.

The requests that actually kill you

Nobody gets destroyed by "can you rebuild the whole thing." That's easy to refuse — it's obviously a new project.

You get destroyed by:

  • "Can we add a blog? Just a simple one."
  • "Can the hero animate on scroll?"
  • "Quick one — can this form email me and go to Mailchimp?"
  • "While you're in there, the mobile menu feels off."

Each is genuinely small. Twenty minutes to two hours. And each arrives while you're mid-task, so the cost of stopping to evaluate it feels higher than the cost of just doing it.

That's the trap. The evaluation is more expensive than the work, so you skip the evaluation. Eleven times.

Why "put it in the contract" doesn't fix it

Everyone says write a better contract. I had a contract. It said "5-page marketing website."

A blog isn't a page. It's a page type, a CMS integration, a template, pagination, and an RSS feed. But "5-page marketing website" doesn't say that, so when he asked for a blog, I had nothing to point at. Arguing would have meant arguing about interpretation, which is a fight you lose even when you win.

Contracts are written to be legally enforceable. They're not written to be referenceable in a Slack message on a Tuesday. Those are different jobs.

What I do now

Three things. None of them are about being tougher with clients.

1. Scope by exclusion, not just inclusion

The list of what you're building is unbounded — every item implies a hundred sub-items. The list of what you're not building is where the money is.
IN SCOPE

5 pages: Home, About, Services, Contact, Legal
Contact form → email notification only
Responsive: 1440 / 768 / 375

OUT OF SCOPE (available as separate work)

Blog or any CMS
Email platform integrations
Scroll/entrance animations
Ongoing maintenance after handover

That "OUT OF SCOPE" block does more work than the entire rest of the document. When the blog request arrives, I'm not interpreting anything. It's already on the list. The conversation is already over and it never became a conversation.

Write the out-of-scope list by predicting what they'll ask for. You already know. You've done this project before.

2. Never evaluate a request in the moment

The reason you cave is that you're deep in a CSS problem and a Slack message arrives and saying yes costs you five seconds while saying no costs you a whole conversation.

So don't have the conversation. Have one response:

Good call — that's outside what we scoped, so let me price it as an add-on and send it over today. Want me to hold it until after launch or slot it in now?

Ten seconds. No judgment call, no confrontation, no "let me check." It's not a no. It's a not for free, and it puts the decision back on them.

Half the time they say hold it until after launch, and after launch they've forgotten they wanted it.

3. Log every request, including the ones you absorb

I absorb small stuff. That's fine — goodwill is real and worth buying.

But I log it:
2026-04-02 Mobile menu tweak 0.5h absorbed
2026-04-09 Hero scroll animation 3h absorbed
2026-04-15 Mailchimp integration 2h absorbed
2026-04-22 Blog (basic) 14h ← invoiced

Two reasons. First, when the fourth request comes, I can see I've already given away 5.5 hours, which changes my answer. Without the log I'd have no idea — each one felt like the first one.

Second, at project close I can say "we added about 20 hours beyond the original scope, roughly 6 of which I covered." Clients are almost always surprised by that number, and it's the single best thing I've found for getting the next project priced correctly. Not as a complaint. Just as data.

The reframe that actually made it work

I spent years thinking my problem was that I was bad at saying no.

My problem was that I was making a judgment call, under time pressure, about a request whose in-or-out status was genuinely ambiguous — because I'd never defined it. Of course I caved. There was nothing to point at except my own opinion, and defending your opinion to a client who's paying you is exhausting.

The fix wasn't a spine. It was a document specific enough that the answer already existed before the question arrived.

The client isn't testing your boundaries. They genuinely don't know where the edge is, because you never showed them one.


I got annoyed enough about this that I built Clarifeed — it generates the scope doc, then checks incoming requests against it and drafts the reply. Built it for me; it's out if you want it. But the system above works fine in a Google Doc, which is where it lived for two years.

Top comments (0)