[{"data":1,"prerenderedAt":730},["ShallowReactive",2],{"blog:2007:refactoring-shared-libraries-and-public-apis":3,"blogMore-Development":716,"comments-refactoring-shared-libraries-and-public-apis":729},{"id":4,"title":5,"body":6,"category":696,"commentCount":697,"date":698,"description":699,"excerpt":700,"extension":701,"filenames":702,"hidden":703,"image":702,"meta":704,"minutes":166,"navigation":174,"path":705,"seo":706,"showCategory":702,"stem":707,"tags":708,"updated":702,"url":713,"wordCount":714,"__hash__":715},"content\u002Fblog\u002F2007\u002Frefactoring-shared-libraries-and-public-apis.md","Refactoring shared libraries and public APIs",{"type":7,"value":8,"toc":689},"minimark",[9,27,30,33,36,41,49,94,109,200,209,212,216,272,278,389,393,495,499,502,586,589,629,632,662,666,677,680,685],[10,11,12,13,20,21,26],"p",{},"Refactoring is an essential process to keep code clean and elegant while it evolves. IDE’s offer common refactorings (although somewhat short of those prescribed in Fowler’s excellent ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fwww.amazon.com\u002Fgp\u002Fredirect.html?ie=UTF8&location=http%3A%2F%2Fwww.amazon.com%2FRefactoring-Improving-Design-Existing-Code%2Fdp%2F0201485672&tag=dam-20&linkCode=ur2&camp=1789&creative=9325",[18],"nofollow","Refactoring"," book and way short of the overall goals explained in Kerievsky’s ",[14,22,25],{"href":23,"rel":24},"https:\u002F\u002Fwww.amazon.com\u002Fgp\u002Fredirect.html?ie=UTF8&location=http%3A%2F%2Fwww.amazon.com%2FRefactoring-Improving-Design-Existing-Code%2Fdp%2F0201485672http%3A%2F%2Fwww.amazon.com%2FRefactoring-Patterns-Addison-Wesley-Signature-Kerievsky%2Fdp%2F0321213351&tag=dam-20&linkCode=ur2&camp=1789&creative=9325",[18],"Refactoring Patterns",").",[10,28,29],{},"One limitation of existing tools is that they can only update references within your solution. When you are refactoring a shared library this is a problem, especially if it is your public API to the outside world.",[10,31,32],{},"We need to introduce metadata to document how the API has evolved and extend the tools to generate and understand this metadata.",[10,34,35],{},"Let’s take a look at a few of the refactoring in Visual Studio and see how they could be documented using the .NET metadata mechanism of choice, attributes.",[37,38,40],"h2",{"id":39},"rename","Rename",[10,42,43,44,48],{},"Starting simple lets we had a property named ",[45,46,47],"code",{},"Reference",":",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-csharp shiki shiki-themes everforest-light dracula","public string Reference {\n  get { return id; }\n}\n","csharp","",[45,57,58,75,88],{"__ignoreMap":55},[59,60,63,67,71],"span",{"class":61,"line":62},"line",1,[59,64,66],{"class":65},"s9HRq","public",[59,68,70],{"class":69},"sXAHl"," string",[59,72,74],{"class":73},"s6Vpi"," Reference {\n",[59,76,78,81,85],{"class":61,"line":77},2,[59,79,80],{"class":73},"  get { ",[59,82,84],{"class":83},"smiwp","return",[59,86,87],{"class":73}," id; }\n",[59,89,91],{"class":61,"line":90},3,[59,92,93],{"class":73},"}\n",[10,95,96,97,99,100,103,104,108],{},"We are going to rename ",[45,98,47],{}," to ",[45,101,102],{},"StockCode"," for the ",[105,106,107],"em",{},"1.1.0.0"," release. The tool could introduce a stub for backward compatibility while also marking it with metadata giving us:",[50,110,112],{"className":52,"code":111,"language":54,"meta":55,"style":55},"[DeprecatedRefactorRename(\"StockCode\", \"1.1.0.0\")]\npublic string Reference {\n  get { return StockCode; }\n}\n\npublic string StockCode {\n  get { return id; }\n}\n",[45,113,114,147,155,164,169,176,186,195],{"__ignoreMap":55},[59,115,116,119,123,126,130,133,135,138,140,142,144],{"class":61,"line":62},[59,117,118],{"class":73},"[",[59,120,122],{"class":121},"snuxY","DeprecatedRefactorRename",[59,124,125],{"class":73},"(",[59,127,129],{"class":128},"sciFF","\"",[59,131,102],{"class":132},"sJQOs",[59,134,129],{"class":128},[59,136,137],{"class":73},", ",[59,139,129],{"class":128},[59,141,107],{"class":132},[59,143,129],{"class":128},[59,145,146],{"class":73},")]\n",[59,148,149,151,153],{"class":61,"line":77},[59,150,66],{"class":65},[59,152,70],{"class":69},[59,154,74],{"class":73},[59,156,157,159,161],{"class":61,"line":90},[59,158,80],{"class":73},[59,160,84],{"class":83},[59,162,163],{"class":73}," StockCode; }\n",[59,165,167],{"class":61,"line":166},4,[59,168,93],{"class":73},[59,170,172],{"class":61,"line":171},5,[59,173,175],{"emptyLinePlaceholder":174},true,"\n",[59,177,179,181,183],{"class":61,"line":178},6,[59,180,66],{"class":65},[59,182,70],{"class":69},[59,184,185],{"class":73}," StockCode {\n",[59,187,189,191,193],{"class":61,"line":188},7,[59,190,80],{"class":73},[59,192,84],{"class":83},[59,194,87],{"class":73},[59,196,198],{"class":61,"line":197},8,[59,199,93],{"class":73},[10,201,202,203,205,206,208],{},"The library is both binary and source compatible but with a little IDE work they could get a warning that ",[45,204,47],{}," is now ",[45,207,102],{}," and given the choice of updating all the references in their project.",[10,210,211],{},"Nice. Let’s try a few more:",[37,213,215],{"id":214},"remove-parameters","Remove Parameters",[50,217,219],{"className":52,"code":218,"language":54,"meta":55,"style":55},"public bool AddStock(int quantity, DateTime arrival, StorageBin location) {\n   ...\n}\n",[45,220,221,260,268],{"__ignoreMap":55},[59,222,223,225,228,232,234,237,241,243,246,249,251,254,257],{"class":61,"line":62},[59,224,66],{"class":65},[59,226,227],{"class":69}," bool",[59,229,231],{"class":230},"sS4Kt"," AddStock",[59,233,125],{"class":73},[59,235,236],{"class":69},"int",[59,238,240],{"class":239},"s7cAX"," quantity",[59,242,137],{"class":73},[59,244,245],{"class":121},"DateTime",[59,247,248],{"class":239}," arrival",[59,250,137],{"class":73},[59,252,253],{"class":121},"StorageBin",[59,255,256],{"class":239}," location",[59,258,259],{"class":73},") {\n",[59,261,262,265],{"class":61,"line":77},[59,263,264],{"class":65},"   ..",[59,266,267],{"class":73},".\n",[59,269,270],{"class":61,"line":90},[59,271,93],{"class":73},[10,273,274,275,277],{},"We are switching to a managed warehouse and so we no longer need to know where items are stored so we refactor and remove the ",[45,276,253],{},".",[50,279,281],{"className":52,"code":280,"language":54,"meta":55,"style":55},"[DeprecatedParameterRemoved(\"location\", \"1.1.0.0.\")]\npublic bool AddStock(int quantity, DateTime arrival, StorageBin location) {\n  return AddStock(quantity, arrival);\n}\n\npublic bool AddStock(int quantity, DateTime arrival) {\n  ...\n}\n",[45,282,283,310,338,348,352,356,378,385],{"__ignoreMap":55},[59,284,285,287,290,292,294,297,299,301,303,306,308],{"class":61,"line":62},[59,286,118],{"class":73},[59,288,289],{"class":121},"DeprecatedParameterRemoved",[59,291,125],{"class":73},[59,293,129],{"class":128},[59,295,296],{"class":132},"location",[59,298,129],{"class":128},[59,300,137],{"class":73},[59,302,129],{"class":128},[59,304,305],{"class":132},"1.1.0.0.",[59,307,129],{"class":128},[59,309,146],{"class":73},[59,311,312,314,316,318,320,322,324,326,328,330,332,334,336],{"class":61,"line":77},[59,313,66],{"class":65},[59,315,227],{"class":69},[59,317,231],{"class":230},[59,319,125],{"class":73},[59,321,236],{"class":69},[59,323,240],{"class":239},[59,325,137],{"class":73},[59,327,245],{"class":121},[59,329,248],{"class":239},[59,331,137],{"class":73},[59,333,253],{"class":121},[59,335,256],{"class":239},[59,337,259],{"class":73},[59,339,340,343,345],{"class":61,"line":90},[59,341,342],{"class":83},"  return",[59,344,231],{"class":230},[59,346,347],{"class":73},"(quantity, arrival);\n",[59,349,350],{"class":61,"line":166},[59,351,93],{"class":73},[59,353,354],{"class":61,"line":171},[59,355,175],{"emptyLinePlaceholder":174},[59,357,358,360,362,364,366,368,370,372,374,376],{"class":61,"line":178},[59,359,66],{"class":65},[59,361,227],{"class":69},[59,363,231],{"class":230},[59,365,125],{"class":73},[59,367,236],{"class":69},[59,369,240],{"class":239},[59,371,137],{"class":73},[59,373,245],{"class":121},[59,375,248],{"class":239},[59,377,259],{"class":73},[59,379,380,383],{"class":61,"line":188},[59,381,382],{"class":65},"  ..",[59,384,267],{"class":73},[59,386,387],{"class":61,"line":197},[59,388,93],{"class":73},[37,390,392],{"id":391},"reorder-parameters","Reorder Parameters",[50,394,396],{"className":52,"code":395,"language":54,"meta":55,"style":55},"[DeprecatedParametersReordered(\"arrival, quantity\", \"1.1.0.0.\")]\npublic bool AddStock(int quantity, DateTime arrival) {\n  return AddStock(arrival, quantity);\n}\n\npublic bool AddStock(DateTime arrival, int quantity) {\n  ...\n}\n",[45,397,398,424,446,455,459,463,485,491],{"__ignoreMap":55},[59,399,400,402,405,407,409,412,414,416,418,420,422],{"class":61,"line":62},[59,401,118],{"class":73},[59,403,404],{"class":121},"DeprecatedParametersReordered",[59,406,125],{"class":73},[59,408,129],{"class":128},[59,410,411],{"class":132},"arrival, quantity",[59,413,129],{"class":128},[59,415,137],{"class":73},[59,417,129],{"class":128},[59,419,305],{"class":132},[59,421,129],{"class":128},[59,423,146],{"class":73},[59,425,426,428,430,432,434,436,438,440,442,444],{"class":61,"line":77},[59,427,66],{"class":65},[59,429,227],{"class":69},[59,431,231],{"class":230},[59,433,125],{"class":73},[59,435,236],{"class":69},[59,437,240],{"class":239},[59,439,137],{"class":73},[59,441,245],{"class":121},[59,443,248],{"class":239},[59,445,259],{"class":73},[59,447,448,450,452],{"class":61,"line":90},[59,449,342],{"class":83},[59,451,231],{"class":230},[59,453,454],{"class":73},"(arrival, quantity);\n",[59,456,457],{"class":61,"line":166},[59,458,93],{"class":73},[59,460,461],{"class":61,"line":171},[59,462,175],{"emptyLinePlaceholder":174},[59,464,465,467,469,471,473,475,477,479,481,483],{"class":61,"line":178},[59,466,66],{"class":65},[59,468,227],{"class":69},[59,470,231],{"class":230},[59,472,125],{"class":73},[59,474,245],{"class":121},[59,476,248],{"class":239},[59,478,137],{"class":73},[59,480,236],{"class":69},[59,482,240],{"class":239},[59,484,259],{"class":73},[59,486,487,489],{"class":61,"line":188},[59,488,382],{"class":65},[59,490,267],{"class":73},[59,492,493],{"class":61,"line":197},[59,494,93],{"class":73},[37,496,498],{"id":497},"move-method","Move Method",[10,500,501],{},"Existing tools offer little support for MoveMethod because they haven’t considered how to refactor the references. It is difficult to retain binary compatibility unless the class has a reference to class that now has the method we are interested in.",[50,503,505],{"className":52,"code":504,"language":54,"meta":55,"style":55},"[DeprecatedMethodMoved(\"StockController\", \"Add\", \"1.1.0.0\")]\npublic bool AddStock(DateTime arrival, int quantity) {\n  return stockController.Add(this, arrival, quantity);\n}\n",[45,506,507,542,564,582],{"__ignoreMap":55},[59,508,509,511,514,516,518,521,523,525,527,530,532,534,536,538,540],{"class":61,"line":62},[59,510,118],{"class":73},[59,512,513],{"class":121},"DeprecatedMethodMoved",[59,515,125],{"class":73},[59,517,129],{"class":128},[59,519,520],{"class":132},"StockController",[59,522,129],{"class":128},[59,524,137],{"class":73},[59,526,129],{"class":128},[59,528,529],{"class":132},"Add",[59,531,129],{"class":128},[59,533,137],{"class":73},[59,535,129],{"class":128},[59,537,107],{"class":132},[59,539,129],{"class":128},[59,541,146],{"class":73},[59,543,544,546,548,550,552,554,556,558,560,562],{"class":61,"line":77},[59,545,66],{"class":65},[59,547,227],{"class":69},[59,549,231],{"class":230},[59,551,125],{"class":73},[59,553,245],{"class":121},[59,555,248],{"class":239},[59,557,137],{"class":73},[59,559,236],{"class":69},[59,561,240],{"class":239},[59,563,259],{"class":73},[59,565,566,568,571,573,575,579],{"class":61,"line":90},[59,567,342],{"class":83},[59,569,570],{"class":73}," stockController.",[59,572,529],{"class":230},[59,574,125],{"class":73},[59,576,578],{"class":577},"sKO3f","this",[59,580,581],{"class":73},", arrival, quantity);\n",[59,583,584],{"class":61,"line":166},[59,585,93],{"class":73},[10,587,588],{},"Let’s say the current calling code looks something like:",[50,590,592],{"className":52,"code":591,"language":54,"meta":55,"style":55},"stockController.DoSomething();\nselectedProduct.AddStock(DateTime.Now, input.Value);\n",[45,593,594,605],{"__ignoreMap":55},[59,595,596,599,602],{"class":61,"line":62},[59,597,598],{"class":73},"stockController.",[59,600,601],{"class":230},"DoSomething",[59,603,604],{"class":73},"();\n",[59,606,607,610,613,616,620,623,626],{"class":61,"line":77},[59,608,609],{"class":73},"selectedProduct.",[59,611,612],{"class":230},"AddStock",[59,614,615],{"class":73},"(DateTime.",[59,617,619],{"class":618},"sSKRk","Now",[59,621,622],{"class":73},", input.",[59,624,625],{"class":618},"Value",[59,627,628],{"class":73},");\n",[10,630,631],{},"However with a little ingenuity the IDE could examine the new method and map existing parameters based on name and type. If it still doesn’t have enough information consider local variables and properties of the objects it does have to present choices. This works especially well if your parameters are not primitives. Our code becomes:",[50,633,635],{"className":52,"code":634,"language":54,"meta":55,"style":55},"stockController.DoSomething();\nstockController.Add(selectedProduct, DateTime.Now, input.Value);\n",[45,636,637,645],{"__ignoreMap":55},[59,638,639,641,643],{"class":61,"line":62},[59,640,598],{"class":73},[59,642,601],{"class":230},[59,644,604],{"class":73},[59,646,647,649,651,654,656,658,660],{"class":61,"line":77},[59,648,598],{"class":73},[59,650,529],{"class":230},[59,652,653],{"class":73},"(selectedProduct, DateTime.",[59,655,619],{"class":618},[59,657,622],{"class":73},[59,659,625],{"class":618},[59,661,628],{"class":73},[37,663,665],{"id":664},"keeping-it-clean","Keeping it clean",[10,667,668,669,672,673,676],{},"We don’t want our classes being cluttered with deprecated code indefinitely so the solution should contain two extra revision numbers, one detailing the oldest revision of attributes to keep in the source, the other for the oldest revision to compile into the binary. All the ",[45,670,671],{},"[Deprecated]"," marked methods and properties can slip into another file, perhaps ",[45,674,675],{},"Product.deprecated.cs"," so they stay out of sight until needed.",[10,678,679],{},"For .NET it would need somebody at Microsoft to take this on board and move us forward from ObsoleteAttribute as the facility should be cross-tool and so adding it solely to SharpDevelop would be of limited gain.",[10,681,682],{},[105,683,684],{},"[)amien",[686,687,688],"style",{},"html pre.shiki code .s9HRq, html code.shiki .s9HRq{--shiki-default:#F57D26;--shiki-dark:#FF79C6}html pre.shiki code .sXAHl, html code.shiki .sXAHl{--shiki-default:#3A94C5;--shiki-dark:#FF79C6}html pre.shiki code .s6Vpi, html code.shiki .s6Vpi{--shiki-default:#5C6A72;--shiki-dark:#F8F8F2}html pre.shiki code .smiwp, html code.shiki .smiwp{--shiki-default:#F85552;--shiki-dark:#FF79C6}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .snuxY, html code.shiki .snuxY{--shiki-default:#3A94C5;--shiki-default-font-style:inherit;--shiki-dark:#8BE9FD;--shiki-dark-font-style:italic}html pre.shiki code .sciFF, html code.shiki .sciFF{--shiki-default:#8DA101;--shiki-dark:#E9F284}html pre.shiki code .sJQOs, html code.shiki .sJQOs{--shiki-default:#8DA101;--shiki-dark:#F1FA8C}html pre.shiki code .sS4Kt, html code.shiki .sS4Kt{--shiki-default:#8DA101;--shiki-dark:#50FA7B}html pre.shiki code .s7cAX, html code.shiki .s7cAX{--shiki-default:#5C6A72;--shiki-default-font-style:inherit;--shiki-dark:#FFB86C;--shiki-dark-font-style:italic}html pre.shiki code .sKO3f, html code.shiki .sKO3f{--shiki-default:#DF69BA;--shiki-default-font-style:inherit;--shiki-dark:#BD93F9;--shiki-dark-font-style:italic}html pre.shiki code .sSKRk, html code.shiki .sSKRk{--shiki-default:#35A77C;--shiki-dark:#F8F8F2}",{"title":55,"searchDepth":77,"depth":77,"links":690},[691,692,693,694,695],{"id":39,"depth":77,"text":40},{"id":214,"depth":77,"text":215},{"id":391,"depth":77,"text":392},{"id":497,"depth":77,"text":498},{"id":664,"depth":77,"text":665},"Development",0,"2007-10-01T11:32:47+00:00","Refactoring is an essential process to keep code clean and elegant while it evolves. IDE’s offer common refactorings (although somewhat short of those prescribed in Fowler’s excellent Refactoring book and way short of the overall goals explained in Kerievsky’s Refactoring Patterns).","[object Object]","md",null,false,{},"\u002Fblog\u002F2007\u002Frefactoring-shared-libraries-and-public-apis",{"title":5,"description":699},"blog\u002F2007\u002Frefactoring-shared-libraries-and-public-apis",[709,710,711,712],".NET","Visual Studio","C#","refactoring","\u002Fblog\u002F2007\u002Frefactoring-shared-libraries-and-public-apis\u002F",757,"QRsh6Hpi1cRpwknxR1d_dWp_EkBSSS_p9mMvOk6ro88",[717,721,725],{"title":718,"date":719,"url":720},"Transactions in the MongoDB EF Core Provider","2025-10-25","\u002Fblog\u002F2025\u002Fmongodb-explicit-transactions\u002F",{"title":722,"date":723,"url":724},"Queryable Encryption with the MongoDB EF Core Provider","2025-09-22","\u002Fblog\u002F2025\u002Fmongodb-queryable-encryption\u002F",{"title":726,"date":727,"url":728},"Lazy Loading with EF Core Proxies","2025-04-02","\u002Fblog\u002F2025\u002Fef-proxies\u002F",[],1780900531058]