Heavy usage of static classes and methods, for simplicity and better performance
It’s hard to draw conclusions but a codebase like this sounds painful to work in. But clearly it kills for performance. In this regard, though, it sounds like they are holding themselves back by staying in C#. Why not use a language that allows lower-level calls but maintaining a superior abstraction layer than static methods. I’m not sure what exactly that language would be. Maybe Go? C++ would allow it, for sure, but at a clear cost.
While I’ve never worked on the StackOverflow codebase (though I’ve read through earlier versions), I have plenty of experience with Kiln, which is written similarly. So, a couple of thoughts:
You mention Go as an alternative. C#, written with a lot of static classes and methods, looks almost exactly like Go. In fact, if you make liberal use of extension methods and value types, you would find that C# looks like Go with explicit interfaces, but that the two languages otherwise look very similar.
Except that C# also provides robust functional interfaces, a great coroutine story that can exist alongside or in addition to Go-style CSPs, full generics, LINQ, and a bunch more. Indeed, part of the benefit of C# is that it can, in fact, be all about low-level calls, and abstractions, at the same time. C# even allows you to dive into flat-out pointer code with manual memory management and struct manipulation if you want to, and to then jump into high-level abstractions like LINQ, on adjacent lines. This means you can trivially pick the right level of abstraction for a given piece of code, and can cleanly move from the higher-level versions to the lower-level versions as the code demands it. Rust and Nimrod are the only two languages I can immediately think of that provide a similar ability to mix very high-level and low-level code so easily.
I can’t vouch for whether the StackOverflow codebase is easy to work with, and I’d strongly discourage anyone writing a new C# app to start by writing against sealed classes and static methods. But I hardly think the experience has to be unpleasant, and in fact think it can serve as a great example of how to cleanly scale from an app maximizing readability to one maximizing performance.
I’m not sure that Go is so much (or at all) faster than C#. In my experience, it’s also not as nearly as easy to work with. All the little rules about using every included module start to wear down. C# tolerates “work in progress” code much better. And the toolchain (particularly debugger) is top notch.
“C# with static methods” sounds a lot like “C with C# niceties”.
The go toolchain seems like it’s getting there as far as OSS toolchains go, although it’s hard to imagine anyone ever getting to the level of the dotnet toolchain.
Edit: Full disclosure, I spend all of my time in scala, where the toolchain is so anemic (outside of a few jvm bits, like yourkit, or jstack, which often don’t play too well with scala) that anything looks impressive in comparison.
All the little rules about using every included module start to wear down
What do you mean by this?
“C# with static methods” sounds a lot like “C with C# niceties”.
Yeah, that might be true. But it just seems like you’re binding your hands by doing it this way. But I have not looked at their code so maybe it’s great.
At first I was impressed that they only had 25 servers, but now I’m less impressed because they are incredibly beefy.
It’s hard to draw conclusions but a codebase like this sounds painful to work in. But clearly it kills for performance. In this regard, though, it sounds like they are holding themselves back by staying in C#. Why not use a language that allows lower-level calls but maintaining a superior abstraction layer than static methods. I’m not sure what exactly that language would be. Maybe Go? C++ would allow it, for sure, but at a clear cost.
While I’ve never worked on the StackOverflow codebase (though I’ve read through earlier versions), I have plenty of experience with Kiln, which is written similarly. So, a couple of thoughts:
You mention Go as an alternative. C#, written with a lot of static classes and methods, looks almost exactly like Go. In fact, if you make liberal use of extension methods and value types, you would find that C# looks like Go with explicit interfaces, but that the two languages otherwise look very similar.
Except that C# also provides robust functional interfaces, a great coroutine story that can exist alongside or in addition to Go-style CSPs, full generics, LINQ, and a bunch more. Indeed, part of the benefit of C# is that it can, in fact, be all about low-level calls, and abstractions, at the same time. C# even allows you to dive into flat-out pointer code with manual memory management and struct manipulation if you want to, and to then jump into high-level abstractions like LINQ, on adjacent lines. This means you can trivially pick the right level of abstraction for a given piece of code, and can cleanly move from the higher-level versions to the lower-level versions as the code demands it. Rust and Nimrod are the only two languages I can immediately think of that provide a similar ability to mix very high-level and low-level code so easily.
I can’t vouch for whether the StackOverflow codebase is easy to work with, and I’d strongly discourage anyone writing a new C# app to start by writing against sealed classes and static methods. But I hardly think the experience has to be unpleasant, and in fact think it can serve as a great example of how to cleanly scale from an app maximizing readability to one maximizing performance.
I’m not sure that Go is so much (or at all) faster than C#. In my experience, it’s also not as nearly as easy to work with. All the little rules about using every included module start to wear down. C# tolerates “work in progress” code much better. And the toolchain (particularly debugger) is top notch.
“C# with static methods” sounds a lot like “C with C# niceties”.
The go toolchain seems like it’s getting there as far as OSS toolchains go, although it’s hard to imagine anyone ever getting to the level of the dotnet toolchain.
Edit: Full disclosure, I spend all of my time in scala, where the toolchain is so anemic (outside of a few jvm bits, like yourkit, or jstack, which often don’t play too well with scala) that anything looks impressive in comparison.
What do you mean by this?
Yeah, that might be true. But it just seems like you’re binding your hands by doing it this way. But I have not looked at their code so maybe it’s great.
The solution to this “problem” is goimports: https://github.com/bradfitz/goimports