I Never Intended on Starting a Blog
Tuesday, May 07, 2019

This article is a work in progress...

So the thing is, I never actually intended to start this blog. I wrote the exact same blog engine three times using three different stacks as an exercise. I wanted to see how long the same project would take me in three different stacks – and whether any differences in time were due to my level of proficiency with the given stack or due to inherent differences in the stacks themselves. Ultimately, when I was done, I decided to keep the blog engine around and use it for some random musings of mine... so here we are.

The Stacks
I wrote the (almost) exact same blog engine in:

  • ASP MVC (C#) with SQL Server and Entity Framework
  • Python+Django
  • Node.js with Express and MongoDB+Mongoose

TL;DR: All three projects took basically the same amount of time. I didn’t time them to the minute, but no one project took considerably longer than any other project. For the most part, for any one area that took longer in one stack, something else ended up being a bit quicker than in the other stacks, so it was more or less a wash.

At the end of the day, the goal should be to pick the right tool for the job as a first priority. All things being equal, when no right (or wrong?) tool stands out, go for personal preference because, hey, we’re doing this because we enjoy it, right?

Some Observations

The bulk of my full-time work is done in .NET, so setting things up was second nature with the ASP MVC project. Creating the project, knowing exactly which packages to pull down, knowing which boilerplate code to keep vs. toss out, etc... it was all very quick.

With Python, Django was an obvious choice, so there wasn’t much to consider in terms of ecosystem here. I personally find myself more productive when I can scaffold out even a small sample app versus an empty app, so I cheated here a bit. I used Visual Studio to scaffold out its boilerplate Django app to give me a bit more to work with than the default Django start project, and then I popped back to VSCode to do the work.

With Node, I figured Express was an obvious choice. The traditional MEAN stack uses MongoDB, and I was keeping the front-end very simple (so no “A” here), so I was left with more of a “MEN” stack (insert Tim "The Tool Man" Taylor grunt here). I considered using MySQL to keep all three databases relational but ultimately stuck with MongoDB. I guess you could say I cheated here a bit, too: I pulled out the default Jade template engine and replaced it with Vash (which looks much like Razor) to give myself something more familiar to work with.

The Database Side
I’ll be honest: I prefer the database first approach. I’ve done it this way for a long time, and it just feels more natural to me. This is sometimes considered a more ".NET'y" way of doing things (though even EF Core uses code first migrations) but I like it. At very least, I'd even take database first with migrations. Being able to design my database first here was a win for me.

With the Entity Framework, every time I made changes to my database, I had to update my EDMX model to reflect my changes, but that wasn't a big deal here. The data model was small, and I didn't have too many changes to make throughout the design. I kept the EDMX model in a second project just for the sake of good design, and I didn't modify any of the EF-generated model classes directly (never a good idea), so I never really lost anything from build to build here.

Given that my data model was pretty small, I was OK with doing all of my entity models code first in Python and just snapping them into the Django admin console. There is an inspectdb function within Django which could have turned my SQLite database into model classes, but I didn't bother – especially considering that there's also mixed opinions on whether you should even deploy SQLite to production vs. switch over to something like PostgreSQL. From my perspective, for an almost zero-traffic blog which is 99%+ "reads" against the database, I'm sure I would have been just fine with SQLite in production.

With MongoDB, I wasn't even sure initially if I wanted an ORM in place. I figured to keep things on a level playing field, I would use one, so I opted for Mongoose. I did ask myself a few times if the end goal was the same deliverable or the same project, and I kind of decided my goal was roughly the same project and approach versus simply the same end result, so I went with an ORM here.

Admin Views & Scaffolding
Visual Studio provides the ability to scaffold CRUD views and controllers given a data model, and this saves a lot of time – especially if you don't need much beyond basic functionality. If this were going to be used by other people, I would have done some extra work in making the backend look nicer than what's generated by default, but I didn't really have a need for a polished backend.

The only caveat here, of course, is that if (when) your data model changes, you'll either have to re-scaffold things or make the updates manually. I knew this going in, so I tried to make as few manual changes to the scaffolded views and controllers as possible until my data model was sound.

With Python+Django, the fact that the admin side is always in sync with my models is a big win – and one of Django’s strongest features, I’d say. Any extra time I spent hand-tweaking models via code was made up for here by having to do so little with the bulk of the admin side.

With Node.js (Express really), I actually didn't know off the top of my head what was out there with regards to CRUD scaffolding, so I did a few quick searches. I sometimes find the vast ecosystem around Node.js a bit of a double-edged sword: for something large (like database connectors, charting and graphing libraries, file format converters, etc.), the time spent exploring the available options is going to be well-spent (you couldn't write any of these quickly even when compared to the time it takes to explore a slew of open-source libraries).

For something like scaffolding CRUD views/controllers, however, when nothing stable jumps out as obvious, I figure I could probably just write my own in the time it would take to explore a few third-party options. In this case, that's just what I did. I did cheat (again!) here... Since I wrote the ASP MVC version of the project first, I used the HTML of the views as a starting point. This would have taken me longer if I hadn't of cheated here. Either I'd have spent more time looking at existing solutions or I'd have been writing views by hand from scratch.

Moving Outside the Box

After getting the bare-bones views scaffolded, it was time to start putting together some of the custom work. I needed an asset manager for uploading and managing files, and I wanted some type of rich text editor. The file manager I was going to write myself, and I was going to use CKEditor for the editor. I also wanted a "save and continue editing" feature everywhere – something I really like in the Django admin panel.

I like working close to bare metal when I can. I often opt to "write my own" versus hunt down something pre-written (but that's a topic for another blog post).

Being close to bare metal served me well in ASP MVC (and Node.js). The CRUD pages were all scaffolded out and looked more or less the same, and replacing textareas with instances of CKEditor was a no-brainer. The asset manager was done completely from scratch and didn't look like the scaffolded CRUD pages, and that wasn't a problem.

Django, however, was another story. Unfortunately, I find Django's greatest strength its biggest weakness. The Django admin is great unless and until you need to customize it. Now, before I get trounced: the Django docs clearly indicate that the admin tool is intended for internal use and there are hooks for customization. That said, in Django, I can't just fall back to bare metal (read: fundamentals); there are Django-ish (Djangoan?) ways of doing things, and you kind of have to do it Django's way.

Now I'll be the first to admit: I have far less experience with Django than I do with .NET, so some of what I was trying to do may very well be possible, but regardless, I had to look for "Django's way" of doing it versus being able to fall back to fundamentals. This slowed me down.


I didn't include the time to get the finished apps actually deployed to production in my timings, and that's probably a good thing, or these results would have been quite a bit different.

Given that I'm running IIS, the ASP MVC project was the easiest to deploy. There are always a few settings to tweak, like picking the correct (integrated pipeline) AppPool and granting IIS access to upload folders, but I've done this many times, so I knew exactly which settings needed to be in place.

The Django and Node.js apps, on the other hand, were not so straightforward...

There are a few articles and videos regarding deploying Django to IIS, but for starters, none of them agree with each other. Sure, there may be more than one way to skin this cat, but no one way stood out as preferable (or easier). One approach involved setting up a FastCGI application within IIS and adding some Django environment variables, and two involved different web.config approaches, both centered around setting up an HTTP handler. The end result is probably the same in each case, but the steps involved in any case were definitely not something I could remember if I had to do this again.

I ultimately got the thing running, only to then find out the admin side wasn't fully functional, so I found a few other necessary tweaks to get static files served correctly. I finally thought I had it working until I realized my URL routing wasn't working correctly. I opted to host my Django app in a subdirectory, and this caused issues. There were some creative ways around this I'd found after doing some digging, but I ultimately just included the subdirectory name in my routes to put this thing to bed. I wasn't proud of it, but it was done.

Getting the Node.js app up took longer than the ASP MVC app, but not as long as the Django app. I had to install iisnode, read the docs to figure out how to get things configured to route .js files to Node.js for only part of the site, and I was good to go. I did have the same issue regarding running this out of a subfolder, so I just (as before) updated my routes to include the subfolder. Again, not what I'd do for a true production app, but it got things working.

Given that I wasn't even initially planning on keeping or using the app(s), I'd figured I'd spent enough time on disposable projects, so I called it a day.

Blog engine and all content © 2024 Jason Plackey