-
Notifications
You must be signed in to change notification settings - Fork 10
fix: align AlgorandClientInterface with AlgorandClient (#355) #365
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
Conversation
In 2a2a0ea I just completely got rid of the usage of I do think it's worthwhile to refactor multiple areas of the codebase to use interfaces for better composability but right now this PR makes things a bit more consistent. |
I was getting circular dependency issues when originally trying to use it directly. |
Are you still getting the errors locally? Building works fine for me locally and in the actions |
Running |
These aren't true circular deps since it's just the types. b1451ff I've updated the imports to make that more clear. Out of the box madge doesn't handle |
True, I can revert this PR to 5271138 |
Yeah you could go either way, which do you prefer? |
I can revert and not mark as deprecated for now. In the future we can assess if there's opportunity for using interfaces in more places and then can refactor/deprecate accordingly. |
b1451ff
to
5271138
Compare
A negative consequence of using interfaces just occurred to me. Any addition to the AlgorandClient interface, such as a new method, becomes a breaking change. I think this means that using an interface solely to avoid circular dependencies is no longer a good enough reason to use interfaces and I'm leaning towards using |
I don't quite understand how an addition to the AlgorandClientInterface would be a breaking change, but an addition to the AlgorandClient isn't? |
Interfaces can be implemented by external libraries. For example, if I create an external class |
Ah yes of course, I wasn't thinking about using it as an input type. I agree I think the best path forward is to change back to |
Agreed. we could make a semver exception because of this although I don't love doing that.
The problem is that marking as deprecated doesn't save us from making a breaking change since either we make a breaking change by replacing it or #371 is a breaking change. I think we just need to rip the band-aid off. The real question is whether we want to follow semver and increment MAJOR or not. I am typically against breaking semver, but since the chance of a single person implement the interface is fairly low perhaps this would be a justifiable instance. |
Agreed, it's a bit of an annoying one. I feel that bumping the major may create more confusion (for likely no value as discussed above), given we're already supporting 2 versions in parallel. @lempira Are you ok with not bumping the major here? |
Ah right forgot about typed clients, we'd need to do these update in lockstep then. Our options are either A) Simply mark B) Replace We can't have best of both worlds (replace usage of A) Continuously break utils semver until next release The initial thinking of
Doesn't apply because it would break the semantic relationship between clients and utils. |
What if we used a type alias via |
Ah brilliant! Definitely hacky but I think it's the least amount of pain for everyone involved. |
I would like major bump right now, but I also like to remain true to semver. I like the approach of |
Just want to clarify that this is still a breaking change and making this change without bumping MAJOR is breaking semver, but the affected users or libraries will be close to zero (and I think 0 is much more likely than > 0), especially with
Correct, So this would mean we...
I also realized this would mean it's a breaking change for any lib that is using |
I would really like to avoid bumping a major version, given that just went through two major bumps in a relatively short amount of time. I would also like to remain true to semver so it doesn't lose meaning. Here are some suggestions Options 1. - Extend the interface to a v2 interface interface AlgorandClientInterface {
// Base interface
}
interface AlgorandClientInterfaceV2 extends AlgorandClientInterface {
// New functionality
registerErrorTransformer(transformer: ErrorTransformer): void;
unregisterErrorTransformer(transformer: ErrorTransformer): void;
} Pro: Don't have to bump major version since we are not changing API Option 2. Make the new functions in the interface optional. We could either do this by making the property optional with interface BaseAlgorandClient {
// Pull existing functionality into a BaseAlgorandClient
}
interface ErrorTransforming {
registerErrorTransformer(transformer: ErrorTransformer): void;
unregisterErrorTransformer(transformer: ErrorTransformer): void;
}
// Users can implement just what they need
type AlgorandClientInterface = BaseAlgorandClient & Partial<ErrorTransforming>; Pros: Done have to bump major version. Pretty simple option that lets user opt into that function be defining it. Option 3: Using a builder pattern. You have users define the errorTransformer with a //Change of current implementation
interface AlgorandClientInterface {
// Current properties
// error transformer builder method 'this' for after adding the errorTransformer to object
registerErrorTransformer(transformer: ErrorTransformer): this;
unregisterErrorTransformer(transformer: ErrorTransformer): this;
}
class AlgorandClient implements AlgorandClientInterface {
private errorTransformer?: ErrorTransformer;
registerErrorTransformer(transformer: ErrorTransformer): this {
this.errorTransformers.add(transformer);
return this;
}
unregisterErrorTransformer(transformer: ErrorTransformer): this {
this.errorTransformers.delete(transformer);
return this;
}
}
//User implementation
const clientWithErrorTransformer = new AlgorandClient()
.registerErrorTransformer(myTransformer1)
.registerErrorTransformer(myTransformer2);
//unregister
client.unregisterErrorTransformer(myTransformer1); Pros: No major bump. I think the simplest option is to make the new properties optional with defaults until a point when we bump the major version for major upgrade. |
Why is there a strong aversion to MAJOR bump? It doesn't "feel" great but every other option so far feels worse.
The problem is that this doesn't solve the original issue in #355 unless we change usage of
This now means any optional functional may be undefined according to typescript. For example
This is how it's implemented now and has the problem of adding a method to the interface. |
After further discussion, there's been a consensus that the use of partial (as seen in 33f8755) is the best path forward. It avoids a breaking change while still giving developers the proper type, albeit some fields are erroneously marked optional. In the next major release the interfaces will be removed entirely and |
See the discussion in #365 for context. Essentially by continuing to use the Interface any additional feature to AlgorandClient becomes a breaking change. The only reason for using the interface in the first place was for a rollup error, but this was a false positive and can be fixed by using the type import as shown in this commit
See the discussion in #365 for context. Essentially by continuing to use the Interface any additional feature to AlgorandClient becomes a breaking change. The only reason for using the interface in the first place was for a rollup error, but this was a false positive and can be fixed by using the type import as shown in this commit
Proposed Changes