Breaking changes in model data structure (the "data" object)

Hello! :slight_smile:

I just noticed some changes on how building elements (columns, walls, …) are saved within the JSON of a model. Must have happened with the update of my connectors or with some recent update of the server.

before:

now:

Regarding this example, this makes sense, and I really like that change. So it’s not about the change itself, but:

→ such changes will break our company’s apps
→ the old models on the server, will not be transformed: We will always have to be able to deal with any (old) data structure and always check which structure the current model has before using it.

This obviously is quite challenging, if all the running, time-critical projects in our company will depend on our working apps.

So my main question would be: What’s the best place to (definitely) get the information about such changes? Also, in advance, so we could prepare for that.
I haven’t found anything about this change here, but for us it’s super important.

Another question … Where is the JSON (the data object) actually created? Within the connectors, or on the server?
… When such changes are coming, we could pause the update of the server, but we couldn’t prevent the colleagues updating their connectors. :confused:

Thanks a lot in advance! :slight_smile:

2 Likes

We know, and we tried being as upfront and helpful about this as possible - see the below post from a bit more than a mo ago. To your direct question, pretty much here and our newsletter. Sorry if you missed this! How would you prefer being informed or getting notified about this? We can see if we can do better on our end :heart:

2 Likes

Repeating Dimitrie’s apology - would love to hear your feedback where else we could notify you of these changes. The Revit release notes and the linked community notice were not reaching you.

Connectors. Both core SDKs (c#\specklepy) take the translations from host app to Speckle objects and transmute that into the json that is passed to the server.

The server makes no changes to the data sent to it, instead it choreographs the versions to keep track of changes.

The commit version structure is also determined by the Connectors.

The unfortunate fail here is that the change was made with app developers in mind. There is now a single way to retrieve data so that your app need not know a version came from a particular authoring application.

2 Likes

Oh, sorry! Yeah, that’s exactly the post I shouldn’t have missed. :innocent:
And such a perfect and thorough one! Thanks!

tbh, I’m a little bit sloppy with following all the (nice) stuff going on here, but I will have a closer look now. Definitely for the breaking changes posts. (I also just discovered the breaking-change tag to filter for those :+1:)
I guess, just an email notification specifically for breacking changes would be even nicer? At least, you wouldn’t have to actively look for it.
Or maybe an own category for breaking changes, so one could subscribe to just those?

Anyways, thanks a lot! I now know where to look! :slight_smile:

PS: And just to repeat myself, I like the described change a lot! Makes so much sense! :heart:

1 Like

Regarding this topic … would it make sense to include the connector’s version number (or even more information) into the commits database table / graphQL output?
Then, it would be a bit more obvious what kind of data structure to expect, and if some code would be able to work with it or not?

Of course, the data structure shouldn’t change that often anyways, but it still might be useful information for the long run. Especially since the old models stay untouched.

The code examples we gave in the post-show that you could defensively code to determine the commit structure by itself and not have to version/app detect. Just the same as feature detection being preferable to browser version code forking.

// The commit object is a Collection which implicitly has the 
// property of an element for all its object data content.

// This adds the new method if the old property is not found.
var walls = myCommitObject["@walls"] ?? myCommitObject.elements
    .OfType<Collection>()
    .FirstOrDefault(coll => coll.name == "walls")?.elements;

In this instance (and it only pertains to Revit data, the myCommitObject["@walls"] would be the expected old model schema where there is a category-named property in the commit => where that is null, either the commit had no walls, or it is in the new form so proceed to find all walls. That example is C# but js/py could be trivially reproduced.

This, in my humble opinion, is preferable to:

if (myCommitObject["applicationVersion"] !=null && myCommitObject["applicationVersion"] < 2.14) {
    // do x
} else {
   // do y
}

not least because we don’t currently store applicationVersion!

We are very sensitive to developer pains (we are also :smiley: after all) and will always favour API changes that deprecate methods and add new ones. We don’t have that option in data shape, so we will work on the messaging being as clear as possible while keeping breaking changes to an absolute minimum. SpecklePromise™

Hey @jonathon !

Yes, I’ve seen your code example and I understand that approach.
In many cases, it’s probably a good strategy to just have a look what’s actually there and handle accordingly and not to assume things based on the version number (and having to know what I am actually able to assume from which version number …). And maybe that’s the way to go! Specifically for your open data strategy.

Looking specifically for (e.g.) walls and specifically for the discussed change in the data structure, this is handled quite quickly the way you proposed.

I feel like this would quickly get more complex, if I had to do this also for columns, floors, stairs, … (and for each expected Revit language!) … and for any single change, which might come (despite the SpecklePromise™ :wink:). Or if I am not looking for anything specific, but for all existing layers/categories.

If I would knew the version number (and what to expect), I could make more “global” decisions up front. I could write seperate blocks/functions/methods/classes for different versions und than have my code a little bit more organized. … or … my app might not even support older models up front. I also could remove those functions easily as one piece, and not piece by piece, once we won’t expect older models on our server anymore.
If-else-ing the version number kind of also self-documents the code.

I think, it’s similar to the applicationSource, which is also relevant to know up front, because this also tells me what to expect and e.g. if my app could even use that kind of model for the desired purpose.

Long story short, I’d currently prefer your second example. :slight_smile:

I anticipated this exact response, and we debated the value of it when we first made it.

In general, we wouldn’t recommend making queries like that, it was intended as a generic example.

That said, a more generic response would properly be:

if (myCommitObject.elements) {
   // Embrace the modern Revit commit shape
} else {
   //Keep doing it the way before
}

Yes, this would be the best solution in that case.

2 Likes