[{"data":1,"prerenderedAt":333},["ShallowReactive",2],{"blog:2021:breaking-changes":3,"blogMore-Development":319,"comments-breaking-changes":332},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"category":11,"excerpt":12,"body":27,"_type":309,"_id":310,"_source":311,"_file":312,"_stem":313,"_extension":314,"url":315,"wordCount":316,"minutes":317,"commentCount":318},"/blog/2021/breaking-changes","2021",false,"en","Shipping breaking changes","Breaking changes are always work for your users. Work you are forcing them to do when they upgrade to your new version. They took a dependency on your library or software because it saved them time but now it's costing them time.","2021-03-29T10:03:00-00:00","Development",{"type":13,"children":14},"root",[15,22],{"type":16,"tag":17,"props":18,"children":19},"element","p",{},[20],{"type":21,"value":9},"text",{"type":16,"tag":17,"props":23,"children":24},{},[25],{"type":21,"value":26},"Every breaking change is a reason for them to stop and reconsider their options. If your library is paid-for or the preferred way for paying users to access your services then lost users can come with a real financial cost.",{"type":13,"children":28,"toc":301},[29,33,37,42,49,79,84,90,118,123,129,149,154,172,177,207,213,218,237,242,270,275,287,292],{"type":16,"tag":17,"props":30,"children":31},{},[32],{"type":21,"value":9},{"type":16,"tag":17,"props":34,"children":35},{},[36],{"type":21,"value":26},{"type":16,"tag":17,"props":38,"children":39},{},[40],{"type":21,"value":41},"Use breaking changes sparingly.",{"type":16,"tag":43,"props":44,"children":46},"h2",{"id":45},"good-reasons-for-breaking-changes",[47],{"type":21,"value":48},"Good reasons for breaking changes",{"type":16,"tag":50,"props":51,"children":52},"ul",{},[53,59,64,69,74],{"type":16,"tag":54,"props":55,"children":56},"li",{},[57],{"type":21,"value":58},"A feature is being removed for business reasons",{"type":16,"tag":54,"props":60,"children":61},{},[62],{"type":21,"value":63},"Standards are changing how something works",{"type":16,"tag":54,"props":65,"children":66},{},[67],{"type":21,"value":68},"The feature did not work as intended and it's impossible to fix without a break",{"type":16,"tag":54,"props":70,"children":71},{},[72],{"type":21,"value":73},"Service or library dependencies you rely upon are forcing a change",{"type":16,"tag":54,"props":75,"children":76},{},[77],{"type":21,"value":78},"A small breaking change now prevents actual significant breaking changes later",{"type":16,"tag":17,"props":80,"children":81},{},[82],{"type":21,"value":83},"Even when presented with these you should think not only about whether you can avoid a break but also take the opportunity to think about what you can do now to avoid similar breaks in the future.",{"type":16,"tag":43,"props":85,"children":87},{"id":86},"poor-reasons-for-breaking-changes",[88],{"type":21,"value":89},"Poor reasons for breaking changes",{"type":16,"tag":50,"props":91,"children":92},{},[93,98,103,108,113],{"type":16,"tag":54,"props":94,"children":95},{},[96],{"type":21,"value":97},"It makes the internals of the library tidier",{"type":16,"tag":54,"props":99,"children":100},{},[101],{"type":21,"value":102},"Parameter order, method naming or property naming would be ”clearer”",{"type":16,"tag":54,"props":104,"children":105},{},[106],{"type":21,"value":107},"Consistency with other platforms or products",{"type":16,"tag":54,"props":109,"children":110},{},[111],{"type":21,"value":112},"Personal subjective interpretations of “better”",{"type":16,"tag":54,"props":114,"children":115},{},[116],{"type":21,"value":117},"Compatibility with a different library to attract new users",{"type":16,"tag":17,"props":119,"children":120},{},[121],{"type":21,"value":122},"While many of these are admirable goals in of themselves they are not a reason to break your existing customers.",{"type":16,"tag":43,"props":124,"children":126},{"id":125},"managing-breaking-changes",[127],{"type":21,"value":128},"Managing breaking changes",{"type":16,"tag":17,"props":130,"children":131},{},[132,134,140,142,147],{"type":21,"value":133},"It goes without saying that ",{"type":16,"tag":135,"props":136,"children":137},"strong",{},[138],{"type":21,"value":139},"intentional",{"type":21,"value":141}," breaking changes should only occur in ",{"type":16,"tag":135,"props":143,"children":144},{},[145],{"type":21,"value":146},"major versions",{"type":21,"value":148}," with the exception of security fixes that require users of your library take some action.",{"type":16,"tag":17,"props":150,"children":151},{},[152],{"type":21,"value":153},"Here are some thoughts to ease the pain of breaking changes:",{"type":16,"tag":50,"props":155,"children":156},{},[157,162,167],{"type":16,"tag":54,"props":158,"children":159},{},[160],{"type":21,"value":161},"List each breaking change in a migration guide with a before and after code fragment",{"type":16,"tag":54,"props":163,"children":164},{},[165],{"type":21,"value":166},"Summarize breaking changes in the README with a link to the migration guide for more information",{"type":16,"tag":54,"props":168,"children":169},{},[170],{"type":21,"value":171},"Keep breaking change count low even on major releases",{"type":16,"tag":17,"props":173,"children":174},{},[175],{"type":21,"value":176},"Users should ideally also be able to find/replace or follow compiler errors. Consider:",{"type":16,"tag":50,"props":178,"children":179},{},[180,202],{"type":16,"tag":54,"props":181,"children":182},{},[183,185,192,194,200],{"type":21,"value":184},"Platform-specific mechanisms for dealing with breaking changes. e.g. in C# you can use the ",{"type":16,"tag":186,"props":187,"children":189},"code",{"className":188},[],[190],{"type":21,"value":191},"[Obsolete]",{"type":21,"value":193}," attribute to help guide to the replacement API, Java has the ",{"type":16,"tag":186,"props":195,"children":197},{"className":196},[],[198],{"type":21,"value":199},"@deprecated",{"type":21,"value":201}," annotation.",{"type":16,"tag":54,"props":203,"children":204},{},[205],{"type":21,"value":206},"Leaving a stub for the old method in place for one whole major release that calls the new method with the right arguments and produces a log warning pointing to the migration guide.",{"type":16,"tag":43,"props":208,"children":210},{"id":209},"rewrites",[211],{"type":21,"value":212},"Rewrites",{"type":16,"tag":17,"props":214,"children":215},{},[216],{"type":21,"value":217},"If a package is drastically different users will need to rewrite code. This is always a bigger deal for them than you expect, because:",{"type":16,"tag":219,"props":220,"children":221},"ol",{},[222,227,232],{"type":16,"tag":54,"props":223,"children":224},{},[225],{"type":21,"value":226},"It is unscheduled and was not on their radar (no they are not monitoring your GitHub issue discussions)",{"type":16,"tag":54,"props":228,"children":229},{},[230],{"type":21,"value":231},"They use your library in ways you likely don't expect or anticipate",{"type":16,"tag":54,"props":233,"children":234},{},[235],{"type":21,"value":236},"The more they depend on your product then the more work your rewrite involves",{"type":16,"tag":17,"props":238,"children":239},{},[240],{"type":21,"value":241},"Really consider whether your new API is actually better (ideally before you ship it). One way to do this is to produce a set of example usage code for using the old library vs the new library. Put them side-by-side and open them up to feedback. Is the new API genuinely better? Some indicators it might be are:",{"type":16,"tag":50,"props":243,"children":244},{},[245,250,255,260,265],{"type":16,"tag":54,"props":246,"children":247},{},[248],{"type":21,"value":249},"Less code for users to write",{"type":16,"tag":54,"props":251,"children":252},{},[253],{"type":21,"value":254},"Plenty of sensible and safe defaults",{"type":16,"tag":54,"props":256,"children":257},{},[258],{"type":21,"value":259},"Less specialized terminology and concepts",{"type":16,"tag":54,"props":261,"children":262},{},[263],{"type":21,"value":264},"Easier to test and abstract",{"type":16,"tag":54,"props":266,"children":267},{},[268],{"type":21,"value":269},"Existing customers prefer the new syntax and think it's worth changing",{"type":16,"tag":17,"props":271,"children":272},{},[273],{"type":21,"value":274},"Some indicators that it isn't really any better is: internal staff prefer it, it aligns better with some other platform or just being different.",{"type":16,"tag":17,"props":276,"children":277},{},[278,280,285],{"type":21,"value":279},"Sometimes it's worth doing because it targets a different crowd or comes at the problem from a simpler direction or abstraction. If so, then ",{"type":16,"tag":135,"props":281,"children":282},{},[283],{"type":21,"value":284},"seriously",{"type":21,"value":286}," consider giving it a new package name and put it out for early access.",{"type":16,"tag":17,"props":288,"children":289},{},[290],{"type":21,"value":291},"Make sure users are led to the right library of the two and if there is a lot of code duplication and users on the \"old\" library then consider making the next version of the old library a compatibility wrapper around the new library.",{"type":16,"tag":17,"props":293,"children":294},{},[295],{"type":16,"tag":296,"props":297,"children":298},"em",{},[299],{"type":21,"value":300},"[)amien",{"title":302,"searchDepth":303,"depth":303,"links":304},"",2,[305,306,307,308],{"id":45,"depth":303,"text":48},{"id":86,"depth":303,"text":89},{"id":125,"depth":303,"text":128},{"id":209,"depth":303,"text":212},"markdown","content:blog:2021:breaking-changes.md","content","blog/2021/breaking-changes.md","blog/2021/breaking-changes","md","/blog/2021/breaking-changes/",667,3,0,[320,324,328],{"title":321,"date":322,"url":323},"HTML5 Video Cheatsheet: Optimizing videos for the web","2025-12-05T00:00:00Z","/blog/2025/html5-video-cheatsheet/",{"title":325,"date":326,"url":327},"Transactions in the MongoDB EF Core Provider","2025-10-25","/blog/2025/mongodb-explicit-transactions/",{"title":329,"date":330,"url":331},"Queryable Encryption with the MongoDB EF Core Provider","2025-09-22","/blog/2025/mongodb-queryable-encryption/",[],1779224641767]