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

feat_: use a single content-topic for all community chats #5864

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

chaitanyaprem
Copy link
Contributor

@chaitanyaprem chaitanyaprem commented Sep 23, 2024

This PR is first step i moving towards using a single content-topic for all community chats and most of the control messages.
Once this is released , in a subsequent release we can disable installing filters using chatIDs for communities altogether.

Detailed proposal https://forum.vac.dev/t/status-communities-review-and-proposed-usage-of-waku-content-topics/335

Refer to waku-org/pm#268 -> Implementation Details for the plan of this feature.

All channels shall use content-topic used for MemberUpdates .

Important changes:

  • pass MemberUpdatesChannelID to filter and use the same to determine when to use content-topic derived from communityID and not chatID.
  • note that only sending of messages shall use this new content-topic , receiving of messages shall work if msg is sent on older content-topic (based on chatID) or communityID based content-topic . this is to ensure non-breaking migration for existing users. In a subsequent release receiving and store queries would also be modified to use single content-topic
  • validated message send/receive with this code and 2.30 code in communities.
  • validate other actions of community
  • check if any other control messages that can be migrated to same content-topic.
  • validate send/receive with lightClients using new code and 2.30 release
  • dogfooded these changes locally by using this version of status-desktop in relay and light modes and also verifying messages sent are received between 2.30 desktop, mobile and this version of desktop(relay and light modes).
  • dogfooding by others in desktop and mobile interworking with 2.30 releases
  • testing all community features (expected to be done by QA)

@status-im-auto
Copy link
Member

status-im-auto commented Sep 23, 2024

Jenkins Builds

Click to see older builds (69)
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ 0b9a61b #1 2024-09-23 06:23:18 ~2 min tests-rpc 📄log
✔️ 0b9a61b #1 2024-09-23 06:24:49 ~4 min linux 📦zip
✔️ 0b9a61b #1 2024-09-23 06:25:23 ~4 min ios 📦zip
✔️ 0b9a61b #1 2024-09-23 06:25:31 ~4 min android 📦aar
✖️ 0b9a61b #1 2024-09-23 06:58:57 ~38 min tests 📄log
✔️ 48283d7 #2 2024-09-25 09:07:35 ~2 min android 📦aar
✔️ 48283d7 #2 2024-09-25 09:07:47 ~2 min linux 📦zip
✖️ 48283d7 #2 2024-09-25 09:07:54 ~2 min tests 📄log
✔️ 48283d7 #2 2024-09-25 09:08:41 ~3 min ios 📦zip
✖️ 48283d7 #2 2024-09-25 09:09:32 ~4 min tests-rpc 📄log
✔️ 4f10919 #3 2024-09-26 11:10:52 ~2 min android 📦aar
✔️ 4f10919 #3 2024-09-26 11:11:08 ~2 min linux 📦zip
✔️ 4f10919 #3 2024-09-26 11:11:11 ~2 min tests-rpc 📄log
✔️ 4f10919 #3 2024-09-26 11:12:15 ~3 min ios 📦zip
✖️ 4f10919 #3 2024-09-26 11:40:36 ~31 min tests 📄log
✔️ 0a9ffb2 #4 2024-10-07 08:25:54 ~2 min tests-rpc 📄log
✔️ 0a9ffb2 #4 2024-10-07 08:27:27 ~3 min ios 📦zip
✔️ 0a9ffb2 #4 2024-10-07 08:27:46 ~4 min linux 📦zip
✔️ 0a9ffb2 #4 2024-10-07 08:27:49 ~4 min android 📦aar
✖️ 0a9ffb2 #4 2024-10-07 08:56:59 ~33 min tests 📄log
✔️ b6aaaf3 #5 2024-10-09 06:24:23 ~2 min android 📦aar
✔️ b6aaaf3 #5 2024-10-09 06:24:28 ~2 min tests-rpc 📄log
✔️ b6aaaf3 #5 2024-10-09 06:24:33 ~2 min linux 📦zip
✔️ b6aaaf3 #5 2024-10-09 06:25:32 ~3 min ios 📦zip
✖️ b6aaaf3 #5 2024-10-09 06:54:45 ~32 min tests 📄log
✔️ 29b3c44 #6 2024-10-09 06:26:55 ~2 min tests-rpc 📄log
✔️ 29b3c44 #6 2024-10-09 06:27:16 ~2 min linux 📦zip
✔️ 29b3c44 #6 2024-10-09 06:28:45 ~3 min ios 📦zip
✔️ 29b3c44 #6 2024-10-09 06:29:59 ~5 min android 📦aar
✖️ 29b3c44 #6 2024-10-09 07:27:03 ~32 min tests 📄log
✔️ 8cb3323 #7 2024-10-09 08:14:04 ~2 min android 📦aar
✔️ 8cb3323 #7 2024-10-09 08:14:14 ~2 min linux 📦zip
✔️ 8cb3323 #7 2024-10-09 08:14:22 ~2 min tests-rpc 📄log
✔️ 8cb3323 #7 2024-10-09 08:15:05 ~3 min ios 📦zip
✔️ 8cb3323 #7 2024-10-09 08:43:11 ~31 min tests 📄log
✔️ b95059b #8 2024-10-10 07:42:16 ~2 min android 📦aar
✔️ b95059b #8 2024-10-10 07:42:31 ~2 min tests-rpc 📄log
✔️ b95059b #8 2024-10-10 07:43:31 ~3 min ios 📦zip
✔️ b95059b #8 2024-10-10 07:44:22 ~4 min linux 📦zip
✖️ b95059b #8 2024-10-10 08:12:04 ~31 min tests 📄log
✔️ cfec7d4 #9 2024-10-10 09:47:48 ~2 min tests-rpc 📄log
✔️ cfec7d4 #9 2024-10-10 09:47:58 ~2 min linux 📦zip
✔️ cfec7d4 #9 2024-10-10 09:48:52 ~3 min ios 📦zip
✔️ cfec7d4 #9 2024-10-10 09:50:24 ~4 min android 📦aar
✖️ cfec7d4 #9 2024-10-10 10:17:57 ~32 min tests 📄log
✔️ d8fa686 #10 2024-10-16 09:46:40 ~4 min linux 📦zip
✔️ d8fa686 #10 2024-10-16 09:47:07 ~5 min ios 📦zip
✔️ d8fa686 #10 2024-10-16 09:47:14 ~5 min android 📦aar
✔️ d8fa686 #10 2024-10-16 09:47:31 ~5 min tests-rpc 📄log
✖️ d8fa686 #10 2024-10-16 10:15:48 ~33 min tests 📄log
✖️ d8fa686 #11 2024-10-16 11:59:34 ~32 min tests 📄log
✔️ 0eb01c3 #11 2024-10-17 07:44:52 ~2 min android 📦aar
✔️ 0eb01c3 #11 2024-10-17 07:45:34 ~3 min ios 📦zip
✔️ 0eb01c3 #11 2024-10-17 07:46:38 ~4 min linux 📦zip
✔️ 0eb01c3 #11 2024-10-17 07:48:06 ~5 min tests-rpc 📄log
✖️ 0eb01c3 #12 2024-10-17 08:16:40 ~34 min tests 📄log
✔️ 12451a8 #12 2024-10-17 09:12:53 ~1 min android 📦aar
✔️ 12451a8 #12 2024-10-17 09:13:48 ~2 min ios 📦zip
✔️ 12451a8 #12 2024-10-17 09:13:57 ~2 min linux 📦zip
✔️ 12451a8 #12 2024-10-17 09:16:51 ~5 min tests-rpc 📄log
✔️ 12451a8 #13 2024-10-17 09:43:37 ~32 min tests 📄log
✔️ d61d200 #13 2024-10-30 09:20:06 ~4 min tests-rpc 📄log
✔️ d61d200 #13 2024-10-30 09:20:25 ~5 min linux 📦zip
✔️ d61d200 #13 2024-10-30 09:20:42 ~5 min android 📦aar
✔️ d61d200 #1 2024-10-30 09:22:37 ~7 min macos 📦zip
✔️ d61d200 #13 2024-10-30 09:23:03 ~7 min ios 📦zip
✔️ d61d200 #1 2024-10-30 09:23:27 ~8 min macos 📦zip
✖️ d61d200 #1 2024-10-30 09:26:12 ~10 min windows 📦zip
✔️ d61d200 #14 2024-10-30 09:49:55 ~34 min tests 📄log
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ b5315d5 #14 2024-11-01 10:16:41 ~4 min tests-rpc 📄log
✔️ b5315d5 #2 2024-11-01 10:17:03 ~4 min macos 📦zip
✔️ b5315d5 #14 2024-11-01 10:17:46 ~5 min linux 📦zip
✔️ b5315d5 #14 2024-11-01 10:17:56 ~5 min ios 📦zip
✔️ b5315d5 #14 2024-11-01 10:18:05 ~5 min android 📦aar
✔️ b5315d5 #2 2024-11-01 10:20:34 ~8 min macos 📦zip
✖️ b5315d5 #2 2024-11-01 10:22:51 ~10 min windows 📦zip
✖️ b5315d5 #15 2024-11-01 10:46:25 ~33 min tests 📄log
✔️ 103b415 #15 2024-11-01 11:48:28 ~4 min tests-rpc 📄log
✔️ 103b415 #3 2024-11-01 11:48:40 ~4 min macos 📦zip
✔️ 103b415 #15 2024-11-01 11:49:15 ~5 min linux 📦zip
✔️ 103b415 #15 2024-11-01 11:49:28 ~5 min ios 📦zip
✔️ 103b415 #15 2024-11-01 11:49:32 ~5 min android 📦aar
✔️ 103b415 #3 2024-11-01 11:52:07 ~8 min macos 📦zip
✖️ 103b415 #3 2024-11-01 11:54:35 ~10 min windows 📦zip
✔️ 103b415 #16 2024-11-01 12:17:53 ~33 min tests 📄log

@chaitanyaprem chaitanyaprem force-pushed the feat/comm-content-topic-poc branch 2 times, most recently from 48283d7 to 4f10919 Compare September 26, 2024 11:08
@chaitanyaprem chaitanyaprem force-pushed the feat/comm-content-topic-poc branch 3 times, most recently from b6aaaf3 to 29b3c44 Compare October 9, 2024 06:23
@chaitanyaprem chaitanyaprem changed the title feat_: poc to use single content-topic for all community chats feat_: use a single content-topic for all community chats Oct 9, 2024
@chaitanyaprem chaitanyaprem requested a review from a team October 9, 2024 08:55
@chaitanyaprem chaitanyaprem marked this pull request as ready for review October 9, 2024 08:55
@chaitanyaprem chaitanyaprem self-assigned this Oct 9, 2024
@chaitanyaprem chaitanyaprem marked this pull request as draft October 9, 2024 11:05
@churik churik added the blocked label Oct 9, 2024
@churik
Copy link
Member

churik commented Oct 9, 2024

Let's not merge it until the release branch for 2.31 is cut in status-go, seems PR might be very risky

@chaitanyaprem
Copy link
Contributor Author

Let's not merge it until the release branch for 2.31 is cut in status-go, seems PR might be very risky

sure, and agreed that this PR needs more testing and dogfooding before merging.

@chaitanyaprem chaitanyaprem force-pushed the feat/comm-content-topic-poc branch 2 times, most recently from cfec7d4 to d8fa686 Compare October 16, 2024 09:41
Copy link

codecov bot commented Oct 16, 2024

Codecov Report

Attention: Patch coverage is 83.33333% with 8 lines in your changes missing coverage. Please review.

Project coverage is 60.57%. Comparing base (768cda8) to head (103b415).
Report is 6 commits behind head on develop.

Files with missing lines Patch % Lines
protocol/messenger_communities.go 40.00% 2 Missing and 1 partial ⚠️
protocol/communities/manager.go 77.77% 1 Missing and 1 partial ⚠️
protocol/messenger.go 66.66% 1 Missing and 1 partial ⚠️
protocol/communities/manager_archive.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #5864      +/-   ##
===========================================
- Coverage    60.57%   60.57%   -0.01%     
===========================================
  Files          812      812              
  Lines       109328   109359      +31     
===========================================
+ Hits         66230    66246      +16     
- Misses       35337    35344       +7     
- Partials      7761     7769       +8     
Flag Coverage Δ
functional 13.01% <25.00%> (-0.09%) ⬇️
unit 59.93% <83.33%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
protocol/common/message_sender.go 70.87% <100.00%> (+0.35%) ⬆️
protocol/communities/community.go 75.17% <100.00%> (+0.03%) ⬆️
protocol/transport/filters_manager.go 77.69% <100.00%> (+2.43%) ⬆️
protocol/transport/transport.go 70.92% <100.00%> (+1.08%) ⬆️
protocol/communities/manager_archive.go 36.26% <0.00%> (-0.10%) ⬇️
protocol/communities/manager.go 65.41% <77.77%> (+0.03%) ⬆️
protocol/messenger.go 62.52% <66.66%> (-0.03%) ⬇️
protocol/messenger_communities.go 53.38% <40.00%> (-0.16%) ⬇️

... and 27 files with indirect coverage changes

Copy link

@jm-clius jm-clius left a comment

Choose a reason for hiding this comment

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

Reviewed for functionality, not code correctness. I know this will be a multi-step upgrade, but LGTM.

protocol/communities_key_distributor.go Outdated Show resolved Hide resolved
@@ -258,6 +258,8 @@ func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
}

for i := range msgs {
// TODO: find the filter for the msg based on chatID in the message and map it properly. or better this is done in filter layer itself?
// something like t.FilterByChatID()
Copy link
Contributor

Choose a reason for hiding this comment

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

Better have an issue to list the todos and link it here if still needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, i am not sure if this is needed.
Will wait for review from status team to understand the impact and then either remove this or track it separately.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I understand why we should find the filter for given message here? The filter is already there 🤔

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 was little doubtful as to how this will have an impact on any other flows.
I had noticed that messages received by a filter can be fetched using chatID. But the actual chatID of the message will be different than the one stored in filter (i.e <communityID>-memberUpdatesChannelID).

I was wondering if message retrieval flow will get affecting in some form if i don't use the actual chatID of the message. The caller of RetrieveAll has to retrieve messages using <communityID>-memberUpdatesChannelID as chatID and will not have flexibility to filter messages based on actual chatID.
Because the result of this function will now index all messages with single filter rather than chatID specific filters.

protocol/messenger_communities.go Outdated Show resolved Hide resolved
Copy link
Member

@jrainville jrainville left a comment

Choose a reason for hiding this comment

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

Looks good to me, but I'm really not an expert on this part of the code so I'll leave it up to others to give the approval.

I also see a lot of TODOs, should they be addressed now in the review or it's for later?

protocol/communities_key_distributor.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@igor-sirotin igor-sirotin left a comment

Choose a reason for hiding this comment

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

Thank you, @chaitanyaprem!

Although I appreciate the minimum code changes for this, I think it made the code more complex.

  1. ContentTopicOverride and the custom argument to FiltersManager.LoadPublc look quite specific. I think we shouldn't do it this way and introduce a proper function that will just subscribe to a content topic and pubsub topic, isn't that possible?

  2. It is also very unobvious why we use MemberUpdateChannelID everywhere now.
    Maybe it would be better to create something like this? At least a wrapper with explanation:

    func (o *Community) UniversalContentTopic() string {
        // TODO: Explain here why we use MemberUpdateChannelID as the universal content topic
        return c.MemberUpdateChannelID()
    }

TargetPeer string `json:"targetPeer"`
Ephemeral bool `json:"ephemeral"`
Priority *int `json:"priority"`
ContentTopicOverride string `json:"contentTopicOverride"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks quite counter-intuitive. Why do we want some special ContentTopicOverride field, and not just use Topic?

Copy link
Contributor Author

@chaitanyaprem chaitanyaprem Nov 1, 2024

Choose a reason for hiding this comment

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

good point, let me think of why i did not do that. maybe it is possible without breaking anything.

but for some reference as to why i had to use a separate override field

Copy link
Contributor Author

@chaitanyaprem chaitanyaprem Nov 1, 2024

Choose a reason for hiding this comment

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

I had to do this as

rawMessage.ContentTopicOverride = community.MemberUpdateChannelID()
doesn't have access for logic to derive content-topic from chatID and similarly
ContentTopicOverride: rawMessage.ContentTopicOverride,

Happy to follow any suggestions you have to get around this.

protocol/communities/manager_archive.go Outdated Show resolved Hide resolved
protocol/messenger_communities.go Show resolved Hide resolved
Copy link
Contributor

@osmaczko osmaczko left a comment

Choose a reason for hiding this comment

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

Thanks, @chaitanyaprem. This looks good overall. I’m just wondering about the possibility and value of making it feel a bit less hacky. I’ve left some comments for you to check out.

@@ -2268,7 +2268,8 @@ func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMe
)
return rawMessage, fmt.Errorf("can't post message type '%d' on chat '%s'", rawMessage.MessageType, chat.ID)
}

//setting content-topic over-ride for community messages to use memberUpdatesChannelID
rawMessage.ContentTopicOverride = community.MemberUpdateChannelID()
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be possible to override rawMessage.LocalChatID directly here rather than using ContentTopicOverride?

Copy link
Contributor Author

@chaitanyaprem chaitanyaprem Nov 1, 2024

Choose a reason for hiding this comment

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

The reason chatID was not over-ridden is because the same method LoadPublic is used to create a filter that is not present and also load an existing filter.
In phase-1 we want to send using only universal-content-topic but we want to receive using both universal and chatID based content-topics to have compatibility.
If i override chatID then i was not sure if in some flow filter gets created via LoadPublic may get messed up. e.g in case of encrypted channels hashratched-groupID is derived from chatID

rawMessage.HashRatchetGroupID = []byte(chat.ID)

Not sure where else in the code downward chatID is used for some other purposes.

If you think it is safe to override, i will go ahead and change it.

@@ -258,6 +258,8 @@ func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
}

for i := range msgs {
// TODO: find the filter for the msg based on chatID in the message and map it properly. or better this is done in filter layer itself?
// something like t.FilterByChatID()
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure I understand why we should find the filter for given message here? The filter is already there 🤔

protocol/communities/manager.go Outdated Show resolved Hide resolved
protocol/communities/manager_archive.go Outdated Show resolved Hide resolved
@chaitanyaprem
Copy link
Contributor Author

chaitanyaprem commented Nov 1, 2024

  1. ContentTopicOverride and the custom argument to FiltersManager.LoadPublc look quite specific. I think we shouldn't do it this way and introduce a proper function that will just subscribe to a content topic and pubsub topic, isn't that possible?

The problem is there are too many layers to navigate in the code and there is a separate function that will subscribe to content-topic and pubsubTopic already here.

The filters are tied to many things in the code such as chatID, sym/asym encryption along with deriving content-topic and subscribing to it.
If we need a clean way to do it, i think it might lead to a massive refactor of the code (which i am not confident enough to do) and it would require significant effort and testing to ensure nothing else is broken.
It took a couple of weeks for me to understand all dependencies and various layers and abstractions to think of a simple way to make this change without impacting lot of code. I still don't understand a large part of it and its impact :)

If you can provide any specific suggestions as to how this change can be done more cleanly, i can implement the same.
I may be missing some simpler approach as i am not familiar with many parts of the code.

2. It is also very unobvious why we use MemberUpdateChannelID everywhere now.
Maybe it would be better to create something like this? At least a wrapper with explanation:

Thanks! will make this change.

@chaitanyaprem
Copy link
Contributor Author

@igor-sirotin , @osmaczko there was a suggestion from @fryorcraken that for any new communities that are created we can start using approach of single conent-topic straight on whereas for existing communities, we can do it in a 2 phase manner.

In order to do that shall we create some sort of attribute fields in the community-description to indicate such feature changes for communities so that we can apply new features to new communities quicker. Explained more here

WDYT? can we add an attributes field in the community description which for now will have just a single flag single-content-topic-usage and it will be set to no by default for all existing communities and yes to communities that are created after this change.
Note that the side-effect is users using old code will not be able to join/interact with these new communities.

@igor-sirotin
Copy link
Collaborator

The problem is there are too many layers to navigate in the code

@chaitanyaprem I'm worried that this PR makes it even more confusing 😄

... i think it might lead to a massive refactor of the code

I think we could take the opportunity of the new requirement and refactor this code, so that this change is not very harmful.

@igor-sirotin
Copy link
Collaborator

there was a suggestion that for any new communities that are created we can start using approach of single conent-topic straight

@chaitanyaprem @fryorcraken Sorry, I don't think this is a good idea.

  1. It will confuse the code even more
  2. So new communities won't work for old apps
  3. Considering that there're not many new communities created everyday (how many do we have? ~5?), the effort is just not worth the benefit.

@chaitanyaprem
Copy link
Contributor Author

@chaitanyaprem I'm worried that this PR makes it even more confusing 😄

I think we could take the opportunity of the new requirement and refactor this code, so that this change is not very harmful.

Let me think and see if there is any other way to make this change without refactor. I want to try and avoid it if possible.

@fryorcraken
Copy link

fryorcraken commented Nov 4, 2024

1. It will confuse the code even more

From reading the comment here, I think there needs to be a clearer path on how to untangle status-go at each PR.

I agree with some of the comments trying to making it less hacky, while at the same time I get Prem's input that he may not have knowledge and confidence here to do further refactor.

If we compare it to the more usual nwaku development flow. Prem is a researcher implementing a PoC for a protocol change (content topic community management).

Usually, the expectation is for codebase owners/engineers to help the researcher harden this PoC and proceed with neeeded refactors.
@kaichaosun @plopezlpz, is that something you can help with please?

2. So new communities won't work for old apps

Let's move this conversation to https://forum.vac.dev/t/breaking-changes-and-roll-out-strategies/338/5

I am not convinced that Status Communities is in a state where the cost of "ensuring that newly created communities are compatible with older software version" outweigh the benefits.

This specific change helps with Community scaling, by reducing the resources used by a single community user on store.

Moreover, we could always have a toggle for an owner creating a new community. "experimental creation: includes changes that improves efficiency but it means your members need to have the same version or new than you to join".

This is probably to discussion to have more generally within Status team here. Speed of development and improvement vs letting your users keep an old app version.

As stated in https://forum.vac.dev/t/breaking-changes-and-roll-out-strategies/338 there is also a clean way to handle it:

Waku chat and Status teams to consider UX around incompatible communities, a generic solution is likely to be enough regardless of the migration. e.g. “This community has been created with a new version for the app, please update to join”.

3. Considering that there're not many new communities created everyday (how many do we have? ~5?), the effort is just not worth the benefit.

This sounds paradoxal. you are suggesting that we should not break things for current users (joining communities) because at the end of the day there is not enough users (creating communities). It is one or the other? Do you have not enough activity or too much activity to make this change viable?

@fryorcraken
Copy link

Final point: by creating a new code path for this, which is "cleaner", then you have the opportunity to deleted the "messy" code path in the future. Yes, it would mean that people "have to upgrade", but what is the alternative?

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.

9 participants