Hey, designing a file format or network protocol? Let me give you one tip that I keep hearing people call unnecessary, and I keep coming to projects and having to work around it:

Add a simple SemVer version number.

Somewhere right at the start of the file, in the first message, or included in every JSON endpoint's response.

Nothing fancy, just a major and minor number. If the major is bigger than your client's, tell the user to update. Remember to increment the minor with each change.

Someone will inevitably introduce a bug, and you'll be happy that you can check the version to activate a workaround.

Also a good idea: Add an identifier (can just be an int) for each client/server. So you can say "oh, this is version 1.8 of server type 220, let's not transmit that one new field that they implemented wrong".

@uliwitness I see a lot of folks do this reflexively, and it often doesn’t work out the way they hope. You need to be clear whether it’s a protocol version or an implementation identifier. And then you need to make sure that folks use it correctly, which is really hard to do. I’ve seen plenty of servers just fall over when you start a connection with a version that’s beyond what they expect.
@uliwitness IMO a better option is to define an extension mechanism, where each extension carries a flag that means “If this is set and you don’t understand this type of extension, you need to stop immediately.”

@justkwin I've found it easier to get the build system to bump the minor version, but agreed, if people change the schema and your version number doesn't get bumped (or worse, make an incompatible change without that), then this of course doesn't fix things either.

But IMO, just having a version number that gets bumped most of the time helps a lot. Not having it doesn't help at all.

@uliwitness @justkwin To us, I have found the best way is to have the app platform and build number in the user-agent.

The build number will always be updated with a new version. It is really rare that we have to use it but when we do it is a solid way to distinguish.

It is not pretty. A “protocol version” seems better, more clean, “correct”, but the build number more fool-proof in the rare case it is necessary.

@gahms @justkwin Definitely a good approach. As much as you can automate it, do it. But just *having some version information* in your protocol can help so much with working around the inevitable issues.

@uliwitness Those that make the claim that implementing cross-version support is unnecessary are probably best ignored.

I omitted that in a long-ago design, and had to hack around the mess when protocol changes were first needed.

A familiar implementation of this requirement is TLS, too.

@uliwitness technically this isn't "semver", although I think this is a more coherent and consistent way to do versioning than what the actual semver spec says

The distinctions between bugfix, new feature and breaking change are too wooly. A bugfix might break something. A new feature might fix a bug, etc.

Having two numbers that simply mean "backwards compatible change" and "non backwards compatible change" is a lot easier to reason about

@uliwitness of course, it still doesn't help with the scenario where you inevitably make a backwards incompatible change without realizing, and ship it as a minor bump, but no versioning system can solve that