Thursday, May 30, 2019

Why I Don't Teach Unity

I teach college level video game design.  Every semester some student asks about Unity.  Unity is a game development platform.  Essentially, it is a program that does a lot of the game development work for you.  Unity can do things like handling lighting and shadows in 3D graphics.  It can deal with animation for you.  It provides access to a decent collection of art assets.  In all, it can make video game development significantly easier.  In software development though, tools that make things easier always come with a price, and complex general purpose tools can come with a high price.

The first reason I don't teach Unity is that it would defeat the point of the class.  There are two parts to the course.  One is knowledge.  This focuses on design.  It covers things like how to design a game that keeps players engaged, and how to design games with specific goals in mind.  The other part is application.  This is about programming.  Students could learn the design knowledge and apply it to games in Unity, but the course would be more of an art course, not a computer science course if I did that.

The second reason I don't teach Unity is that big game companies (that might be interested in hiring my students) don't use it.  Yes, there are a few large game companies that have used Unity for maybe one fairly small game.  This is not typical though.  Large game companies write their games in languages like C++, mostly from scratch.  If a student applied for a job at a company like Blizzard and cited my game course as a qualification, if I taught Unity, that student would be treated like a second class applicant.  Blizzard is not looking for Unity developers.  Blizzard is looking for developers who can write games without the need for a crutch.  If I want my course to be a good selling point for getting a job in the game industry, I cannot take the lazy approach and teach students to use a game development platform that does all the heavy lifting for them.  They need real experience with real tools.

The third reason is teaching problem solving skills and providing practical experience.  While many of my students do have an interest in becoming game developers, most of them won't end up in that industry.  Learning to write games using Unity will be worthless to these students.  It does not carry over to other applications.  It also solves the hardest problems for them, denying them experience that will help build their problem solving skills.  Writing games from scratch does provide significant experience that will carry over to other applications, and it provides an excellent opportunity to build problem solving skills.

The fourth reason is that relying heavily on 3rd party tools carries a lot of risk and difficulty.  The main problem is that tools designed to be general purpose are rarely ideal for any given specific purpose.  Unity is designed to be suitable for a wide variety of games.  This means that it is tracking data and doing processing that is probably not necessary for any given game.  A game of one genre or style might require something that other games don't really need, but Unity has to provide it to accommodate any game someone might want to make.  If there are things Unity does not provide, that will make it harder or even impossible to make games that need to rely on those things.  This cuts into efficiency, which means that a game written in Unity will almost certainly perform worse than a game written in C++.  For small games, this may make no difference, but for big ones it can limit the market to only users with very high end computers.  This is one element of risk.  The other element of risk is failure.  If Unity has a bug that makes your game not work, you can't just fix it.  Even if you had access to Unity's code, it could take months to learn the code, find the bug, and then fix it.  This is rare, but it is worth considering, and big game companies do consider it, and they don't use 3rd party game development platforms for these reasons.


These are the top four reasons I don't teach Unity.  They do not cover everything though.  Unity is mostly used by small indie developers who are either not interested in hiring or who cannot afford to pay even the low wages of a CS graduate.  It is certainly a useful platform for hobby game makers who like to tinker.  It can even be a good gateway into game development for people without much programming experience.  It is not appropriate for a course in a college Computer Science department that wants to produce high quality graduates who have good programming skills, and it is certainly not appropriate for giving students knowledge and experience that might help them get a job in the video game industry.  This is an industry that gets a ton of applicants, and even a small edge is worth quite a lot.

Unity is not terribly hard to learn.  Like I said, it does all of the heavy lifting for you.  Real game programming requires decent math skills.  Unity does most of the hard math for you.  This makes it trivial to learn compared to real game development.  There are plenty of online tutorials and courses for learning to make games in Unity, which is why indie developers with little or no education in computer science can do it.  People that want to learn Unity have plenty of resources available to them.

I want to provide the best education I can to my students.  Teaching them something that is probably not going to be useful to them does not do that.  Teaching them something they can easily learn on their own with a little bit of study online does not do that.  Teaching them skills that are useful in a variety of places and knowledge that is difficult to find will provide them with the value they deserve.

Wednesday, May 29, 2019

Why I Hate Frameworks

While most of the software development world has been rushing headlong into mountains of awesome frameworks over the last decade-and-then-some, I have been quietly reinventing the wheel.  At least, that is how a lot of developers often see me.  I do not like frameworks.  I avoid using frameworks.  When I am forced to use frameworks, I do not enjoy it.  I find frameworks only cause me pain and suffering.

Reusable code is not a bad idea.  I am a big fan of well written libraries that save me significant time and energy.  That is not what frameworks are though.  I have seen well written frameworks, but the truth is, it tends to be a toss up.  Your odds of getting a well written framework are not better than your odds of getting a poorly written one.  While I have rarely had to peak into the guts of a framework (a distinct benefit of avoiding them), those that I have had to dive into have not been good.  On one occasion, a family member offered me a decent bounty to find and fix a bug in a popular framework.  I spent a few hours trying to get familiar with the code of the framework, but in the end, I realized that I would have to charge several times the bounty offered to make it worth my time.  Over the course of a few hours, I was unable to trace the path through the code that the program was taking.  It was not that the framework was particularly complex.  I can handle complex code.  It was just poorly written.  It might have looked like a nice framework for people who would not have understood what was under the hood regardless of quality, but for an experienced software developer, it was absolute garbage.  On another occasion, I was doing some QA work for a corporation, and I needed to get a little bit more information out of their automated testing system.  Of course, they were using a reasonably popular framework for the automated testing.  It took me days of combing through the framework to figure out where everything was happening, so I could add the critical code to provide the required data.  Again, it was not a matter of complexity.  The problem was that whoever wrote the code was doing things in the most roundabout way possible.  There was a massive amount of coupling.  Everything was touching everything else, and consequently, to understand one thing, I had to understand everything.  Other frameworks I have used are often buggy, and the generally poor design and code quality make it not worth the effort to try to fix the bugs myself.  I have also had a few occasions to look inside general purpose libraries, and I typically find them to be of much higher quality.  This is not always the case, but when I discover a bug in a library, I do not feel the same anxiety when considering whether to try to fix it myself or not.  So the problem is not that reusable code is just a lousy idea.  It is a great idea.  The problem is that frameworks specifically tend to be low quality.

There are a lot of reasons I avoid frameworks.  The worst is the fact that most frameworks require a lot of effort to do even the simplest things.  The theory is that frameworks make everything easier.  The reality is that they tend to make hard things significantly easier and easy things much harder.  For example, the straw that broke the camel's back (and pushed me over the edge, into writing this article) is the ESP-IDF framework for programming the ESP32 micro-controller.  I would not have used the framework voluntarily (I actually prefer manipulating the hardware directly), but it turns out enough of the ESP32 is proprietary, and the FreeRTOS real-time operating system only has the framework implementation available, so I have no other option.  The ESP32 is an excellent micro-controller, with built in wifi and a lot of other really nice features for its price.  The framework, however, is a disaster.  For a previous project, I used I2C on an Arduino, which I programmed entirely in C.  Because I did not want to write my own I2C driver, I took Arduino's driver and ported it to C.  This was mildly challenging, but despite excessive complexity, Arduino's code was written well enough that it was not too much of a chore.  Now I am writing the same program for the ESP32, and the ESP-IDF framework's I2C driver is a nightmare.  I do not even have to port it to C.  It is already in C.  Somehow though, it is actually more work to use than it was to port Arduino's driver to C and use that.  The fact is, I2C is trivial.  It is not a complicated protocol.  (It is not terribly well designed, but it is not complicated either.)  The most difficult part of porting the Arduino driver was eliminating all of the unnecessary object orientation.  ESP-IDF is written for C, so it does not even have object orientation, and yet it manages to be several times more complex.  The fact is, I2C is such a simple protocol that the most difficult part of using it should be setting up the pins on your chip and configuring the clock speed, if your chip has a built in I2C controller (the ESP32 does), but that is not how it works with ESP-IDF.  No, setting up the I2C device is fairly simple (though, the documentation does nothing to help, and the provided example code is stupidly complex).  The first step is setting up a struct to tell it which pins to use, how to initialize them, what mode to use, and what clock speed to use.  The second and third steps are a pair of function calls that really could have been rolled into a single function call.  One extra function call is not a disaster though, and maybe it is somewhat justified, if you need to enable and disable I2C frequently.  That is where the simplicity ends though.  With the ported Arduino I2C library, once you have the thing the initialized, you can call a single function to read and a single function to write.  You give the functions the address, the command, and a few other parameters, and you are good to go.  With ESP-IDF, I2C reads and writes are major productions.  First, you have to tell it that you are starting an I2C transaction.  Next you have to tell it to start accepting data for the transaction.  Then you have to do a pair of write function calls to tell it what you want to write (it may be possible to roll them into one, but all of the examples show it as two).  Then you tell it you are done giving it the transaction data.  Do not think it has actually sent the command though.  After that, you tell it to send the command.  Once that is done, you have to tell it to delete the transaction.  In Arduino, this is all a single function call.  In ESP-IDF, it is a disasterI2C is a simple protocol!  ESP-IDF manages to take something incredibly simple and turn it into something extremely complicated.  This is perhaps the biggest reason I hate frameworks.  This is not a one time thing either.  Even Arduino's I2C driver is overly complicated to use.  In the process of porting it, I eliminated a significant amount of setup cruft caused by using an object oriented approach where it is entirely unnecessary.  In my experience, every framework has places where it takes something almost trivial and makes it more complicated than it has to be.  Libraries sometimes do this too, for example, most high level languages built in libraries make pipes prohibitively hard to use (one of the few places Python really biffs it), but frameworks do this all over the place.  Threading libraries tend to do this as well.

Compared to this, most of my other issues with frameworks are trivial.  The worst one is that when a framework has a bug, it is a lot harder to fix than a bug in code you wrote yourself, even if the framework is well written, because you are not familiar with the framework's code.  I have legitimately abandoned frameworks because they had one critical bug that would have taken me more time to fix than just writing the code myself, from scratch.  Now days, I generally just skip the part where I use a framework in the first place.  Another is that, in my experience, frameworks often take more time to use than writing code from scratch anyway, because learning a framework takes time.  Unless I am going to use most of the features offered by the framework, it is not worth my time to learn it.  I have written AJAX functions from scratch many times instead of using JQuery, because it is actually faster to do that than it is to setup JQuery on my web site and look up how to use its (admittedly high quality) AJAX stuff.  Frameworks also consume unnecessary space, leading to memory bloat, performance costs, and unnecessary bandwidth costs, unless you are using almost 100% of the features provided.  These things may seem trivial, but when you consider the scale on the server side instead of at the individual client, it adds up surprisingly fast.  Frameworks also tend to come and go, especially in web development.  This means that the framework you are learning today probably will not be worth much to you in a couple of years.  It might help you advance in your current company, but most promotion in the software industry is not internal, and that other company you really want to work for probably does not use the framework you are learning for your current job.  This is further aggravated by the fact that there are so many frameworks out there that there really is no point learning one that you do not have an immediate application for.  Lastly, and this is really more important than the last few issues, frameworks are almost never as general purpose as libraries.  Nearly all frameworks are designed with a fairly narrow set of use cases in mind, but then they are advertised with a focus on one design element that is not closely related to those use cases.  For example, responsiveness is a popular feature of web frameworks that dynamically load data.  Most such frameworks are designed for point-of-sale and inventory management, and they tend to be janky or even downright lousy for anything else.  They do not tell you that you should not use them to make a site that needs good quality real-time interaction though.  They do not tell you that the responsiveness comes at a heavy performance cost.  So you decide to use a framework like AngularJS, because "responsive" sounds like exactly what you want for your browser game, but halfway through, after you have spent many hours learning the framework, you discover that while it might work well for business applications (if you are willing to tolerate the making-easy-things-excessively-complicated problem that it also has), it is a horrible choice for games, because what they mean by "responsive" is not the same as what you thought it meant.  (No, I did not actually try to use AngularJS for a browser game.  I did use it for an inventory management system though, where it worked fairly well but where the performance costs occasionally reared their ugly heads in ways that do not matter to an inventory management system but that would make a game unplayable.)

There are a lot of problems with frameworks.  Their popularity makes it clear that a lot of people are willing to put up with those problems, for the perceived benefits, but in my experience, most of those benefits are ethereal.  Recent research suggests that system and development architectures make a much bigger difference in success rates than development stacks anyway.  This means that any benefits gained from using a particular framework will almost certainly be dwarfed by improving team communication and spending more time on quality design work.  My personal experience supports this conclusion.

What it all comes down to is that if you really want to use a framework, pick one with features you are going to use, and know what you are getting yourself into.  Personally, I find it easier and faster to code the features I want myself, and I find that the time spent finding and learning a suitable framework ultimately costs me more than just writing the code myself.  You might say I am reinventing the wheel, but the fact is, when there are a million different kinds of track, you need a million different kinds of wheel if you want all of your trains to run smoothly.