Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: 0192 Removal of the "constructs" compatibility layer #195

Merged
merged 30 commits into from
Jul 28, 2020

Conversation

eladb
Copy link
Contributor

@eladb eladb commented Jul 9, 2020


By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache-2.0 license

@eladb eladb requested review from RomainMuller and a team July 9, 2020 18:32
@eladb eladb changed the title RFC 0192 Removal of the "constructs" compatibility layer RFC 0192 - Removal of the "constructs" compatibility layer Jul 9, 2020
@mergify
Copy link
Contributor

mergify bot commented Jul 9, 2020

Title does not follow the guidelines of Conventional Commits. Please adjust title before merge.

@eladb eladb changed the title RFC 0192 - Removal of the "constructs" compatibility layer RFC: 0192 - Removal of the "constructs" compatibility layer Jul 9, 2020
@eladb eladb changed the title RFC: 0192 - Removal of the "constructs" compatibility layer RFC: 0192 Removal of the "constructs" compatibility layer Jul 9, 2020
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved

To apply AWS tags to a scope, prefer `Tag.add(scope, name, value)`.

> TODO: should we change to `Tags.of(scope).add(name, value)`?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda feel this would be more consistent with the Aspects API and thus would incur less cognitive load to developers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I like it too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree, Tag.add is the current API, as mentioned here we can consider changing to Tags.of(x).add(), but it's not a breaking change nor required for this migration at this point.

text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved

- [x] Migrate to projen
- [x] Branch to 4.x and release as @next
- [x] Reintroduce `c.node` instead of `Node.of(c)`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda liked Node.of(c) because it keeps the API of Construct/IConstruct super clean. However I don't really love how IConstruct is just a marker interface and it has no bearing in how Node.of(IConstruct) achieves getting the Node from there (which makes it unsafe).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, and that's why I am reintroducing IConstruct.node so it's not a marker interface anymore. Also, not having this.node as a way to access the node is going to break a lot of CDK code.

text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Show resolved Hide resolved
@iliapolo
Copy link
Contributor

iliapolo commented Jul 10, 2020

@eladb Notice: aws/aws-cdk#8645

Should we really have the addInfo, addWarning, ... methods in constructs? These are tied to the aws-cdk implementation, and don't have any meaning outside its context (for example for cdk8s).

Can we move this to @aws-cdk/core? Perhaps its worth extending constructs.Construct in @aws-cdk/core and providing this API, and have users still extend core.Construct rather than constructs.Construct?

Im thinking this intermediate core.Construct will eventually have additional API that is relevant to all aws-cdk constructs, but not to the generic Construct.

EDIT: I think we can disregard this as I see it was addressed in the RFC. I also agree that we should keep the composition model pure to constructs.Construct, and add any domain specific features another way.

text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved

To apply AWS tags to a scope, prefer `Tag.add(scope, name, value)`.

> TODO: should we change to `Tags.of(scope).add(name, value)`?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I like it too.

text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
stage.synth();
```

Consider a design where you mutate the tree in-band (preferable), or use `Lazy`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an example for a Lazy (or whatever we most recommend for "late" bound calculations) implementation of something that would normally be in prepare would be nice.

text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
Elad Ben-Israel added 2 commits July 12, 2020 15:51
@eladb eladb requested review from RomainMuller, iliapolo and a team July 14, 2020 16:14
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Show resolved Hide resolved
text/0192-remove-constructs-compat.md Show resolved Hide resolved
text/0192-remove-constructs-compat.md Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
Elad Ben-Israel and others added 2 commits July 15, 2020 19:14
@mergify
Copy link
Contributor

mergify bot commented Jul 16, 2020

Title does not follow the guidelines of Conventional Commits. Please adjust title before merge.

text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
text/0192-remove-constructs-compat.md Outdated Show resolved Hide resolved
njlynch and others added 15 commits July 27, 2020 08:41
* RFC: 0171 CloudFront Redesign - initial rough ideas

* Iteration #2 of the API, sans the Lambda@Edge functionality.

Still in draft state, but hopefully getting closer.

* Lambda@Edge API v1, plus some of the open questions and other sections filled in.

* Added detailed design section, including before/after examples

* Proper namespacing in the examples, and updated the open questions

* Minor update to force pull request refresh

* Minor changes per feedback; removing the 'draft' tag

* Distribution.for* syntactic sugar

* Removing Distribution.behavior, keeping origin->behavior nesting consistent

* Minor API updates per feedback and lessons learned from implementation

* Added implementation note for capturing behavior ordering
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Comment on lines +560 to +563
To reduce the risk of naming conflicts (e.g. see [Terraform CDK
issue](https://github.com/hashicorp/terraform-cdk/pull/230)) between `node` and
domain-specific APIs, we propose to introduce this API under the name
`construct` (of type `Node`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... let me summarize this: all classes that extend Construct will have a construct property that returns an instance of Node? I would naturally have expected construct to return an instance of Construct (basically, this), and the naming you're proposing strikes me as surprising and counter-intuitive.

Copy link
Contributor Author

@eladb eladb Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property name should be considered in the context of its usage rather than its type because the type is not part of the usage semantics.

Do you find this counterintuitive?

bucket.construct.addDependency(role);

As oppose to:

bucket.node.addDependency(role);

I actually think the former is much more intuitively and reads much better.

We actually have a naming guideline that says that names should not include the type name because it's redundant information in strongly typed environments.

Do you have an alternative suggestion?

Perhaps we can rename Node to something else?

Copy link
Contributor

@RomainMuller RomainMuller Jul 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually have a naming guideline that says that names should not include the type name because it's redundant information in strongly typed environments.

The problem is the name is the base type name (of the receiver) as well as the coined term for sub-types of it. The reason I don't love it is precisely because of this guideline: it looks like redundant information (so why's it there?), but is not (so it's surprising in that sense).


As discussed... I don't feel extremely strong against this. It does feel counter-intuitive to me but I have no idea whether I'm representative of users or not in this respect.

For the record, I think this.construct.addDependency(role) reads super good, but for some reason the magic weakens when the receiver is not this.

Additionally, I cannot come up with a better suggestion for a name... so there's that, too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's a little late, but I don't agree with this rename.

I would expect a field called construct to point to an object that IS a Construct. I like what they did in TerraCDK, constructNode seems like a good compromise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason we have this property is in order to group all the API of Construct under a single namespace so it won't "pollute" the APIs of subclasses, which are oftentimes generated classes in CDKs.

The conceptual mental model is that this is "the construct API". The fact that the type is Node is actually not in line with this mental model.

If I am looking at this from a usage point of view, I think construct makes more sense:

this.construct.id
this.construct.path
this.construct.addDependency(other)
this.construct.addMetadata

This is actually more natural then:

this.node.id
this.node.path
...

Additionally, we actually have a guideline against using the type name in symbol names because type information is an intrinsic part of the API (we prefer handler: lambda.Function over handlerFunction: lambda.Function).

I would rather rename the Node class (maybe ConstructApi) instead of renaming this property.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, after some discussion I think I would have preferred for this member to be called constructTree, as this is the member that consumers use to access the features of the "construct tree" (which is a CDK concept that people are, or should be, familiar with which is distinct from the concept of a "construct").

We get things like:

// All perfectly natural
bucket.constructTree.findChild()
bucket.constructTree.removeChild()
bucket.constructTree.defaultChild
bucket.constructTree.findAll()
bucket.constructTree.children
bucket.constructTree.addWarning()

// Better than what we have now, this name emphasizes the tree traversal involved in these operations
bucket.constructTree.setContext()
bucket.constructTree.applyAspect()
bucket.constructTree.addDependency() 
 
// These now have not such great names, because they apply to the "current node" and don't really have
// strong tree-ish connotations. Don't think any of them are *horrible* but they are certainly not fantastic.
bucket.constructTree.id
bucket.constructTree.path
bucket.constructTree.uniqueId

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think CDK users are required to understand the tree concept. In fact, we have traditionally tried to talk about "scopes" (I.e "the construct is created within the scope of another construct"). From a CS perspective this is indeed a tree, but I don't see how this adds much value to users.

As mentioned above, the primary reason for this property is to group all the construct APIs under an api namespace so they don't pollute or conflict the public api of subclasses, which is the primary programming pattern we use in CDKs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a difference between not needing to know that there is a tree structure involved (until you do), and pretending that the tree structure doesn't exist by studiously avoiding calling it a tree.

You might be under the impression that my argument is one about purity of CS concepts. It is not.

It is about acknowledging the fact that this structure exists, that it underpins the way the CDK works in a way which you can oftentimes ignore, but sometimes is quite relevant and requires people to understand it (see Stages, Stacks, Aspects, LogicalIDs, default children and escape hatches, see tree.json and the VSCode plugin, ...) and that there is a term available which accurately describes the semantics, expectations, and the sort of API that people can expect from this structure that they are already intimately familiar with from other programming tasks.

I think being vague and coy and pretending there is no tree when we all full well know that there is, or using the word "construct" to refer to two distinct concepts, is not going to help users form a mental model.

I'm also not saying they won't be able to. Given enough exposure and enough examples, over time people are going to eventually be trained to write the right code. In that sense, ultimately it doesn't even matter what it's called, be it node, construct, or abc, people will learn to cope.


I think I've laid out my reasoning for why I think it's advantageous to call the beast by its name. It's completely fine if you disagree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks for the effort in explaining.

The plan is to introduce construct and deprecate node over the 1.x branch in short order. This will give us some "bake time" of a few months until we completely remove node in 2.x. This will allow us to get feedback from users in case the mental model is confusing or insufficient.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just stumbled upon this - thanks for making the effort of changing the name 👍

RomainMuller
RomainMuller previously approved these changes Jul 27, 2020
@mergify mergify bot dismissed RomainMuller’s stale review July 28, 2020 07:17

Pull request has been modified.

@eladb eladb merged commit 1584041 into master Jul 28, 2020
@eladb eladb deleted the benisrae/remove-constructs branch July 28, 2020 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants