What to Include in a Software Specification

There is no agreed standard for what goes in a software specification. Here is a practical anatomy: the sections that actually remove ambiguity, from context and constraints to C4 and Mermaid diagrams.

All posts

There is no agreed standard for what goes in a software specification. The closest thing is the old IEEE 830 recommended practice, long since folded into ISO/IEC/IEEE 29148, and almost nobody writes specs that way anymore. So in practice "spec" means something different on every team: a Confluence page here, a Google design doc there, a Notion database, a stack of Jira tickets, or a Slack thread someone screenshotted. We have inherited all of those, and the problem is never the format. It is that the parts which actually remove ambiguity are the parts most often left out.

This is a practical anatomy of a software specification: what to include, what each part is for, and the specific way each one usually goes wrong.

Why there is no standard, and why that is fine

The standards exist. They are just heavy. IEEE 830 and its successor describe a thorough requirements document, genuinely useful as a checklist, but written for a world of signed-off requirements and formal handover that most teams have left behind. Around them grew lighter conventions: RFCs, Google-style design docs, behaviour-driven user stories, architecture decision records. Everyone borrows a bit of each.

The lesson is not to pick the one true template. A spec is defined by its job, not its format, and the job is to remove ambiguity for whoever reads it next. The right question is never "is this the correct document type", it is "what does the reader need that they cannot get from the code". Everything below answers some version of that. Take the parts that earn their place on your project and drop the rest.

Start with the frame, not the feature list

Most weak specs jump straight to a list of things to build. The build list is the easy part and the least durable. Before it, a spec has to establish why the work exists and what would make it good. Five things carry most of that weight.

Context is the background: who this is for, what problem they actually have, and why now. It is the first thing a new engineer or an AI agent has to reconstruct if you leave it out, and they will reconstruct it wrong. One honest paragraph here saves a week of misaimed work. The failure mode is a spec that describes a solution to a problem it never states, so nobody downstream can tell whether the solution still fits when the problem moves.

Constraints are the hard limits: the stack you must use, the platform you ship to, the budget, the deadline, the compliance rules, the legacy system you have to integrate with whether you like it or not. Constraints are what make a spec real rather than a wish list, and they are usually the thing that actually decided the design. "Must run against the client's existing on-premise SQL Server", "must work offline on the warehouse floor", "personal data cannot leave the EU" each rule out whole categories of solution. We have watched more designs die on an unstated constraint than on a bad idea.

Goals are what you are trying to achieve, written as outcomes, not features. "Reduce the time it takes a client to approve a report from days to minutes" is a goal. "Add an approve button" is a feature wearing a goal's clothes. The test is whether you could hit it a different way; if the only thing that satisfies it is the exact item named, it is not a goal, it is a task. Goals are how you tell, months later, whether the features you shipped were the right ones.

Trade-offs are the most skipped section and often the most valuable. They record what you deliberately chose and what you gave up: speed over flexibility, build over buy, a managed service over control, this platform over that one. This is the reasoning that evaporates the instant a decision becomes code, and that someone pays days to reconstruct later, usually by re-running a debate the team already had. The shape is simple:

We chose <option> over <alternative> because <reason>,
accepting <the cost we are taking on>.

Five minutes to write, and a saved argument every time someone new asks why.

Success criteria are how you will know it works, written so they can actually be checked. "Approvals are fast" is not a success criterion. "95% of report approvals complete in under two minutes, measured over a week" is. Where you can, make them measurable; where you genuinely cannot, make them at least observable. This section is also the bridge to your tests, because a success criterion you cannot verify is just a hope with a number next to it.

The build: features, designs, decisions, tests

With the frame in place, describe the thing itself from four connected angles.

Features are what it does, and the craft is in the acceptance criteria: specific enough that two people would agree on whether they are met. Compare:

Vague:  The user can filter the reports table.

Better: Given a user on the reports list, when they select a status
        filter, then only reports with that status are shown, the URL
        updates so the filtered view can be shared, and an empty result
        shows the "no reports match" state rather than a blank table.

The second names the happy path, the shareable-state requirement, and the edge case. Given/When/Then is not the only shape, but it forces all three.

A solution design is how you intend to build it: the components, the data, the interfaces, the shape of the change. Architecture decisions record why it is built that way, with the alternatives considered, so the reasoning outlives the people who made it. Tests are how you verify it, linked back to the features and success criteria they cover.

None of these is novel alone. The value is in keeping them connected. A feature that links to its design, its decisions and its tests is a spec that can tell you what breaks when something changes. Four separate documents are four things that quietly drift apart.

Say what you are not building

The most useful sentence in many specs starts with "out of scope". Ambiguity is not only what you failed to specify; it is everything a reader might reasonably assume you meant. An explicit non-goals list, "this release does not handle multi-currency", "no bulk import yet", "mobile is view-only", removes a whole class of wrong assumptions, scope creep, and "I thought that was included" conversations at handover. It costs a few lines, and it is the part an eager AI agent needs most, because it will otherwise cheerfully build the thing you never asked for.

A spec usually needs a picture

Prose is bad at structure. At some point most specs need a diagram, and there are two separate questions: what to draw, and how to keep it from rotting.

For what to draw, the most useful convention we have found is the C4 model, from Simon Brown. It diagrams a system at four zoom levels, context, containers, components and code, and the discipline is only zooming in as far as you need. Most specs are well served by the top two: a context diagram showing the system and the things around it, and a container diagram showing the major moving parts. That alone avoids the usual failure, one enormous boxes-and-arrows picture that shows everything and communicates nothing.

For keeping it alive, prefer diagrams as text. An exported PNG is correct the day you draw it and wrong soon after, and nobody reopens the design tool to fix it. A Mermaid diagram is plain text that lives next to the words it illustrates, versions and diffs like code, and renders in Markdown and most modern tools:

sequenceDiagram
  Client ->> API: POST /reports/:id/approve
  API ->> DB: update status = approved
  DB -->> API: ok
  API -->> Client: 200 approved

A diagram that travels with the decision it explains has a chance of staying true. One exported to an image does not.

There is no certificate for a correct spec

Because there is no standard, there is also no external mark that says a spec is done. The only honest test is whether someone who was not in the room, a new hire, a client, an AI agent, can build the right thing from it without coming to ask you. If they can, the spec is good in whatever format it is in. If they cannot, no template will save it.

That test is what the structure we use in Specsource is built around: context, constraints, goals, trade-offs and success criteria framing the features, designs, decisions and tests, all connected so the answer to "why is it like this" is never more than a link away. The format matters far less than the habit of writing down the parts the code can never tell you.

Frequently asked questions