This is a repost from my personal blog...
Last weekend, I showed a very early behind-the-scenes preview of what will eventually be the next version of CoasterBuzz (the fifth major revision in 12 years) to some folks that participated in the PointBuzz off-season tour at Cedar Point. It's very early in the process, but I thought it would be fun to talk about some of the things that have changed over the years. This series of posts probably won't mean much to non-code-monkeys, but it's fun for me to write about and create some record of what I'm doing.
First, a little background. CoasterBuzz was first built in 2000 using the old ASP scripting platform, which was awful. The second version came shortly thereafter. It was awful in part because I wasn't particularly good at writing code, and had only been doing it for two years, without any mentoring. It wasn't until 2003 that it became an ASP.NET application, and that major platform change required a total rewrite.
Version 4 took even longer... more than five years, launching again in September, 2008. It ditched the home-made CMS that I built, but still recycled a lot of the underlying code. Nearly ten years ago, I liked to build data objects that included data access built into them. I wouldn't describe this as an awful practice, in part because I wrote about it in my aging book, but it's not great for testing. By that time, I had largely embraced test-driven (or test-influenced) development, but I just wasn't up to rewriting everything.
What that version did do for me is create a relatively stable platform to expand on. While it has been more than three years since a major revision, I've been able to add a ton of features that had real impact on traffic. I've been mostly happy with it.
However, there is still a bit of legacy in there, as old as nine years. The other thing that happened, just a few months after I launched, was the beta release of ASP.NET MVC, the framework that runs on top of ASP.NET to bring development back to what HTTP really is. I've been living in that world ever since. In fact, I did a total rewrite of the forum app in MVC, and POP Forums is available open source. There is a whole stack of goodness at my disposal now, from jQuery to EF. These are all things I'll write about. I don't have a timeline in mind yet for vNext of CoasterBuzz.
Right now, I wanted to talk about WCF, and how I've expunged it from my life. The promise of WCF has always been that you could expose and consume services wrapped in whatever protocols, and it would just work and be easy. Maybe that's not the promise, but that's what I was sold. I think what we got was something that's completely dependent on configuration, either in code or XML, and almost never works the first try. And if that weren't enough, putting in Azure has been a pain, in my experience.
Still, I decided to give it an earnest shot when I built a Silverlight-based feed watcher for CoasterBuzz. I liked that it could be out-of-browser, work on my Mac, and I needed a science project for Silverlight. Getting both ends of the chatting to work was a pain, but eventually I got it working. That was years ago, and whatever I've done with Silverlight since has been for Windows Phone, some DeepZoom stuff, and a file uploader.
Fast forward to today, and the next thing on my to-do list was to port over the service for the feed to vNext. It didn't work. Same config as far as I could tell, and obviously I didn't change the client. It just didn't work. It threw all kinds of ambiguous errors that didn't make sense. It was all coming back to me about how much I struggled to make it work the first time.
When I was still working at Microsoft, on MSDN/TechNet stuff, we built an API for one of our services using MVC. Think about how stupid-easy it is to make a RESTful endpoint using MVC. You can define the route and literally consume and respond with any bits you want. And it doesn't take long to do, either. It's no wonder that, a few years later, we see Web API surfacing with the next version of MVC.
So with the annoyance of WCF, I added a route to serve up the feed as JSON, and borrowed the helper class I had written for the CoasterBuzz Windows Phone app to talk to it, and I was done.
In a lot of ways, WCF shares an issue with ASP.NET Webforms. It tries to abstract away something to an extent that it starts to obscure the original problem. Both are fundamentally trying to shield you from the process of sending bits over a wire and doing something with them, and both turn it into something entirely different. HTTP isn't really that complicated in the first place, so why make it harder?
The common thread you'll see in a lot of my writing about this re-write is that I'm doing more with less code. Ditching WCF for simple JSON via MVC was a no-brainer. Next time, I'll talk a bit about replacing ASP.NET AJAX with jQuery.
Download the bits from CodePlex here. Visit a live demo (preferably on a mobile device) here.
This is an extremely experimental build of POP Forums v9.3, which includes jQuery Mobile and mobile views baked in for mobile formatting sauce. It requires ASP.NET MVC 4 Beta, which you can download here. Of course, feel free to submit bugs to the issue tracker.
See a live demo here: http://popforums.com/Forums
- Uses jQuery Mobile and the ASP.NET MVC4 beta to provide mobile-optimized views.
- Numbers are formatted (sensitive to culture) when 1,000 or higher.
- CSS is more integration friendly, and specific to the ForumContainer element.
- FIX: Bug in topic repository around caching keys for single-server data layer.
- FIX: Pager links on recent topics pointed to incorrect route.
- Update to jQuery v1.7.1.
- Replaced use of .live() with .on() in script, pursuant to jQuery update, which deprecates .live().
- This is a first attempt at using jQuery Mobile, and as such, it's not great.
- AJAX loading is explicitly turned off, for now, until I spend more time understanding what it does and how it caches stuff. Kind of important in an ever-changing forum.
- Scrolling to newest post doesn't work because of how jQuery Mobile uses URL hashes. See previous point.
- Integrating with an existing site is messy.
Like a lot of Web publishers, I depend pretty heavily on AdSense to generate revenue and pay the bills. A good server isn't free. So imagine my horror when, on Thursday, the numbers dropped between 50 and 80%, depending on the site. Something is horribly wrong.
Nothing has changed at my end, so I'm working on the assumption that they changed something. I haven't yet figured out what. My suspicion is that it has something to do with the async code they switched to recently for DFP, but I can't be sure. Analytics and DFP show consistent numbers, and those are also Google products.
Here's what pisses me off the most though: I can't even contact them. I can make a post in their forums, which no one ever reads, and that's pretty much it. Unless you're in the top 1% of earners, humans are not available to answer your questions. I might be small, but I'd like to think after tens of thousands of dollars in revenue for them over the years, I'm at least entitled to a form e-mail.
I hate crap like this. Google, you suck.
I've noticed that I write software in one of three modes:
- For myself: Shortcuts, less testing, not well-factored.
- For myself but in public: Mostly POP Forums, which I try to avoid letting it suck since others will use it and see the code.
- For sharing: Any day job or gig where others will use or maintain your code. You don't want to unleash crapsauce on others.
I have to admit that second case isn't the most clean of endeavors. While I'm generally happy with the forum app and the feedback I get for it, it needs some refactoring in places. The thing that bothers me the most is that a lot of the controllers do way too much. This is particularly obvious because of all the mocking required for the tests. It's not like I've got inline SQL in there, but they could definitely stand to delegate stuff to other stuff.
One of the inevitable things you end up doing at some point in your ASP.NET MVC controller is access the HttpContext in some way. Because the cats that build the MVC framework are so smart, they actually used HttpContextBase as the type for the HttpContext property on the Controller base class. That already makes life a little easier, because you don't have to mock out a huge graph of objects to test what it is your controller is doing.
I suggest that you can take that one step forward. For example, say that you need to molest your model a little before you return it to a view, but only if the client is a mobile browser. It's actually pretty easy to check, but here's the Boolean that tells you:
var isMobile = HttpContext.Request.Browser.IsMobileDevice;
Simple enough, but it's also pretty deep in the object graph for mocking. It also assumes you'll never do any logic more complicated than checking the property. Since you're already using dependency injection in your controller (and if you're not, shame on you, go read up on it), it might be easier to hand off this logic to something else. So in this example, perhaps you have a class called MobileDetectionWrapper, which implements IMobileDetectionWrapper, and looks like this:
public class MobileDetectionWrapper : IMobileDetectionWrapper
public bool IsMobileDevice(HttpContextBase context)
Super simple example, and what might seem like a needless abstraction, but this is far easier to test. Inject the IMobileDetectionWrapper into your controller, and now you simply mock that. Your test might look something like this (using Moq here):
var mobileDetection = new Mock<IMobileDetectionWrapper>();
mobileDetection.Setup(x => x.IsMobileDevice(It.IsAny<HttpContextBase>()).Returns(true);
var controller = new MyController(mobileDetection.Object);
var result = controller.MyActionMethod();
// test the result here for whatever
And just in case you're unclear about the way the controller looks:
public class MyController : Controller
public MyController(IMobileDetectionWrapper mobileDetection)
_mobileDetection = mobileDetection;
public ActionResult MyActionMethod()
var isMobile = _mobileDetection.IsMobileDevice(HttpContext);
// do stuff
Get it? I freehand-typed that without Visual Studio or Intellicrack, so hopefully it's right. :) In any case, the dependency injection resolver you have set up in your MVC app news up the controller with the concrete implementation of IMobileDetectionWrapper and you use it in your action method. Again, trivial example, but imagine you had to do other special things, like check for a cookie and identify an iPad, and therefore return a false value instead of true for the IsMobileDevice question. The necessary mocking in the controller is significantly less.