I have been learning about terraform-plugin-framework, and trying to add a missing feature.
I was surprised to find, that despite being a newly written library, it seems to have extreme levels of copy/paste, with no obvious sign of code generation in use to maintain control of the repetitive code.
I took as a sample, a particular line of documentation comment text, In Terraform 1.2.7 and later, this warning diagnostic is displayed, and searched the codebase - this comment is duplicated 48 times throughout the project.
Imagine the pain of making and reviewing a PR, if a piece of documentation like this needed to be updated in any way!
DRY (Don’t Repeat Yourself) is a fairly well-known programming principle, which terraform-plugin-framework appears to be vigourously rejecting - it makes me worry about whether it can be a maintainable project in the long term.
Is anyone able to share some insight into why it is the way it is?
Hi @maxb Thank you for raising this topic. As the author of a lot of that code duplication, I can hopefully provide you the insight you are looking for here.
The Go documentation comment you mention here is present in the schema handling and its important from the maintainer’s perspective that the Go comments are fully realized for every type and field, so it works well with the Go package documentation and editor/LSP support for developers to easily access the information.
The history here is that originally in the framework’s design, there was a single tfsdk.Attribute type which was an overloaded abstraction for representing an attribute across all the provider schema concepts (provider attribute, provider meta attribute, resource attribute, and data source attributes), very similar to terraform-plugin-sdk. This has always been relatively problematic for provider developers since not all behaviors and capabilities should be code-able for each concept. To address this as part of Consider Splitting tfsdk.Attribute into datasource.Attribute, provider.Attribute, and resource.Attribute · Issue #132 · hashicorp/terraform-plugin-framework · GitHub, the schema handling abstraction was split into individual Go packages to align only the available capabilities for each concept. Due to pre-1.0 feedback, the attributes were further split to be typed implementations of an interface (e.g. StringAttribute implementing Attribute interface), which caused a larger repeatable surface area. To provide some additional hidden context, this effort was being completed quickly before a self-imposed 1.0 release timeline in December, since getting the breaking changes in was most important at the time.
So with all that said, this particularly duplicated Go code for schemas is probably less than ideal from a framework code update standpoint at the moment, but its logic is quite simple in each case meaning that it probably is okay for Go code generation or potentially generics. I’ve developed similar code generation within terraform-provider-aws when I was maintaining that project, so its not for lack of knowledge of how to do it, just more relating to the development time aspect. The main “challenge” for this area of code is that we do want to ensure that every schema concept (provider versus resource, etc.) is able to customize its Go comments since Terraform may apply different behaviors in each concept. Not difficult, just something to consider.
The map and group block nesting modes are likely not implemented wholly, or correctly, within Terraform core since no supported higher level SDK enabled providers to include them so there is limited testing of configuration, plan, and state handling for them. These issues could be practitioner-facing panics. Given that these fixes would require practitioners to update to latest Terraform version releases, this limits the utility of enabling the functionality when equivalent and preferable functionality exists. If I’m remembering correctly, map nested attributes have not had major issues in Terraform version 1.0.3 onwards.
This decision was in coordination with the Terraform maintainers to not enable them for the reasons above, even though as you found out it would be relatively straightforward in the framework code to enable them.
Is there functionality you are looking for that is not available with map nested attributes?