Monday, January 19, 2009

Why dispose is necessary and other insights on managed code

Jeff Atwood has once again jumped the shark, this time about managed code. Then there's this little gem from twitter:

I believe .Dispose() is a form of optimization, which is necessary *sometimes*
but not *always*. Anyone got links with evidence otherwise?

So for my next trick, I am going to show Jeff (and everyone else) how managed code works and when it fails.

First, I always have a saying when introducing managed code:
Managed code isn't.

The reason that I say this is that most people think that because your code is managed, you don't have to think about memory management at all; you can just allocate whatever you feel like and the garbage collector will just take care of it for you. According to Jeff, this is a good thing. It is indeed a good thing, although unfortunately it doesn't work that way, and if you don't know precisely what the CLR is doing to your code and how garbage collection works, you're going to have problems and when they occur you will have no idea where to look for solutions. The goal of this post is to try to educate you as to what is going on. I will not be able to go into a lot of depth in one post, however this should get you started and give you some resources to do further exploration when you run into these problems in your code. Let's start with looking at dispose().



For those of you who are unsure, dispose() is an implementation of the disposal pattern. The idea behind this is that you have a way to mark a class as having an explicit cleanup that needs to occur prior to that object being deallocated. .Net accomplishes this by an object implementing the IDisposable interface with the Dispose() method, which you should call when you are finished using an object so that it can free its resources. This method should invoke the same code as when Finalize() is called (more on finalization below), so if someone forgets to call Dispose(), it is still guaranteed to be cleaned up prior to garbage collection occurring. Therefore, it is a good place for cleaning up unmanaged resources, like database connections or open streams (e.g. files and network connections). That way you are 100% guaranteed that this cleanup will occur, even if the programmer simply stops using a particular object and lets it fall out of scope.

Using(disposal pattern)

This brings us to our second useful piece of code, which is the using() {} block. This block allows you to put something that implements IDisposable (henceforth "disposable") inside the using() keyword, and then upon leaving the basic block defined by the using statement (i.e. going outside of the {} by any means), the dispose() method WILL be run prior to the jump that occurs. This means that inside my using block I can return, break, exit, throw, or otherwise jump to another block of code and before that code exists, dispose is called on the object in the using() statement. These can be nested, in which case objects are disposed in FILO order (first in last out). These also work with finally blocks so that if I have a try..finally with a nested using block, the dispose is called prior to the finally, even in cases where code inside the using block throws an exception dispose is still called first. You can test this quite easily by writing a simple app with nested using blocks and then throwing from inside one of them and catching outside of it.



So why does using() exist?


When I interviewed for Microsoft, one of the questions I got asked was "Is the using() statement syntactical sugar?" My answer was that yes, it is, but it is important. Here's why: I define syntactical sugar as any language keywords that perform a function that you could still perform without using that specific keyword(s) but the implementation would be uglier, more difficult, or less likely for a programmer to use properly. For example, I could still close database connections with a try . . .finally block and explicitly call dispose(). In fact, if you look at the MSIL code generated for a using() block you'll see that it generates a try . . .finally block. I could even not have IDisposable and just have a cleanup method somewhere else or even explicitly define it. The nifty thing about using() is that I can just wrap an object around it and be guaranteed that the object will be disposed of properly in a deterministic fashion (with finally, it's impossible to know when dispose will be called because you don't know if an exception will be thrown until runtime and exception handling runs before finally does).


In addition, what if I forget to call dispose in the finally block? What if it is far away from where I use the disposable object so I don't see it right away? What if I need to ensure the order that dispose is called in for multiple objects and I mess that up? What if exception handling is significant to how I handle disposal? There are a lot of what-if's here and the using() statement basically give me a convenient shortcut while making my code a bit more clean.


So how does this tie into garbage collection now and why call dispose() at all?
The garbage collector mostly runs whenever it feels like it and what it does is entirely up to the garbage collector. Let's look at Jeff's SqlConnection cleanup example and see what's going on here:

sqlConnection.Close();
sqlConnection.Dispose();
sqlConnection = null;

So first, we call Close(). This has nothing to do with garbage collection, presumably it just closes the connection itself. Then we call Dispose(). Presumably, Dispose() would clean up an open connection, probably by calling Close(), but it's entirely possible that it does other things as well. According to MSDN, Close() and Dispose() are redundant, so it looks like Jeff is violating DRY here, although I think that having Close() and Dispose() do the same thing is ugly and misleading code. Finally, he's setting the object = null. Remember kids, this isn't actually setting the value of the object, he's just setting his current reference to it to null, which tells the garbage collector that he's not using it here anymore. If there are no other references still in scope in the rest of the application, it means that the garbage collector is free to clean it up (in unmanaged code, it would mean you have a memory leak).

Let's look at a non-real, contrived example; if I grab a connection to a db and I'm using winsock to connect, I'll get a handle to the socket that I'm using from winsock, which will be wrapped in some sort of SafeHandle object to ensure that it gets cleaned up. If Close() only closes the socket, it may not clean up the handles. If Dispose() calls Close() and then cleans up the handles, then it's entirely possible that the Close() and Dispose() calls are redundant, but that they do different things. Maybe if you call Close() you can re-use the connection object. That might be useful, particularly if that object takes a long time to create or is resource-heavy even when not connected (like if it allocates buffers for example). Ultimately, what you do depends on how you implement your object, so be sure that you have a good design and don't encourage bad code with redundant calls.

One other problem can exist: when an object is Disposing, what if somewhere you have another reference to that object and that reference is in use that you don't know about? If you let the garbage collector just run Dispose for you then obviously this won't be a problem, but if an eager developer calls it, you definitely have a problem. I think that if you have this problem, probably your code is not well designed, but there's a cheap way to manage it if you think it's necessary: create a private "bool isdisposed" property and when you call dispose, set this property immediately at the start of the Dispose() method (and make this thread-safe through correct use of some sort of locking strategy). Then, on every other method call, you can check the value of this property and then react accordingly, although again I would label this as code smell.

And then there's the object = null
Jeff mentions the confusing choice of calling dispose and setting null but in reality, if you know what these actually do, there's no confusion at all. If you call Dispose(), as I have mentioned at least six times now, it will run the cleanup of the object when you call it. If you set the object to null, then all you're doing is removing a reference to it and the garbage collector will then see that there are no more references to this object so it's ok to clean it up. If it wants to. When it does do cleanup, it will call Dispose for you, however it is completely non-deterministic as to when this occurs, so leaving it to the garbage collector for things like database connections or files or network connections is a bad idea since it would be easy for you to have a bunch of open connections this way, which can cause all kinds of bad problems.

So now, how is disposal "more of an optimization than anything else?"

It's not. Jeff is wrong because Jeff is right (different Jeff though. . .) and Jeff (me) says that calling Dispose is absolutely necessary so that you can have deterministic behavior in your application in terms of resource deallocation. There are lots of resources that you're using in the .Net framework, even if you are not aware of the fact (things like threads, files, even graphics tend to open handles into unmanaged code). If you are not careful about when you dispose things, it's easy to start leaking these, particularly in high-performance applications. It really isn't an "optimization" to call Dispose when you want to explicitly free up resources, particularly unmanaged ones.

Also, if you have some significant amount of work that has to be done to free up those resources, then you really should do that work at a time of your choosing. If you don't, then potentially any time you create a new reference type, the garbage collector may run and may collect something that you aren't using anymore, at which point all of that work that needs to be done to free your resources will just happen, which may affect the performance of your app in various ways, none of which will make it go faster. This isn't optimization though, it's simply good coding style.

So what should I do in my code?

First, if you allocate any resources in an object, particularly unmanaged ones, you should clean them up in your Dispose() method (while ensuring that your object implements IDisposable). I find it unusual that your objects will need to allocate external resources, but it's likely that you will consume some without realizing it, which means that you should either dispose of them explicitly inside the object when you're through with them, or that you should call their Dispose() methods from inside your object's own Dispose() method.

Next, if you have another method like "Close()" or "Shutdown()" or "DieYouGravySuckingPigDog()" then you should ensure that these methods don't do any disposal for you. These types of methods aren't meant to clean up an object so that it can't be used anymore (that's what Dispose() is for). If you have an object, then at any point in the object's lifecycle it should be usable in some way prior to calling Dispose(), and not-usable after calling Dispose(). Make sure that a disposed object won't try to do anything bad if you accidently call it after you've disposed it also, generally by writing good code where you don't attempt do this, but some checking in your object won't hurt. If you want to reuse objects a lot, consider using an object pool (a buffer pool for handling Socket reads would be a good idea as an example).

Finally, remember that just because you set an object to null doesn't mean that it's going to be disposed at any point in the near future, it is just being marked as no longer in use (unless you have another reference to it somewhere else). This is not a strategy for garbage collection

So what about Finalize()?
It turns out there is another method called Finalize() that is used by the garbage collector for cleaning up unmanaged resources. It's purpose in life, according to MSDN, is to clean up unmanaged resources in the event that Dispose was never called, but if you implement Dispose you should NOT have a Finalize and you should use GC.SurpressFinalize(this) to stop the finalize method from being called. This is to ensure that you do not do duplicate work that is not needed (generally the code called in Dispose and Finalize should be the same). There is also an object destructor, but for managed objects you should not use it. Again, this is all according to MSDN, and I would trust MSDN on garbage collection more than a person who used to code and stopped doing that to write about it.

Wait, wait, I didn't get that. What should I do? Can you sum this all up?
  1. If you are using something that is IDisposable, you should call Dispose() when you are finished with it and when it is a good time in your app to do so performance-wise. Just letting the garbage collector do it for you is not a strategy for anything except crappy coding (if crappy code is your goal, then never call dispose and you'll be about 80% there)
  2. If you are implementing an object that uses unmanaged resources, you should make that object IDisposable and free those unmanaged resources and then call GC.SurpressFinalize(this) in your Dispose() method. Ensure that calling Dispose() multiple times does not throw an exception or duplicate work (setting a private property is a useful idea here)
  3. Setting an object to null is not a strategy for garbage collection. It is a strategy for crap.
  4. If you have an object that is re-usable after calling some sort of closing method (like a connection, file, etc.) then consider pooling that object, particularly if that object is expensive to create or destroy. Remember, every time you allocate a reference type you may trigger garbage collection and you'll never know it.
  5. "Managed code" isn't managed all that well; you need to be absolutely aware of what the CLR is doing to your code and how garbage collection works. It's like walking on a tightrope- managed code gives you a safety net but that doesn't mean you should just jump into it whenever and then climb back up and keep going. That is not a strategy.
  6. Every time codinghorror posts something that jumps the shark, check out http://agilology.blogspot.com/ for clarification and amusement.

Tuesday, December 23, 2008

Alt .Net Seattle Open Spaces is on!

I just heard back from Digipen about using their facility for the Alt .Net open spaces this year and they are once again graciously willing to host the event. Therefore, it's looking like it's going to happen the weekend before the MVP summit, which is Feb 27 - Mar 1, 2009. I don't have a lot of details yet, but last year's open spaces event was phenomenal and this year is going to be at least as awesome also. Keep watching the blogosphere for details. I'll post updates here as well as link to other information once it becomes available. Registration hasn't opened yet but here's the site: http://altnetseattle.pbwiki.com/

Saturday, December 20, 2008

Can't blog now, I have a class to teach!

So it's official, I will be teaching the networking class (CS 260) at Digipen starting in January. This was a bit of a surprise to me (a friend of mine recommended me for the job), but I'm really excited about it. This will be my first official foray into the classroom, so I'm looking forward to the experience and more importantly the chance to teach and influence about 100 aspiring developers (and now would be the time where some of you probably have a heart attack). I've done plenty of public speaking and teaching on a much smaller scale, so I view this as a challenge to my current abilities but not something that is beyond me.

Why am I doing this? Well, it goes back to something that Scott Hanselman said back at Alt.net Seattle last year. I don't remember his exact words, but he said to go out to your local schools and talk about programming and that engaging the next generation of programmers is important. Well, you can't say I wasn't paying attention.

So the obvious question is, why me? Well, to start with, I work with networking every day. I am on the Network Class Library Team at Microsoft, and we are the owners of System.Net, which is where pretty much all low-level networking functionality lives in the .Net framework (technically it lives in winsock and we call into it via P/Invokes). I'm not a game programmer (although I originally started coding to write games) but I've written a few games in my day (tetris clone, text-based RPG, all the common ones that you start tinkering with) so I at least have a good idea about how these things work, and games are very non-trivial to write, even the simple ones.

Also, networking for games isn't horribly different from networking for anything else in terms of passing data, however there are a LOT of interesting considerations in making a networked game. For example, naggling is absolute death for a game. Sending a dataset that is too large is also death because of a few reasons, two of which would be that longer packets are more prone to errors and collisions, and that larger sets of data are more likely to get sent as part of multiple packets, which can delay your messages even further and cause more lag, especially at lower layers than TCP/IP that you're probably not even aware of. On top of that, things like NAT and proxies are a huge problem. Finally, games are real-time sytems, and networks by definition do not have real-time, there is ALWAYS a delay of some type so handling that can be very painful as well.

So, what's the hardest thing about teaching a networking class? So far, it's creating the fucking syllabus! I'm not talking about writing it out and all the class expectations, grading policies, and all that stuff; that's easy. The question is, how do I go from week one to week fifteen and teach people who may or may not know anything about networking how to program networked games? There are obviously a lot of topics that I should cover, and I could probably spend 15 weeks on any one of them. The problem is that I have to figure out what isn't as important and what can be cut out as well as what acedemic information I need to include.

I've made a few choices so far. First, I'm only spending about an hour on the OSI model. It's not horribly useful and TCP/IP, which is the dominant protocol out there right now, doesn't really map very well onto it, despite what lots of people say.

Next, I'm going to jump straight to the top of the TCP/IP model and introduce winsock so that the students can start writing code, then we'll work our way down from there. I will have at least three projects, but I'd like to have four if my current plan for a third project goes the way I intend it to. I won't say what they are (in case some of my students are reading my blog. Yes, I know you are AND I know how you figured out that this is my blog AND I know who told you about it) but they're going to be a lot of fun and a huge challenge. Everything in networking is multi-threaded, it pretty much has to be or you'll get stuck listening and unable to talk or talking and unable to listen. Or maybe you'll just take some of your data and then start doing something with it and meanwhile, your 8k buffer that winsock has is starting to fill up and all of a sudden you're rejecting packets.

Beyond that, I won't say too much, although I will be posting about my experiences here periodically. Anyone who has any ideas or input for the class, please feel free to post up in the comments. I'm really looking forward to this and I hope that my students learn something valuable from it (and I hope they enjoy the class, too, but I'll take learning over enjoyment in this case).

Monday, September 15, 2008

Visual Studio quirk- it works on my box

I encountered an interesting Visual Studio thing today. Someone sent me a bug with a repro. I ran it on my machine and started stepping through the internals of the framework to see what the issue is. It worked fine. Hmm, that was interesting, I wonder why? So I run it again and this time I don't step through it and it fails. Ok, that's strange. When I just execute the code, it fails, but if I step into it and don't do ANYTHING except step through the code, it succeeds. WTF?

Well, I had forgotten about something: Autos and locals. In Visual Studio, when debugging, the debugger creates watches on local variables as well as a few things it just watches automatically. In order to get the values of these, it has to evaluate them, and therein lies the problem: If evaluating any of those variables causes any side effects that don't occur during the normal running of the application, it can cause unexpected behavior. Here's an example:

Let's say that I have some state property on my object that is initialized to null. I have a method that depends on this state property being set. That state property is set when you access another property somewhere. Assuming that property is not accessed in the code path that I'm executing, the state property will not be set. HOWEVER, if I trace through the code and evaluate the property that sets the state property, it will end up setting my state, thus changing the way my code executes. Let's look at a concrete example:

class testthing
{
private string s = null;

public string PropString
{
get { if (s == null)s = "new"; return "new";}
set { s = value; }
}

public bool forceit = false;

public bool DoSomething()
{

if (forceit)
Console.WriteLine(PropString);

return s == null;
}

}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting run");
testthing t = new testthing();
bool x = t.DoSomething();

Console.WriteLine("result was: " + (string)(x ? "true" : "false"));
Console.ReadKey();

}
}
So the class testthing has a property PropString that doesn't set the internal value of the private field s until the Get is called. Therefore, if you never call PropString.Get, it never sets the value of s, and DoSomething() will return true because s defaults to null. Run the example code and observe this, it's pretty straightforward.

Now, run it a second time, except this time put a breakpoint on the first line in DoSomething(). When it breaks, hover over the Console.WriteLine(PropString) so that it forces the debugger to evaluate PropString. Now, execute the rest of the code (f5) and observe the output is false, because the debugger has executed the getter of PropString which had a side-effect.

So, the next time you debug an application in Visual Studio and it works in the debugger but not in the code, look at your variables within the method throwing the exception and see if any of them could possibly be changed through evaluation. If so, then you may have found the problem.

One final word: If you have a unit test for something like this, your unit test will fail since it won't evaluate the property when it's run. It would be far easier to write a failing unit test around the method that has the bug and then figure out why it's failing than to step through the method and hope that you can see where it's going wrong.

Thursday, September 11, 2008

Let's just blame Microsoft!

This is a good one. Some guy named Steven J. Vaughn-Nichols is blaming the Sept. 10th London Stock Exchange crash on .Net. Wow, informative! It crashed. it runs on .Net, so that must be the reason. .Net isn't suited for real time systems! Right? Not so fast, dude.

Full disclosure

Before I start, let me just say that I do work for Microsoft and I work on the .Net framework. Does this make me biased? Probably, but I'm going to attempt to focus on other things besides "Microsoft good, .Net good" here and draw a logical conclusion.

What happens

So, what's the scenario? Well, apparently (according to Steven) the LSE runs some software called TradElec, which is a c# application. It also runs on Windows 2003 with Sql Server 2000. Clearly, the weak point is .Net here, nothing else it could possible be. Right?

You are full of fail


So Steven probably wrote all those "conclusions" down on a mat, which he then placed on the floor, so that he can "jump" to them. He clearly has. Something broke, so it's Microsoft's fault, because .Net just sucks for real-time applications. So does Sql Server 2000 and Windows Server 2003. There's nothing else that could have gone wrong, right?


There's no way it could be human error. No way at all

What he doesn't say is that this could possibly be programmer error. There are thousands of ways that a programmer could mess this up and just write crappy code. For network connections, the Asynchronous programming model is not trivial and requires some reasonably deep understanding before you can really make it work well for you. I see a lot of people mess this up, and unfortunately it's their fault and their problem most of the time because the performance you get through asynchronous programming comes at the price of being complex and involving multiple threads, which is something that a lot of people just don't understand.

Additionally, we don't know how they're doing their DB access here. Maybe they have some sort of transaction hell that's locking the shit out of their DB. Maybe they don't use stored procs (BIG performance issue in Sql2k, fixed in Sql2k5 so not a big deal there). Maybe they don't know how to create an index. My point is that we don't know, so we can't say for sure. Probably, however, this is an issue.

Finally, the .Net framework itself has some interesting quirks if you don't really understand the CLR well. I don't usually recommend books on specific software technologies, but go out and get a copy of CLR via C# by Jeffery Richter; I learned more about the CLR in that book in a month than I did in two years of using .Net every day. Granted, garbage collection takes away a lot of the complexities of memory management, which can be a big performance issue, however as a developer you STILL need to understand what the CLR is doing. Things like boxing and unboxing can take time, mis-using value types and reference types eat performance, even how you allocate objects can affect performance. For example, if you're using buffers for network traffic, if you allocate a new buffer each time, you may trigger garbage collection which will randomly hurt performance and be difficult to track down. if instead you allocate a massive pool of buffers and then just use those, they will live on the large object heap and they will NEVER trigger garbage collection so your app will be more consistent.

Blame Canada . . .um. . . er. . . .Net?

So do we blame .Net? With this much information, we really can't. It's far more likely that Sql 2000 is to blame (if anything), although I've seen shit databases created in open source just as often as MS Sql so it's entirely possible that it was just designed stupidly. It's also equally likely that the people who wrote this just screwed up, either in writing the code or improperly testing it. Again, these things would happen if the same programmers used open source software.

Wow, what a useful solution!!!

What does Steven suggest? Use linux. Wow, that will fix everything! I'll just go install it right now, with KDE and everything!!! Wait, no.

Next, he suggests Oracle. I've used Oracle and in some ways I love it way more than MS Sql Server but in other ways I hate it a lot. Oracle is better than Sql2k but I have yet to see proof that it's better than Sql2k5, however I won't pass judgement on that yet. Maybe Oracle would be a better db choice. Not that Oracle's open source or anything. It also works with .Net. I've used it.

Next, he recommends Java. Java, with the worst threading model in the history of the world (more on that later), is his recommendation for a fix! I have yet to see a case where a Java application works significantly better than a .Net application doing the same thing. A lot of the tools are similar. The languages are similar.

In conclusion, Steven is jumping to conclusions that Open Source software (+Oracle) is better for performance. He has no evidence other than "it was running .net and it crashed" to base this on. He is therefore wrong. I have an idea. So you take this mat, and you write various "conclusions" on it, and put it on the floor, so you can "jump" to them. I'll send him one!

And I KNOW it wasn't a .Net networking issue because

I am on the NCL team at Microsoft. We own the System.Net namespace, which is what handles networking in the .Net framework. It was my turn to handle issues that came that week. If it had been a .Net issue with networking, I would have heard about it. I heard nothing.

Tuesday, July 29, 2008

Teaching Java in school is just as controversial as an interview with Justice Gray

I read a great article today here that was a follow up to an interview here with a Computer Science professor Robert Dewar of New York University. I'll stop for a moment while you go read the articles. Both of them. Like I did. All done? Good, now we can have an intelligent conversation about them, and I have to say that I absolutely agree with Prof. Dewar's main points about today's graduates. (disclosure: I have a degree in Computing and Software Systems from the University of Washington. While Java was used for some things, the majority of my degree was in c++).

One of the main arguments that he makes is that Java has a lot of libraries. A lot. To quote Prof. Dewar as quoted from the article (not sure how to cite a quote from a quote correctly so just pretend that I did):

“If you go into a store and buy a Java book, it’s 1,200 pages; 300 pages are the language and 900 pages are miscellaneous libraries. And it is true that you can sort of cobble things together in Java very easily…so you can sort of throw things together with minimal knowledge,” he says. “But to me, that’s not software engineering, that’s some kind of consuming-level programming.”


Now this is absolutely true, and that's one of the strengths of Java is that there are a lot of libraries for everything. The same can be said of php, .Net, and perl. In fact, .Net has CLR implementations of functional programming as well (F#). There are also thousands of applications out there written in Java, .Net, php, and perl. True, none of them keep airplanes in flight or help launch the space shuttle. However, they do help trade stocks, manage companies, guard private health data, run military equipment, and create useless social -networking sites. So, what's the problem then? Is it that Java isn't as popular anymore in the business world? (Hint: no). Is it that Java is the wrong language to teach students how to program? (hint: no). Is it that we're not teaching computer science properly? (hint: warmer). Well, if you want my opinion, and since you're reading this blog I'm going to assume that you do, then my Answer-with-a-capital-A is:

WE'RE NOT TEACHING THE WRITE THINGS WITH THE RIGHT TOOLS!!!11!1!!11!one!1

Allow me to explain a bit.

Computer Science is too hard so only nerds can take that class

CS is intimidating for a lot of people. Computers are scary. Computers are complicated. Computers are for nerds who stare at the command prompt all day and never see the sun. These are all things that I've heard from non-CS majors.

Except that people in Math, Chemistry, and Physics need to take that class too

When I was in school, there were also a number of degrees that required the introductory computer programming classes (that's CSE 142 and the follow up cse 143 at the UW). I took the cse 142 equivalent class at community college when I was in high school through the running start program. The class was conducted in c (not c++) and I got credit for cse 142 by taking it as well as a year of science credits for my high school so that I could free up an extra period for a year to do nothing. The class was challenging but not overly so (in my opinion) and something that helped was that the class had about 20-25 people in it so there was a lot of opportunity for students to get individual help from the professor.

I took cse 143 at the UW my freshman year (way back in 1999 so now you all know how old I am) and it was in c++ at that time. I already knew c++ but that class was still challenging, even for me. I recall the first quiz we had, I got something like 44 out of 100 and I was still two full standard deviations above the mean. A lot of people dropped the class. I remember a project that was our first big exposure to objects and it was a dll-hell type situation and almost no one (including me) could get the code to actually build and link. The TA's couldn't get it to work. My friend Kevin who was a graduating senior in CS couldn't get it to work. The professor finally said that we should all just turn in what we had and if it didn't build or link, he'd grade more easily on this assignment. This almost made me hate computers. It did make a large number of students in that class say "fuck it, this sucks" and drop.

These are the problems that Java is trying to solve

Java doesn't have dll hell. It had an IDE that is well supported. It's free. It can be run all platforms easily enough and doesn't require special changes to the code to get it to build on different platforms. The syntax is reasonable friendly. It does have a lot of libraries and stuff, but it's still able to implement most data structures and algorithms that you'd commonly find in such classes, such as linked-lists, b-trees, heaps, hashtables, etc. as well as common searching/sorting algorithms. Now, students only have to worry about their code and making it work. There is no (or minimal) frustration with things like getting the damn compiler to work or worrying about environment. If a student likes solving problems in code, they may now choose to pursue that as a degree instead of getting overly frustrated with dealing with their build tool or IDE or whatever. The thoughts of the universities are clearly in making CS not look as intimidating at first and I think that Java solves this problem about as well as it can be solved.

How Java creates at least 10 new problems (that's 10 in like base 50)

The first problem is how students are taught using Java. Just because Java has 100000 different libraries doesn't mean that you have to use them. Ultimately, most of those libraries are written in Java, right? So that's the first problem: when you start teaching data structures and algorithms, you must actually teach them; you can't just let people use the libraries to build applications. Maybe a good example for teaching hashtables would be to show the Java HashTable class and write a working application that stores and retrieves values from one, but write that application against the Map interface (which hashtable implements). Now, write your own Hashtable class that implements Map and the output from your implementation and the Java HashTable class should be the same. Repeat for other data structures and algorithms. Wow, problem solved.

So now that the students learned Java, we can just do everything in Java, right?

Wrong. You just can't teach everything in Java. You can teach some things in Java. I had an operating systems class that was taught about 50% in Java (the other 50% was c++ on a linux kernel). It was taught in Java because illustrating concepts with multi-threading is much more complex in c++. Doesn't mean that I don't know how to multithread in c++, but it was much easier to debug this crap in Java, which let me focus on the concepts of an OS. The same is true of file IO- we had to implement a virtual file system, which Java made easy by handling the actual file IO for me, however we only got one really big file, and inside that file we had to have our inodes and our data and we had to create a "file" implementation that would simulate reading and writing to our big "file." Again, this helped me understand the concept without having to worry about formatting an actual hard drive and interfacing with it. Here's the point:

Java allows you to focus more on the concept that you're trying to study without having to spend a lot of time working on the tools and environment. If focusing on the concept is not dependent on the tools or environment, then Java is an acceptable choice.
Learning about memory management in a language that manages memory for you is hard

This is probably the biggest point in which Java breaks down as a teaching tool. In order to be a developer, you have to have a solid understanding of how the machine handles things like memory and what implications it has on your program. You don't have to worry about memory at all in Java, so this makes it an inadequate tool for the job. A language like C is really the best teaching tool here since you have to do everything manually. In fact, I think the best way to really understand how memory works is in assembly, where you can actually look at the addressing modes and see the difference between them. This helped me understand pointers more than anything else, which brings us to:

You can't learn about machine language without an actual machine

Java is a virtual machine, but we want to know about actual machines. Having a strong working knowledge of the principles behind how computers work is critical, especially when something goes terribly wrong. You probably won't ever use assembly again in your career after college. You will probably never write a driver or a compiler. However, if you are using these tools (yes, I consider a compiler to be a tool), it's important that you know generally how they work because if they ever don't work, you'll never be able to figure out why. You need to learn this by actually looking at hardware and how it's built. You should be able to design a relatively simple logic-based circuit. You should know how these circuits are used to make up a computer. These things aren't that hard if taught well (and I was taught well so thanks Arnie if you're reading this). Assembly language is how the hardware and software interface, so it's pretty important that you learn it as well. I learned motorola 68000 assembly which I think is much simpler than x86 assembly but still illustrates the points well. I now know the difference between
int a = 5
int* b = a
int* b = 5
is really in what assembly instructions are emitted (hint: it's the same instruction but the addressing mode changes). This helps me understand how memory works in programming, and that helps me to ensure that I write programs that don't leak memory (or references in the case of managed code because it's a similar concept).

And now, the things that are missing from the education system.

What I (and many other people) think is missing is a good foundation in object-oriented programming. Most people get a week or two and a homework assignment on polymorphism. That's cool, you understand inheritance not really. What they don't teach is WHY to use inheritance and how to use it correctly. There is nothing on patterns, or refactoring, or just generally how to program with objects. There needs to be, as I think this skill is critical to have and that most college grads don't have it because they were never taught it (I know I wasn't). I think that Java would be a good tool for teaching this (although obviously not the only one).

And then there was testing

No one teaches how to test code or even how to make code testable. I'm not talking about running your app and checking inputs and outputs. I'm talking about unit testing, integration testing, and the automation of those things. It's not enough to just know that you have to test or that unit tests are important. You need to understand things like test doubles, test automation, and how to write code that can be tested in isolation. Java would probably be a good language for this.

And then there was all the "other stuff"

So how do you handle building big projects? How do automate a build? How do you manage source code? What is a branch? These are all things that most developers who have experience with them take for granted, but we all had to learn somewhere. Probably we either figured it out by experiencing the problems that these things solve, or someone at our jobs showed us. This needs to start in schools. Don't focus on a specific technology for any of these but again teach the concepts and why they're important. The code isn't all that important in this type of class so I could argue that you could use any language you want. HOWEVER, remember the purpose of this class is managing code not writing it, so don't force students to do anything complex in the code. Use some existing code and make trivial changes to it that force the students to use version control and to change the build process to take into account the more complex stuff.

The final thought

A lot of universities stick with Java because the students already know it and it's the lowest common denominator. That's fine if you want your students to come out being the lowest common denominators in the world of developers. One critical skill that developers have is the ability to learn new languages, particularly since new languages are developed all the time. This helps stay competitive in the workforce as technology changes. If you just teach the whole thing in Java, then that's a problem because students never get the opportunity to figure out a new language rapidly.

So my solution?

  1. Teach every class in the most appropriate language for the subject. Intro classes should be taught in something that has a minimum of extra crap to make the programs compile and run. Java is really ideal for this but I would be OK with c# also. The point of this class is an intro to programming, not an intro into fucking with the compiler.
  2. At a minimum, each student should be required to work in at least four programming languages while in school, one of which should be assembly and one of which should be object-oriented. HTML is not a programming language.
  3. Teach how to write good code. Comments != good code. This should be enforced in every class but there needs to be a specific class in how to do this and it needs to happen early in a student's career. Class should cover things like patterns, principles of OO design, unit testing, etc.
  4. Require version control to be used by every student for every class past the intro classes. Universities should provide access to a university-run vcs for each student. This isn't as hard to do as it sounds.
  5. Compiler, Hardware, and Operating Systems classes should be mandatory (sometimes some of these are not). I wrote a disassembler in assembly language as a final project in hardware. It was hard but not impossible and everyone in the class got at least something that sorta worked. Mine could disassemble itself accurately.
  6. Students should be forced to collaborate with each other in every class. Collaboration might include working together, but could also include code reviews or paired programming.
  7. Don't ever force a student to have their code reviewed in front of the class unless the student is ok with it, but anonymous code review or review by the professor in a private setting is fine. I realize that the business world will not conform to this but this is school and we don't want to alienate students. I think this is a compromise that will still teach a code review's value and how to conduct one without making people want to drop out of the program (or worse).
  8. Every class should involve writing at least some code.
  9. Professors should provide at least one well-written piece of code that demonstrates something that the class is teaching. It's helpful for students to read good code. It's equally helpful for students to read bad code and know why it's bad.
Finally, if you're a professor, college administrator, or anything similar and you want to talk to me or anyone else in more detail about this, I'd be happy to chat with you any time. I only rant about this because I passionately believe that it's important and I will do everything in my power to try to make Computer Science education better. If you're reading this, I challenge you to make this a priority as well. Go talk to your local college. Email your professors. Go offer to talk to classes at your local schools, particularly at the high school and community college levels. Encourage people to be CS students. You never know what kind of influence you'll have on someone unless you do nothing.

Monday, July 21, 2008

Tag Soup sucks: Hey Jeff, here's a better way

Jeff Atwood of coding horror posted about "tag soup" in web development. I absolutely agree with him on this one: every web development framework currently in existence renders crap HTML code. Remember my HTML wall of shame? Yes, that's a good example of crap HTML being rendered by frameworks. Jeff (Atwood) asks if there's a better solution. Luckily, Jeff (me) has one: it's called writing good HTMLand separation of concerns in rendering. Wow, that's a long phrase. Let's try again: Don't use frameworks because you don't know HTML; the people who wrote the framework don't know HTML either. No, still not good. Let's stick with the old favorite:

It's called HTML and it's not hard

That's right, HTML is not a complex thing and writing clean HTML isn't particularly difficult. In fact, you can leverage a framework and still write good HTML and I'm going to show you how. It's really as simple as using separation of concerns. Let's analyze the various parts of a web page.

HTML

What is the HTML for? It's really a place to store content. Your text, your menu bars, your stupid scrolling marquees, your <blink> tags, etc. All of this goes here. The way I think about it is that you're using HTML tags to create containers for content. A <p> tag is a container for some text. A <table> contains some related data in rows and columns. A <span> tag is going to hold some special line of content. A <div> is going to contain some special stuff inside of it. Notice that I haven't said a thing about formatting, style, or actual content yet. The reason would be that it DOESN'T BELONG IN YOUR STUPID HTML!!!!! One more thing I'd like to bring up here is the hell of nested tables. This occurs when someone wants to do some sort of complex formatting and doesn't know how to use the div tag with CSS. Nested tables are an anti-pattern called "Nested fucking tables" and should be avoided. It won't make your formatting better (Firefox and IE sometimes render different table elements differently so often this actually makes things worse). This brings us to:


Formatting and Style


So wouldn't it be nice if there were some sort of "style" thing you could use to store all your styles so you can keep them in one place (DRY, right?). Maybe some sort of "sheet" where your "styles" could go, and then they would "cascade" throughout the whole site for every page that referenced them. Maybe some sort of "cascading style sheet?" Oh wait, that already exists. Let's use it! Now, you can focus on the HTML only be containers for content and let your CSS define how that content is presented and styled. Separation of concerns, right? Now you have only containers and maybe some information in the containers to identify them to your style sheets (ID and Class are the attributes you're looking for). This is good separation of concerns.


So what about behavior?


This is where the client side stuff comes in. Things get a little trickier here but not that tricky. Ok, I lied, it's not tricky at all if you actually know javascript and treat it as actual application code and not some bastardized client side tag-hiding-style-manipulating crap. Javascript is a language. It is subject to the same rules of all programming: Separation of concerns, DRY, IoC, etc. It should also have its own unit tests. Finally, like CSS, it should be extracted into its own file so that every page can consume it.

So now you have containers that can be identified, styles that can be applied to them, and scripts that can determine their behavior all in separate places. The ID's and classes of your containers help your styles and scripts know what to apply themselves to. There is a minimum of code that exists in your HTML that helps bind these things together, and in those pages that really, really are one-offs, you have inline styles and inline javascript (this should REALLY be the exception though).


But the server blah blah blah . . . .

This is where the example that Jeff shows really breaks down. I'm not going to post it up here, but go look at his post and check out the example code. You'll see something really stupidly obvious: YOU'RE DOING LOGIC IN THE DAMN MARKUP!!!! You're concatenating links, you're looping through stuff, you're doing all kinds of crap. Hell, as long as you're at it, why don't you query the database there too just so all your crap is at least in one file?

There is a simple solution to this problem: You already have containers defined by your html. Use them. Expose them to the server side code and let that code render stuff inside them. For example, in ASP .Net one of my favorite tricks is to have a table on my page and actually use the <asp:table> object so that my codebehind can expose it to my controller (You're using MVC, right?) and my controller can populate it with data. Wait, controllers shouldn't populate data, so wtf am I doing? Am I breaking my own rules? No, I don't directly populate tables from the controller; typically I use an intermediary object to do that for me (more about this in a future post, I promise). This way, the controller is able to provide the model to the view via some other object that is responsible for doing complex formatting. I can reuse my formatting objects where appropriate. I can also change the formatting without changing the model or the view itself. I can change the view even if I want to without caring how the formatting is created (as long as the contract between the view and my formatting object is fulfilled, i.e. if the view is expecting a table then the formatter had best be rendering one).


Here you go, Jeff-

A nice, happy, clean solution looks something like this:

1. The HTML provides containers for content and possibly some content as well.
2. The CSS provides style information and formatting for the containers.
3. Javascript manipulates the containers client-side to create a client-side view when neccessary
4. The server side code populates the HTML containers with content.
5. The server side uses helper objects to populate content that requires more complex rendering (tables with grouping levels I think are a good example here).

The only thing you need in order to pull this off is to know all of these different technologies. This isn't that hard, and as a web developer you really should know all of this stuff anyway. I think Microsoft started a horrible trend with Asp .Net that allowed application developers to write web apps without knowing anything about web technologies. This attitude has brought us the viewstate, page events, chatty controls, and a bunch of other crap that makes your html look like tag soup. Rails and MVC haven't helped this problem at all.