How to Scope Non-Goals in Technical Specs
The biggest predictor of whether a project ships on time isn't the goal list — it's the non-goal list. Teams that write explicit non-goals finish roughly on schedule. Teams that don't end up arguing, three weeks in, about whether something was always in scope. Every project I've watched ship late had a non-goal section that was either missing or one line long.
Review Note
Reviewed May 6, 2026. This article is now part of the public topic path for the Spec-First Development Hub. It was rechecked for concrete examples, internal links, and indexable metadata before returning to the sitemap and feed.
What a non-goal actually is
A non-goal is a specific thing the current work will not do, even though a reasonable person might expect it to. The last clause is the important part. "We won't solve world peace" isn't a non-goal — nobody expected you to. "We won't handle partial refunds, even though the full refund flow looks similar" is a non-goal, because someone will try to sneak it into scope two weeks from now.
Good non-goals read like commitments, not aspirations. "In this release" or "in this milestone" should be implicit. The test is: if someone asks during implementation "hey, should we also do X?" — is the answer already in the document?
Where non-goals come from
Most non-goals aren't invented. They're extracted from conversations that already happened. Sources I mine for them:
- Scope debates during kickoff. If the team spent ten minutes deciding "we'll do A but not B," B is a non-goal. Write it down verbatim.
- Adjacent user stories. If the feature interacts with an existing flow, the interactions that aren't changing are non-goals. "Login flow is unchanged; this spec does not modify session lifetime."
- Tempting optimizations. The "we should also" moments. "We are not optimizing the N+1 query in list view. That's a separate ticket."
- Dependencies you're not taking on. "We are not introducing a new message queue; this feature reuses the existing HTTP path."
The practice is: every time you notice yourself saying "oh but not that," write it down. That's your non-goal list.
The three shapes I use
Not every non-goal reads the same. I pattern them into three shapes depending on why they're not in scope.
Deferred
The thing is real, we'll probably do it, but not now.
Non-goal: Partial refunds Reason: Out of scope for v1. Planned for v2 (tracked in JIRA-1234). The full refund flow will be implemented in a way that allows later extension without restructuring the refund table.
Deferred non-goals need a forward pointer. Without one, they come back in a scope-creep argument.
Rejected
We considered it and decided no.
Non-goal: SMS-based password reset Reason: SMS delivery has a 3% failure rate in our current provider's geography, incompatible with the 99% delivery expectation for password reset. Decision is final unless we change provider.
Rejected non-goals prevent re-litigation. Include the reason or someone will revisit it during implementation.
Out of domain
The thing belongs to a different system or team.
Non-goal: Auth token refresh logic Reason: Owned by the identity team. This feature consumes the existing refresh behavior unchanged. Any refresh bugs go to them.
Out-of-domain non-goals prevent accidental cross-team work. Name the owning team so escalation is clear.
How many non-goals is enough
I aim for 3-6 non-goals per mid-sized feature. Below three, you probably haven't thought about adjacent scope. Above six, you're either listing things no one would have asked for, or the feature is too ambitious.
Quick sanity check: read your non-goals back and ask "would anyone actually have wanted this?" If the answer is no, delete it — it's noise. If the answer is "yes, and they're going to ask during implementation," keep it.
Non-goals and acceptance criteria interact
A subtle point: if a non-goal is "we don't support X," your acceptance criteria need to cover what happens when someone tries X anyway. It's not enough to say "we won't build partial refunds." The spec also needs: "When a user attempts a partial refund via the API, the endpoint returns 400 with error code 'unsupported_refund_type' and a link to the docs."
Non-goal without a handling AC = user gets a 500 and a support ticket. Non-goal with a handling AC = user gets a clear rejection and bounces off cleanly.
When non-goals need to move
Sometimes a non-goal becomes a goal mid-project. That's allowed, but the process matters. The rule I use:
- Scope expansion requires explicit re-signoff from the same reviewers who approved the spec.
- The spec document is updated — non-goal deleted, AC added — before implementation starts on the new scope.
- The timeline is explicitly revisited. Moving a non-goal to a goal without adjusting the date is where deadlines die.
Language patterns that work
Phrasings I've found keep non-goals from being ignored:
- "This release does not include X." Starts with action, commits immediately.
- "X is deliberately out of scope for this work. X is tracked separately at [link]." Points to where it lives instead.
- "We considered X and decided not to do it because Y." Leaves the reasoning visible for future reviewers.
Phrasings that get ignored:
- "X might be added later." Soft, invites scope creep.
- "X is not a priority." Priorities change; commitments don't.
- "We'll see how far we get." Not a non-goal. Not a plan.
The meta-non-goal
One I always include near the top of a spec:
Meta non-goal: This spec does not describe implementation. Architectural choices and data structures are engineering decisions, not product decisions. Changes to the architecture do not require spec changes unless they alter observable behavior.
This prevents the spec from becoming a design doc, which is how specs balloon to 30 pages and stop being useful as review artifacts.
The bottom line
Non-goals are a cheap way to buy scope discipline. They cost five minutes to write and save the team an argument every week of implementation. If your spec has a goal list but no non-goals, it's only half done. Every time I've skipped this step, I've regretted it in the second week.
Review drill
Use the non-goals section to end scope arguments early. A good non-goal is not a polite "later"; it names the work that is excluded, why it is excluded, and what would reopen the decision.
- Boundary: write the exact user flow, API behavior, data migration, or UI state that is out of scope.
- Reason: tie the exclusion to risk, timeline, dependency, or product priority instead of leaving it as preference.
- Revisit trigger: state the metric, customer request, release phase, or dependency completion that would move the item back into scope.
After review, copy the strongest non-goals into the ticket or release plan so implementation does not quietly absorb them.
Example: "multi-currency settlement is out of scope" is useful only if the spec also says single-currency assumptions remain, invoices store currency explicitly, and FX handling needs a separate migration plan.
Worked Review Example
For a reporting dashboard, "custom report builder is out of scope" is still too loose. Say the release includes fixed filters only, saved views are not supported, CSV columns follow the current export contract, and ad hoc SQL-style grouping needs a separate data-model review. That lets design, backend, and QA reject scope creep with the same sentence.
Release Note Example
In the release checklist, restate the highest-risk non-goal in operational language. For example: "This release does not migrate historical exports. Existing CSV files keep their current column order, the new report_version field applies only to exports created after deployment, and support should route historical-export requests to the existing manual process." That prevents a non-goal from being lost between the spec, QA plan, and support handoff.
API Boundary Example
For an API change, write non-goals with the same precision you use for the contract. "No webhook changes" should become: "This release does not add a new webhook event, does not change the invoice.updated payload, and does not require consumers to store tax_region. Partner notification, schema versioning, and replay behavior are tracked in API-217." That wording gives backend, QA, support, and partner teams the same boundary.
During review, check that the non-goal matches the test plan: no new schema migration, no new response field, no new retry policy, no new dashboard metric, and no hidden feature flag that changes public behavior.
Topic Path
Read the hub first, then use these adjacent examples and templates to place this article inside the full workflow.
Keep Reading
Editorial Note
- Author details: Spec Coding Editorial Team
- Editorial policy: How we review and update articles
- Corrections: Contact the editor