{"id":39102,"date":"2019-05-21T12:25:32","date_gmt":"2019-05-21T17:25:32","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/appcenter\/?p=39102"},"modified":"2019-05-26T17:13:10","modified_gmt":"2019-05-26T22:13:10","slug":"moving-from-node-js-to-net-core","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/appcenter\/moving-from-node-js-to-net-core\/","title":{"rendered":"Moving from Node.js to .NET Core"},"content":{"rendered":"<p><span data-contrast=\"none\">Here on\u00a0<\/span><span data-contrast=\"none\">Visual<\/span><span data-contrast=\"none\">\u00a0Studio\u00a0<\/span><span data-contrast=\"none\">App Center, our platform is built as a set of microservices, which has afforded teams to make language and platform choices that work best for them, and ultimately allowed us to move and iterate quickly. Over time, two distinct stacks have emerged:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<ul>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"3\" aria-setsize=\"-1\" data-aria-posinset=\"1\" data-aria-level=\"1\"><span data-contrast=\"none\">TypeScript\/JavaScript running on Node.js, deployed to AKS (<\/span><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/kubernetes-service\/\"><span data-contrast=\"none\">Azure\u00a0<\/span><span data-contrast=\"none\">Kubnernetes<\/span><span data-contrast=\"none\">\u00a0Service<\/span><\/a><span data-contrast=\"none\">)<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<\/span><\/li>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"3\" aria-setsize=\"-1\" data-aria-posinset=\"2\" data-aria-level=\"1\"><span data-contrast=\"none\">.NET Framework (C#) running on Windows Service Fabric<\/span><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:60,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<\/span><\/li>\n<\/ul>\n<p><span data-contrast=\"none\">As we have continued to merge our teams and do more and more cross-team work, i<\/span><span data-contrast=\"none\">t\u2019s become\u00a0<\/span><span data-contrast=\"none\">apparent that we should settle on a single choice and unify so that jumping between services\u00a0<\/span><span data-contrast=\"none\">is<\/span><span data-contrast=\"none\">\u00a0less jarring. The fact that a lot of our customers run non-Windows platforms, and our team runs a mix of Mac, PC, and Linux, led us to explore a new direction.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<h5 aria-level=\"2\"><b><span data-contrast=\"none\">Enter .NET Core<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/h5>\n<p><span data-contrast=\"none\">We started investigating .NET Core as a solution because of the cross-platform abilities it offered, and the fact that it works great with VS Code, our cross-platform development solution. We felt like .NET Core allowed us to preserve this flexibility, but not continue diverging our code base,\u00a0<\/span><span data-contrast=\"none\">with\u00a0<\/span><span data-contrast=\"none\">some\u00a0<\/span><span data-contrast=\"none\">new<\/span><span data-contrast=\"none\">\u00a0services written in TypeScript, and\u00a0<\/span><span data-contrast=\"none\">others\u00a0<\/span><span data-contrast=\"none\">in C# on the .NET Framework. This allows us more flexibility when moving work between teams as well. We could have also gone with TypeScript but given the skill set of\u00a0<\/span><span data-contrast=\"none\">the majority of<\/span><span data-contrast=\"none\">\u00a0the team (C#)<\/span><span data-contrast=\"none\">\u00a0and<\/span><span data-contrast=\"none\">\u00a0the pain we have seen managing Node.js dependencies, we decided on .NET Core. We\u2019ll discuss the\u00a0<\/span><span data-contrast=\"none\">dependency issue<\/span><span data-contrast=\"none\">\u00a0in more detail later in this post. For more information on .NET Core, see the\u202f<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/aspnet\/core\/?view=aspnetcore-2.2\"><span data-contrast=\"none\">docs<\/span><\/a><span data-contrast=\"none\">.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Our goal with this post isn&#8217;t to convince you that .NET Core is better than Node.j<\/span><span data-contrast=\"none\">s:<\/span><span data-contrast=\"none\">\u00a0<\/span><span data-contrast=\"none\">w<\/span><span data-contrast=\"none\">e made our decision primarily based on the skill sets of our team<\/span><span data-contrast=\"none\">.\u00a0<\/span><span data-contrast=\"none\">Instead, we want to share the things that we noticed as we have been starting the porting work, in the hope that if you&#8217;re embarking on a similar journey, this information will be helpful. If you&#8217;re curious about direct comparisons regarding performance,\u202f<\/span><a href=\"https:\/\/www.techempower.com\/benchmarks\/\"><span data-contrast=\"none\">this site<\/span><\/a><span data-contrast=\"none\">\u202fis a good resource that is constantly updated. The .NET Core entry on that list is called\u202f<\/span><span data-contrast=\"none\">aspcore<\/span><span data-contrast=\"none\">.\u00a0<\/span><span data-contrast=\"none\">Another interesting post explores<\/span><span data-contrast=\"none\">\u202f<\/span><a href=\"https:\/\/www.ageofascent.com\/2019\/02\/04\/asp-net-core-saturating-10gbe-at-7-million-requests-per-second\/\"><span data-contrast=\"none\">saturating 10GbE at 7+ million request\/s<\/span><\/a><span data-contrast=\"none\">\u202fusing .NET Core.<\/span><span data-contrast=\"none\">\u00a0<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Also, n<\/span><span data-contrast=\"none\">ote that for the purposes of this post we aren&#8217;t looking at specific TypeScript features, and on our team, we continue to use TypeScript for new front-end development as there are many benefits to having concrete types on top of JavaScript.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<h5 aria-level=\"2\"><b><span data-contrast=\"none\">The Challenge<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/h5>\n<p><span data-contrast=\"none\">We are not embarking on a re-write of all services, but aside from having all new services run on .NET Core targeting AKS, we are looking for opportunities to port over problematic code from either the .NET Framework or Node.js. In this blog post we&#8217;ll talk about going from Node.js to .NET Core, by exploring some key differences we noticed as we ported some targeted REST API endpoints. For our take-aways, we are mainly focused on the developer experience, and not necessarily on performance benchmarks or other orthogonal concerns.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<h5 aria-level=\"2\"><b><span data-contrast=\"none\">Key Take-Aways We Noticed<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/h5>\n<p aria-level=\"3\"><b><span data-contrast=\"none\">Porting code into a strongly typed language takes time<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">In our existing\u00a0<\/span><span data-contrast=\"none\">TypeScript\u00a0<\/span><span data-contrast=\"none\">code base<\/span><span data-contrast=\"none\">,<\/span><span data-contrast=\"none\">\u00a0we take advantage of dynamic types\u00a0<\/span><span data-contrast=\"none\">frequently<\/span><span data-contrast=\"none\">, and in the C# version of the code we had to define more concrete types. Additionally, the code that we were porting specifically dealt with bit masks, so translating those concepts between JavaScript and C# presented some pain for us. One of the main things we lost was easy parsing of JSON and dynamic object graphs. .NET Core includes JSON parsing out of the box, but it isn&#8217;t quite as native as what JavaScript provides.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">Another thing that came up was the need to create more mapping types instead of relying on dynamic mapping, which we didn&#8217;t have to handle as much since we were already in TypeScript, but if you are coming from a straight JavaScript project this is something else you will want to consider.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">All in all, if you embark on this journey, be prepared to invest the\u00a0<\/span><span data-contrast=\"none\">appropriate<\/span><span data-contrast=\"none\">\u00a0amount of time on model classes and other scaffolding that you will need for a successful transition.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p aria-level=\"3\"><b><span data-contrast=\"none\">String comparison differences<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">In JavaScript, when doing case insensitive string comparisons, we tend towards doing\u202f<\/span><span data-contrast=\"none\"><span class=\"font:consolas lang:default decode:true crayon-inline\">toLowerCase()<\/span>\u00a0 prior to doing a <span class=\"font:consolas lang:default decode:true crayon-inline\">===<\/span>, <\/span><span data-contrast=\"none\">where we don&#8217;t care about case. Generally speaking, the equivalent C# code uses\u202f<span class=\"lang:default decode:true crayon-inline \">string.Equals<\/span>\u00a0<\/span><span data-contrast=\"none\">\u202fand passes in\u00a0<\/span><span data-contrast=\"none\">a\u202f<span class=\"lang:default decode:true crayon-inline \">StringComparison<\/span>\u00a0<\/span><span data-contrast=\"none\">\u202f<\/span><span data-contrast=\"none\">enum<\/span><span data-contrast=\"none\">\u00a0value to indicate how to handle issues of case sensitivity.\u00a0<\/span><span data-contrast=\"none\">A common case is scenarios where you do not care about case and locale, and for those you should use <span class=\"lang:default decode:true crayon-inline\">StringComparison.OrdinalIgnoreCase<\/span><\/span><span data-contrast=\"none\">. H<\/span><span data-contrast=\"none\">ere are some useful references for other scenarios that may arise:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<ul>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"4\" aria-setsize=\"-1\" data-aria-posinset=\"1\" data-aria-level=\"1\"><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/standard\/base-types\/best-practices-strings\"><span data-contrast=\"none\">Picking a\u00a0<\/span><span data-contrast=\"none\">StringComparison<\/span><span data-contrast=\"none\">\u00a0Member<\/span><\/a><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<\/span><\/li>\n<li data-leveltext=\"\uf0b7\" data-font=\"Symbol\" data-listid=\"4\" aria-setsize=\"-1\" data-aria-posinset=\"2\" data-aria-level=\"1\"><a href=\"https:\/\/haacked.com\/archive\/2012\/07\/05\/turkish-i-problem-and-why-you-should-care.aspx\/\"><span data-contrast=\"none\">Issues with the Turkish\u00a0<\/span><span data-contrast=\"none\">i<\/span><\/a><span data-ccp-props=\"{&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:60,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<\/span><\/li>\n<\/ul>\n<p><span data-contrast=\"none\">A concrete example of porting where both examples return success under the same conditions:<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p>JavaScript:<\/p>\n<pre class=\"lang:default decode:true\">var desiredValue = 'someValue'; \r\n\r\nif (envVar &amp;&amp; envVar.toLowerCase() === desiredValue.toLowerCase()) { \r\n\r\n  console.log('Success!'); \r\n\r\n\r\n}<\/pre>\n<p>Equivalent C#:<\/p>\n<pre class=\"lang:default decode:true\">var desiredValue = \"someValue\"; \r\n\r\nif (string.Equals(envVar, desiredValue, StringComparison.OrdinalIgnoreCase)) { \r\n\r\n  Console.WriteLine(\"Success\"); \r\n\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p aria-level=\"3\"><b><span data-contrast=\"none\">Fewer dependencies in .NET Core<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">One thing that has been nice is to manage fewer dependencies (and a shallower dependency tree) in the .NET Core version of the code, and hopefully this means it will be easier to maintain. We have struggled with keeping dependencies up to date and managing\u00a0<\/span><span data-contrast=\"none\">many<\/span><span data-contrast=\"none\">\u00a0dependencies in our Node.js projects. Additionally, developers on our team prefer NuGet&#8217;s policy of &#8220;lowest matching version&#8221; rather than\u00a0<\/span><span data-contrast=\"none\">npm&#8217;s<\/span><span data-contrast=\"none\">\u00a0encouragement to use &#8220;highest matching patch&#8221; or &#8220;highest matching minor version&#8221; (using the\u202f<\/span><span data-contrast=\"none\">~<\/span><span data-contrast=\"none\">syntax in\u202f<\/span><span data-contrast=\"none\">package.json<\/span><span data-contrast=\"none\">\u202fby default via\u202f<\/span><span data-contrast=\"none\">npm<\/span><span data-contrast=\"none\">\u00a0<\/span><span data-contrast=\"none\">i<\/span><span data-contrast=\"none\">). We recognize this is a philosophical difference and opinions on this may vary. The pros of the NuGet approach are more control and explicitness, whereas the\u00a0<\/span><span data-contrast=\"none\">npm<\/span><span data-contrast=\"none\">\u00a0approach allows for theoretically non-breaking changes to be easily applied.<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p aria-level=\"2\"><b><span data-contrast=\"none\">Other Thoughts<\/span><\/b><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559738&quot;:360,&quot;335559739&quot;:240,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"none\">There isn&#8217;t one right answer, and the purpose of this post isn&#8217;t to convince you one way or another, just to shed some light on the process we have gone through recently as a\u00a0<\/span><span data-contrast=\"none\">team. As we discover and do more in .NET Core<\/span><span data-contrast=\"none\">\u00a0on AKS<\/span><span data-contrast=\"none\">\u00a0we will be sure to share along the way!<\/span><span data-ccp-props=\"{&quot;134233117&quot;:true,&quot;134233118&quot;:true,&quot;201341983&quot;:0,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here on\u00a0Visual\u00a0Studio\u00a0App Center, our platform is built as a set of microservices, which has afforded teams to make language and platform choices that work best for them, and ultimately allowed us to move and iterate quickly<\/p>\n","protected":false},"author":4454,"featured_media":38034,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[16],"tags":[],"class_list":["post-39102","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-mobiledev"],"acf":[],"blog_post_summary":"<p>Here on\u00a0Visual\u00a0Studio\u00a0App Center, our platform is built as a set of microservices, which has afforded teams to make language and platform choices that work best for them, and ultimately allowed us to move and iterate quickly<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/posts\/39102","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/users\/4454"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/comments?post=39102"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/posts\/39102\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/media\/38034"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/media?parent=39102"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/categories?post=39102"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/appcenter\/wp-json\/wp\/v2\/tags?post=39102"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}