CompositionalIT / farmer

Repeatable Azure deployments with ARM templates - made easy!
https://compositionalit.github.io/farmer
MIT License
513 stars 156 forks source link

Semantic versioning proposal #842

Open et1975 opened 2 years ago

et1975 commented 2 years ago

I often find the API has been broken within the same major version. Would it make sense to start using semantic versioning to give the user indication - "broken API ahead"?

isaacabraham commented 2 years ago

@et1975 Yes, we should probably do something like that. Up until now we've been fairly lax on versioning with regards to the API except for two things:

  1. Breaks should be immediately obvious i.e. compile-time, and simple to fix.
  2. We're quite strict around the emitting of the ARM itself - we don't want to change that unless fixing a bug.
stroborobo commented 1 year ago

Hey I've thinking about this, maybe it's too much, since it touches on a lot of stuff.

The ARM templates follow a spec that is versioned, this is currently not represented in the API or package version. Updating the resource type version to support new features seems like a hard decision to make, since it might have unintended consequences, as Farmer is technically targeting a different Azure API, which might behave differently regardless of changes in the JSON. The strictness in changing the ARM output might not be very helpful, since it sets incentives to keep outdated implementations, that might not even work anymore.

What if the builders were versioned according to the spec? Like instead of containerApp { ... } it'd be containerApp.v20221001 { ... } and containerApp.latest { ... }. Updates to the output would be explicit and breaking changes would not be blocked by forced compatibility to previous versions, both in the ARM output as well as in the builder's API.

While this isn't really about semantic versioning, it could help to reduce breakages.

I'm not sure how to structure the builders internally for this, this could either mean a bunch of duplication in the code base or some major changes internally.

I've been toying with the idea of computation expression builders (like builder builders?) to keep the API (mostly?) stable and reduce duplication while enabling these versioned builders. Idk if a lib like it already exists to enable this, but it would be nice if you could be more explicit about the resource version you're targeting. Exactly because things might not work as expected sometimes and you'd need to target a specific one sometimes.

The idea is kind of inspired by how applicative form builders work, not exactly like Fable.Form, but somewhat like it. So you'd reuse/reference field definitions from previous versions and compose them together into a CE, that looks like the one we have today.

I'm not sure if it's possible to build usable CEs at runtime though, so this approach might be a flop. Versioned builders would be neat though imo.

ninjarobot commented 1 year ago

I may have misinterpreted the OP, but I believe this issue is about breakage in Farmer rather than API changes in ARM. It's frustrating to when code breaks just because of a package update.

Usually the builders can be made backwards compatible. This is probably one of my biggest revisions I request when reviewing. Breakage more often comes from complex parameter types that don't use a builder, which means that adding a field to a record will break anyone using it.

We really can't guarantee any backwards compatibility on the resource records that the builders are creating. This should be considered a public but not stable API.

For the ARM API versions, those can nearly always increment without any issue, so long as there is no structural change. It is up to the the resource providers to ensure this, but the ARM team also heavily gates breaking changes. A new ARM API version in and of itself does not change anything at all on the underlying resource. It may expose more options, but the underlying resource would be the same. If we look hard enough, we can find exceptions (ACI subnets is a recent one), but they are rare enough to take on a case by case basis rather.

et1975 commented 1 year ago

I was referring to Farmer DSL changes - adding/removing a required param, changing the type used in the DSL in breaking manner, CE keyword renames, etc. My thinking was as we find better abstractions the breakages are unavoidable.

The breakage happening as result of changing to a new resource provider version are interesting too, but seem different. The guidance from ARM is to keep the api-version on the provisioned resource, which is obviously a concern the DSL library like Farmer should be able to reflect.

ninjarobot commented 1 year ago

It definitely adds a lot of complexity to be able to emit different versions based on features, probably an unsustainable amount. Here's an example where I did this for container groups due to their breakage, and this is difficult to test and maintain. I did that because it was a unique exception, and while it's not elegant, even something cleaner would lead to a huge and tedious testing process to test the ARM resources emitted for different API versions.