Why bids for the same project differ by multiples: how to write a software requirements spec
If you send the same document to five vendors and the bids come back several times apart, the spread is rarely about pricing policy. It is about five teams estimating five different projects. A requirements spec is not there to tell developers how to write code. It exists to record which outcome you want, how anyone will confirm that outcome happened, and what sits outside the job. Leave those three out and the risk does not disappear, it just becomes invisible: the bid comes in low and the invoice grows later, line by line, under the heading of change requests. The useful part is that most of what makes a spec comparable is not technical. It is sentence-level discipline.
The real variable in a bid is uncertainty
Every vendor reading your document makes a silent decision at each vague sentence: assume the bad case and add a risk premium, or assume the good case and bid low. The optimists win the deal. Then both sides start arguing about whether "user management" included the role configuration screen.
That is why spec quality is not a document-aesthetics question, it is directly a question of whether bids can be compared at all. We covered the field research on how the information you hand over shapes the number you get back in the estimation post. The requirements-side version of that finding: the less you write down, the more the number you receive describes the vendor's assumptions instead of your project.
What a single requirement looks like
ISO/IEC/IEEE 29148:2018, the international standard for requirements engineering, lists nine characteristics a single requirement should have: necessary, singular, verifiable, unambiguous, complete, feasible, correct, appropriate in its level of abstraction, and conforming to an agreed format. It also offers a sentence pattern built from condition, subject, action, object and constraint of action. In practice that reads like this: "When an order is placed, the system sends the order confirmation email to the customer within 60 seconds."
The two items teams skip most often are singular and appropriate. Singular means one requirement per sentence. Write "the system generates the report and emails it" and you have created a delivery that can be half done at acceptance time. Appropriate means not dictating the solution. "PostgreSQL 16 will be used" is not a requirement, it is a design decision, and putting it in the spec disqualifies the vendor who had a better answer. Real constraints do belong in the document (the existing ERP only runs on MSSQL, for instance) but they go in a constraints section with the reason attached. There is a reassuring detail in 29148 too: the standard accepts user stories in place of formal shall statements. The format is open. Verifiability is not.
Word-level smells
Henning Femmer and colleagues, in a paper published in the Journal of Systems and Software, inverted the 29148 characteristics into nine "requirements smells" that are easy to find in text. They make a fast scanning list: superlatives (fastest, best possible), comparatives (more usable, improved), subjective language (user-friendly, modern, intuitive), loopholes (if necessary, as appropriate, where possible), ambiguous adverbs and adjectives (quickly, approximately, sufficient), negative statements, vague pronouns (this, they), open-ended non-verifiable terms (etc., and so on), and incomplete references (which document, which version).
You can compress the whole list into one question per sentence: how would I measure that this happened? If there is no answer, what you have is a wish, not a requirement. "The interface will be user-friendly" survives only in the memories of the people in the room, and at the acceptance meeting the person who read the contract more carefully wins that argument.
Draw the line between mandatory and preferred yourself
Standards drafting is strict about this. Under ISO/IEC Directives Part 2, "shall" expresses a requirement from which no deviation is permitted if you want to claim conformance, "should" is a recommendation, and "may" is permission. Most specs never make that distinction. Every line arrives in the same voice, the vendor treats all of them as binding, and ten nice-to-haves inflate the price.
The fix takes a paragraph. Put a short glossary of verb forms at the front of the document and use it consistently: sends, provides, validates for the mandatory items, is preferred for recommendations, may for permissions. If you also want priority labels, attach them to each item rather than keeping a separate table, because the separate table goes out of sync at the first revision. We wrote about why priority lists alone do not hold the line in the MVP scope post.
A quality requirement without a number is not a requirement
Functional items usually get written down in some form. Quality attributes tend to end at "the system will be fast and secure", which cannot be measured and therefore cannot be accepted. The 2023 revision of ISO/IEC 25010 gives a good set of headings here: functional suitability, performance efficiency, compatibility, interaction capability, reliability, security, maintainability, flexibility and safety. In that revision usability was renamed interaction capability, portability became flexibility, and safety was added as a characteristic of its own.
Walk those nine headings and for each one either write a measurable target or state that no target is defined for this project. The second answer is legitimate; what matters is that the decision is written down. In concrete form: "the product listing page responds within 800 ms at p95 with 200 concurrent users", "monthly availability of 99.5 percent excluding planned maintenance", "restore from backup within 4 hours". For the mechanics of choosing and measuring those targets, see the SLO and error budget post. Accessibility belongs in this group too, and in some markets it now has a legal floor; the dates and conformance levels are in the WCAG post.
Acceptance criteria start where the requirement ends
If you do not write down when an item counts as done, you have deferred that decision to delivery day. The most practical pattern is the triple that spread through Dan North's 2006 article "Introducing BDD": given a state, when an action happens, then this outcome. North and business analyst Chris Matts pushed the pattern toward business analysis for exactly this reason, since the same three lines act as a test case for the developer and an acceptance sentence for the business.
An example. Given a cart containing an out-of-stock item, when the customer moves to checkout, then that line is flagged with a warning, no order is created, and the remaining items stay in the cart. Three lines, one acceptance decision. Most specs describe only the happy path, and cost accumulates at the edges. Writing down what happens when stock runs short, a payment stalls halfway, or the same form is submitted twice closes dozens of future support tickets in advance.
Where estimates go wrong most: integrations and data
Integrations are usually the weakest section. "Will integrate with the ERP" can mean a week or three months depending on who is reading it. Most of that uncertainty disappears if each integration gets these lines: which system and version, who grants access, whether a test environment exists, who supplies the credentials, what the rate limits and quotas are, which direction the data flows, and what should happen when the other side stops responding.
You may not know all of those answers, and writing down what you do not know is information too; it splits the bid into a discovery phase and the rest. Data deserves the same treatment. If records are moving from an old system, the spec should carry the volume, how many years of history are in scope, who owns data quality, and who decides what happens with duplicates. The way old data semantics turn into a real budget line is covered in the incremental modernization post.
The out-of-scope page earns its keep
Listing what will be built is easy. Listing what will not be built takes experience. Sentences like "no mobile app in this scope", "multilingual support is not in the first release", "accounting integration is phase two", and "only the 12 reports listed below move over, not all 60" settle most of the arguments that would otherwise happen at acceptance.
Put the work that lives at the edge of the contract on that same page: who runs user training, who enters content and product data, who is on call on go-live day, and how long defect fixes are free. When these are missing, both things happen at once. The vendor does not price them and the client assumes they were included.
Do not ban change, write down how it works
Freezing scope is not a real option. The spec was written on a particular day, the need will move, and your own thinking will move as you learn. What belongs in the document is not a prohibition but a route: who receives a change request, how quickly it gets priced, which size of change is absorbed into the existing scope, and whose signature approves it.
What that mechanism costs depends on your contract model. Under fixed price every change is a negotiation; under time and materials it is a prioritization call. We compared how the two models distribute risk in the fixed price versus time and materials post.
The definition of delivery covers more than screens
What you accept is more than a working interface. A spec should name the full delivery: how source code is handed over and how rights transfer, which automated tests are expected and at what level, environment setup, database schema and migration files, a runbook, monitoring and alerting, and a handover session. If you need contract language for the rights part, the source code ownership post covers it, and for framing test expectations see the test automation post.
The single most useful acceptance item you can add here: a new developer taking over the project can bring the environment up and run the test suite using only the documentation in the repository. Teams that make this a criterion do not spend their first month after handover doing knowledge archaeology.
Drafting with a model helps, within limits
Language models genuinely solve the blank-page problem. They produce a skeleton, remind you of headings you forgot, and turn scattered notes into numbered items. What they cannot do is know your processes. The recurring flaw in generated drafts is sentences that look verifiable but contain no number from your business.
So use the draft, then run it through the smell list above and write your own figure next to every quality item. For the same discipline applied to code, see the reviewing AI-generated code post.
A first step that fits into this week
If you already have a spec, make two passes over it. On the first pass mark every sentence you cannot verify. On the second, add either a number or an acceptance criterion to each marked sentence; the ones where you can do neither belong in the out-of-scope list or in a discovery phase. Then write the one-page out-of-scope section, which is usually the fastest-paying part of the whole exercise.
If you have no spec yet, start with 20 sentences describing outcomes, send it to your shortlist marked as a draft, and ask each vendor for a list of questions instead of a price. The questions measure two things at once: the gaps in your document, and whether the vendor actually read it. The bid you should worry about is the one that arrives with no questions attached.
Need help with this topic?