Confession: the worst scope creep I ever dealt with was my own fault, and it started with the words “sure, that’s a quick one.”
It was a Laravel admin panel for a logistics company. Fixed price, six weeks, clear spec. Around week three the client asked if the export button could also email the CSV. Quick one. Then could the email go to a list of addresses. Quick one. Then could the list be managed in the panel. Then could those users get their own login with read-only access. By week eight I had built a small user management system, a notification layer, and a permissions model, none of which were in the contract, and I was the one who’d said yes every time.
I’ve been freelancing and running small projects for long enough now that I’ve stopped thinking of scope creep as something clients do to you. It’s something that happens in the gap between what was written down and what everyone assumed. This post is about how I handle that gap now: what I put in contracts, what I say in the moment, and the one habit that fixed most of it.
Scope creep is mostly a definition problem
The standard definition, the one you’ll find in the Atlassian guide to scope creep and every project management textbook, is uncontrolled growth in a project’s requirements after the project has started. That’s accurate and almost useless, because it makes it sound like a thing with a clear start line.
In practice the growth is never uncontrolled from the client’s point of view. Each request is small, reasonable, and obviously related to the thing you’re already building. The export button emailing the CSV is a two-hour job. The client isn’t trying to get free work. They’re seeing the software for the first time and having ideas, which is what you’d want from an engaged client.
The problem is that “related to the thing you’re building” and “in the thing you agreed to build” are different sets, and if the contract doesn’t draw the line, the line gets drawn by whoever is more uncomfortable saying no. That’s usually the developer.
So the first fix isn’t a script for pushing back. It’s writing the scope down in a way that makes the line visible to both of you before anyone has to be the bad guy.
What goes in my scope document now
Every fixed-price project I take gets a scope section that has two lists, and the second one matters more than the first.
The first list is what’s included. Screens, features, integrations, whatever the unit of work is for that project. Pretty standard.
The second list is what’s explicitly not included, and it’s specific. Not “additional features are out of scope”, which is legally fine and practically meaningless. It says things like: user roles beyond admin and viewer, email notifications of any kind, data import from the old system, mobile layouts for the admin panel. I write this list by imagining the project is done and asking what the client will ask for in the first week. I’m usually right about half of it.
The point of the second list isn’t to refuse those things. It’s to make the conversation, when it comes, sound like “that’s on the not-included list, so let’s price it” instead of “well, I didn’t think that was included.” The first sentence is a fact both parties signed. The second is an argument.
I also add one sentence that has done more work for me than any other: changes to scope are welcome and will be quoted separately before work begins. Welcome is the important word. It tells the client that asking is fine, and that asking has a process.
The five-minute rule
Here’s the habit that fixed most of it. When a request comes in mid-project, I do not answer yes or no in the same message. I answer with an estimate.
“Emailing the CSV: that’s about two hours, so roughly 250. Want me to add it to the change list?”
This does several things at once. It makes the cost visible, which is the entire problem with small requests: the client can’t see the cost, so to them it’s free. It gives them a real decision to make rather than a favour to accept. And it keeps my tone neutral, because I’m not saying no, I’m saying here’s the price.
About a third of requests disappear at this point. The client sees 250 and decides emailing the CSV can wait. Another third get a yes and go on the change list. The remaining third turn into a conversation, which is where the actual project management happens.
What I don’t do anymore is absorb the two hours to be nice. I did that on the logistics project maybe fifteen times, and fifteen two-hour favours is nearly a week of unpaid work. The client didn’t know they’d received a week of free work, because I never told them. That’s not generosity. That’s just bad bookkeeping with extra resentment.
When the request is actually a spec bug
Sometimes the request isn’t creep at all. Sometimes it’s the client noticing that the spec was wrong, and if you’d noticed it during scoping you’d have included it.
The logistics panel had a reporting screen. The spec said “monthly report of deliveries by region”. When I built it, the client said, reasonably, that they also needed it by driver, because that’s how they actually ran their operation. Nothing in the spec said driver. But the report as specified was useless to them, and a useless report was never what either of us intended.
I treat these differently. If the spec was ambiguous and the client’s reading is reasonable, I eat it. If the spec was clear and the client wants something different, it’s a change. The test I use is: would a competent third party reading the spec have built what I built? If yes, the client is asking for a change. If they’d have asked a clarifying question I failed to ask, that’s on me.
I still tell the client which bucket it landed in, either way. “This one’s on me, the spec should have caught it” costs nothing and buys a lot of goodwill for the next time I quote a change. This is also where I’ve learned to be honest about my own mistakes rather than quietly fixing them; I wrote about a much more expensive version of that lesson in the post about the refund bot that cost me a client.
Retainers make this easier, projects make it harder
One structural note. Almost all of my scope creep pain has come from fixed-price projects. On retainers, where the client buys a block of hours per month, scope creep mostly stops being a concept. A new request is just a new item in the queue, and the question is what it displaces, not whether it’s free.
That doesn’t mean retainers are always right. Clients with a defined one-off build want a fixed price, and I’d want one too in their position. But when a project turns out to be the first of many, I now suggest moving to a retainer early, sometimes before the first project is finished. The client gets a predictable bill, I get a predictable month, and the “is this in scope” conversation is replaced with “where does this sit in the priority list”, which is a much better conversation to be having.
If a client resists a retainer but keeps asking for changes, that’s information. The Asana article on scope creep lists unclear priorities as a leading cause, and in my experience the client who wants both a fixed price and an open feature list hasn’t decided what the project is yet. Better to find that out in week two than week eight.
What to change this week
If you’ve got a project in flight right now, do one thing: open a shared document called “Change list”, put a table in it with columns for request, estimate, status, and date, and send the link to your client with a one-line note that says you’ll log any new requests there so nothing gets lost. Then, the next time a request arrives, put it in the table with an estimate before you do anything else.
That table has turned more awkward conversations into boring ones than any contract clause I’ve written. The client can see what they’ve asked for, what it costs, and what they’ve approved. You can see how much unpaid work you were about to do. Nobody has to be the bad guy, because the table is the bad guy, and it’s very polite about it.
If you’re setting up your first few contracts and want to see how I structure the scope section on real projects, the work page on my site has a few case studies where I’ve been fairly open about what was in and out of scope, and what it cost when I got that wrong.