Ten years of fastcgi++
April 10, 2016
It’s been about ten years since I started the fastcgi++ project and over these last couple months I’ve been thrust back into it and forced to reflect upon it’s development and usage. Doing a quick Google search reveals what I consider be to many misconceptions regarding the project, it’s motivations, it’s design choices, it’s status, and it’s future. I am writing this with the intention of addressing much of this and bringing to light some of softer non-technical realities of fastcgi++.
The Story
So how did fastcgi++ come into existence? The reality is a little less grandiose than some might think. It was actually a learning exercise for me. I had recently acquired Stroustrup’s The C++ Programming Language and Langer’s Standard C++ IOStreams and Locales and was eager for a project to try tackling as I went through the books. I’ve always found having a large project in parallel with book learning to be the best way to go. At the time I was in my early twenties and had never actually done any real programming or software design beyond using Small-C to cheat at Counter-Strike. I hadn’t yet done any sort of university or college studies in the field. I was as green as they get. Web development was something that had interested me and I’d done a little play stuff with PHP. I learnt of FastCGI and quickly noticed a lack of any sort of C++-centric library so I thought “this is the perfect project”. The output of request data was the perfect fit for an iostreams implementation.
The learning process turned out to be great for me. I quickly realized the holes in C++ that could be filled with boost. I discovered CVS, SVN and finally Git. Migrating the project between those versioning systems was quite the experience. Soon I discovered Doxygen and was again totally blown away. There was so much new information wafting into my brain that it was virtually impossible to make any sort of objective design choices. All such choices were made entirely out of inexperience.
So I hacked away at it for a few years and accomplished exactly what I’d set out to do: I learnt the shit out of C++ and software design in general. Then something happened that I did not expect. Other people started using it. Obviously this caught me by surprise as it was just my hack learning project. At first I didn’t know how to react but then I thought, “what a great learning opportunity!”. I played the role of an experienced developer all the while secretly using the feedback and questions from the community for my own personal development. Certain people to this day stand out in my mind as mentors, all the while thinking it was the other way around. This is a reality of open-source software and it’s community that just doesn’t get talked about enough. I didn’t release fastcgi++ for the good of the community; I did it for myself. The community afforded me one of the greatest learning opportunities of my life and I was never even made to feel like a student.
Then life happened. I pursued a BSc. in Electrical Engineering. I met the love of my life. I had two children. I took a step back from fastcgi++. I had gotten everything I wanted out of it. I even developed a few Web applications using it. I stayed somewhat present, answering the odd question and helping the odd person. I guess I felt an obligation; fastcgi++ was like my first child. The codebase, like everything in life, got old. C++0x/11/14 came along. Boost components were changed and obsoleted. Newer compilers didn’t like the code as much as they used to. As I studied in university and continued to work I realized some of the design choices I made were questionable at best. So I unconsciously “let it go free into the woods” and it effectively became another of the thousands of orphaned open-source projects floating around the Internet.
Fast-forward to a few months back and something happened that I did not foresee: fastcgi++ has immense value in another project I’m involved in and I’ve got lot’s of time to spare. I’ve decided to do a complete overhaul of the entire codebase and migrate the project away from a lot of the ageing and inconsistent technologies it had become dependent on. Beyond that, I Google fastcgi++ and realize that a number of people out there are still using it! They are chatting and blogging about it, abiet sometimes quite negatively. It has been packaged into some GNU/Linux distributions. So to all those people, this is directed at you.
The Lessons
At risk of sounding preachy, I thought it would be useful to get into some of the lessons I learnt from fastcgi++. This is in no way complete or authoritative but upon reflection these things really stand out in my mind.
Keep it standard
Just because your code compiles and works perfectly doesn’t mean much. An “engineering” approach where every outcome of your code is defined by the particular language standard you are using is far more resilient. There were so many elements of the library that worked perfectly when I wrote them. Then GCC evolved, new versions of the C++ standard came out and boom, everything breaks. When the rude jackasses on ##c++ at Freenode say what you are doing is only implementation defined, listen to them. If you want your code to continue effortlessly working as intended for years to come, keep it standard.
Readability is prime
This is something I had a lot of trouble coming to terms with. I would always come up with code solutions that I felt were so clever and optimized. I had an emotional attachment to them. The inevitable result of this was, of course, difficult to understand code. I eventually came to terms with the idea that good programming doesn’t mean optimizing the shit out of everything. It means communicating your goal to both the compiler and human readers in a concise and elegant way with as little ambiguity as possible. If you do that, you give the compiler the tools it needs to optimize away. Stroustrup said that C++ is a programming language that should require very little commenting and he was right; it’s just that the code should be written with that in mind.
Don’t ever optimize boolean logic in code. I know it’s awful tempting to put that slick Demorgan’s theorem you learnt in school to use but it just obfuscates the intent. The compiler will do it for you.
Break your lines
I remember when I first got a 16x9 monitor I was excited that I’d be able to display extra long lines of code. What a misplaced sentiment that was. In the field of typography it is a quite well accepted that a line of text should not exceed 80 characters. There is no reason this shouldn’t apply to code as well. When you throw templates into function declarations with numerous arguments, C++ can give you some brutally long lines if you don’t break them up. Breaking a line at just the right spot can improve readability dramatically. Keep it under 80.
Choose your dependencies wisely
When I first started using Boost I didn’t fully understand the fractured nature of the project. Some parts of it, like the date/time and thread facilities, were well defined and implemented. They basically ended up getting rolled right into C++11. Other parts, not so much. The first implementations of fastcgi++ used standard iostreams but I started to realize I could accomplish my goals far easier using boost::iostreams. Unbeknownst to me, it had become an orphan of it’s own; nobody was maintaining it anymore. I had found a critical bug in it that was causing fastcgi++ to fail. I submitted a patch to Boost that completely fixed the bug only to learn that without the maintainer it was impossible to apply the patch. Beyond that, things that worked with old versions of boost no longer worked with new versions.
The tangled web of interdependencies we’ve woven is delicate. We all know this but it can be difficult to resist the temptation to link against some shiny new library you’ve found on the Web. I know this is somewhat ironic since fastcgi++ is a library itself. I guess it’s not so much about avoiding dependencies all together but taking a good long look at whether they are worth it. Now if I’m vetting a library I’d want to be sure it has the following attributes:
- A narrow and clearly defined scope.
- Very much “kept to the standard”.
- Emphasis on maintaining a consistent and mature API as it evolves.
- Ensures it’s own dependencies follow suit.
There are no gurus
I found when I first got into this I expected to someday reach some sort of guru status where I knew it all. Problems would come my way and I’d instantly have the perfect solution. People would ask me questions and I’d always have the perfect answer. Debates would arise regarding best practise and I’d always emerge victorious. I really don’t think anyone actually achieves that status. They might think they do, but that only makes it less true. We are all in a continuous state of learning and sometimes the greenest amongst us have the most valuable insight to offer.
Unit tests
We all get told this when we start out, and we all ignore it: Developing good unit tests and test driven development in general make everything much much easier. I had a lot of trouble with this at first but my life sure got much easier when I finally came to terms with it. Writing small units of code, formally testing them and then washing your hands of them is such a stress reliever. If you find a bug later on that your unit test didn’t catch, amend the test so it does. That way the bug will never return. Above all, this will ensure that your code ages ever so gracefully.
The Plan
So here’s what everyone is interested in. If you actually read everything leading up to this I thank you for your time. Basically I’m in the process of doing a full re-write of the library. I’m emphasizing the lessons I outlined in the previous section and will be narrowing the scope of the library. Here’s a quick itemization:
- Moving the library to CMake. Boy CMake just kicks the crap out of the old GNU build system.
- The project is moving out of GNU Savannah and into Github. Again, Github just kicks the crap out of Savannah.
- No more boost or any dependencies for that matter. The library will be C++14 and that’s it. It will rigidly adhere to the standard and compile with -Wall and -Werror.
- Any platform specific stuff (sockets) will be isolated into very narrowly scoped interfaces for ease of porting. I plan to specialize this for both generic POSIX and Linux (epoll) while providing a very easy mechanism to pursue the same for Windows.
- No more single thread for all requests. You’ll now be able to specify how many threads worth of request handling you want. Ten years ago this wasn’t so relevant but it is very relevant today.
- The ability to easily add other handlers to the managers task queue.
- Ditching ASql and some other superfluous stuff. ASql was always kind of a piece of junk. I may pursue it again but as a completely independent project. Gotta keep that scope narrow.
I know this doesn’t address all the criticism I’ve read about the library but as far as I’m concerned the rest are somewhat uninformed. I’ve always felt the documentation and tutorials are really quite well done. The premise that the library should allow for one thread per request is patently absurd. The goal was always to get away from that flawed design and allow requests to give up execution control when they don’t need it.
Anyway, if you use fastcgi++ and you’re interested in how things proceed check out the project on Github. Once the new codebase is done and in a beta stage I really hope to get people trying it out.