Salesforce: Globals and Breaking Changes, Part 2

In my last post we examined a scenario where replacing an implicit constructor with an explicit one was a breaking change. Now we'll look at a similar issue where overriding a virtual method can be a breaking change.

This time we'll create two global classes in our managed package, where the first contains a virtual method and the second extends the first.

global virtual class ExampleClass {
    global virtual void foo() { }

global virtual class ExampleChildClass extends ExampleClass {

Once again, we'll imagine that these have been packaged into version 1.0 of a managed package with a prefix of TEST, and this package has been installed into the subscriber org. In the subscriber org, we extend the second class from our managed package and override the virtual method that it inherits from the first.

global class ExampleGrandchildClass extends TEST.ExampleChildClass {
    public override void foo() { }

Back in our managed package, we determine that ExampleChildClass actually needs to override the behavior of our virtual method.

global virtual class ExampleChildClass extends ExampleClass {
    // added this line:
    global override virtual void foo() { }

Now we release version 1.1 of our managed package with this change, and install it into the subscriber org. Since our new override is virtual, we expect that the class we created earlier is still valid.

ExampleGrandchildClass: line #, column #: ExampleGrandchildClass: Method does not override an ancestor method: void foo()

The class in our subscriber org fails to compile because it cannot see the override that we created in version 1.1 of our package. Like last time, this error can be resolved by going to the Version Settings tab and changing the class to reference version 1.1.

What options do we have if we don't want to break the subscriber classes in the first place?

1. Override global virtual methods on every subclass at the time of creation

This option is far from ideal, especially if you have a lot of virtual methods or a large class hierarchy, and unfortunately does not help if you have existing virtual methods.

2. Wait for the fix

This issue was acknowledged last week, and you can find the known issue here if you wish to follow it for status updates.

Have you encountered any other changes that can unexpectedly break consuming code? Let me know in the comments.