- Published on
Reflections on What I've Learned Since College
- Authors
-
-
- Name
- David Mohundro
- @drmohundro
-
I’ve been going through some of my old code from college lately and I’ve been surprised by just how little I knew early on, even during my senior year. It made me think about the sorts of things that new programmers don’t (usually) learn or that are learned best from mentors as opposed to from books or classes. In this post, I want to share concrete things that I’ve learned. No calling out other people here, this is all me.
The main code I’ll be working through here is from my senior capstone course. It was a group project where we built an Othello game. The high level requirements covered the basics of what we had learned during our degree… things like graphical interfaces, networking (we had multiplayer support), artificial intelligence (we had difficulty levels), etc.
I was in charge of writing the networking component, which was a .NET 1.0 TCP server executable.
Duplication
One thing I’ve learned to deal with is code duplication. As I was reading through my code over two decades later, I had this sense that a lot of the logic was very similar. It made me wonder how similar, so I pulled up Beyond Compare and copy pasted one method into the left pane and then another in the right pane.
In a method that was 133 lines long… these were the only differences:
/// <summary>
/// Initializes the Othello AI and begins Flash communication assuming
- /// that the AI side will have the first move
+ /// that the Flash side will have the first move
/// </summary>
- private void SinglePlayerComputerFirst()
+ private void SinglePlayerHumanFirst()
{
- status.Text = "Single player game - Computer player has first move.";
+ status.Text = "Single player game - Human player has first move";
// Human goes first
bool human = true;
bool game_lockA = false, game_lockH = false;
bool gameAbort = false;
byte[] bytesReceived = new byte[1024];
I obviously can’t remember the thought process I had while I was writing it, but I definitely wasn’t thinking about duplication - I only cared about the problem immediately in front of me. Brute force!
(Effective) Source Control
Another thing I was quite unfamiliar with early on was source control. I suspect that college courses are teaching source control practices far sooner now, but I didn’t know anything about it when I was in college. Granted, source control has come a long way in the last 20 years - the first release of Subversion didn’t even happen until 2004. The code from our Othello project was all burned on a CD. I had multiple folders of various iterations showing various versions of the project. Check out some of the directory names:
COMP-440
├── Final Version (with)
├── Flash Stuff
├── Robots - Othello
| ├── From cs-server/Othello-bots
| ├── Old Stuff
│ │ ├── Board - Places Pieces
│ │ ├── Board - Plays Game with AI
│ │ ├── Board - Recognizes Clicks
│ │ ├── ChatServer
│ │ ├── Flash Server
| ├── UdpTest
| ├── UdpTesting
I have no idea what my deal was with the word “Stuff” - I guess that was just a catch all for when I didn’t know how to categorize something. The “Old Stuff” directory was entirely proof of concept (POC) projects, so I should have just put them in a “spikes” or “poc” directory.
The biggest issue with the directory organization was that the duplication of code files and not just logic. I had multiple copies of the full Robots .NET server - I think there were three copies of RobotsServer.cs
!
I do remember attempting to use Visual Source Safe, but I don’t think I have any copies of the VSS database… or whatever it actually tracked with. It’d probably be entertaining to see my commit messages from then.
Naming
On top of duplicating code left and right, I also clearly didn’t know how to name variables.
Just to showcase a few examples of variables that I found:
game_lockA
andgame_lockH
- I can infer that the
A
andH
stand for AI and Human… but I have no idea why I thought the variables needed a_
in the middle
- I can infer that the
toRecv
versusbytesReceived
- I wasn’t consistent with “receive” here
gameExit
andgameAbort
- These were in the same method. I have no idea if they serve two different purposes or not.
These are fairly nitpicky, but looking back I’m still surprised that I wasn’t using some consistency.
Variable Declarations
I suspect this section has more to do with learning C and C++ first, but I definitely thought that I needed to declare and initialize all of my variables at the top of the method. The problem is, at least in C#, you can declare your variables closer to their usage and then the intent is much clearer.
bool human = true;
bool game_lockA = false, game_lockH = false;
bool gameAbort = false;
byte[] bytesReceived = new byte[1024];
byte[] bytesSent = new byte[1024];
int bytesRead;
string toSend = "";
string toRecv = "";
string[] parseMove = new string[2];
int row, col;
AIMove aiMove;
Having lots of variables at the top of a method isn’t as bad as lots of global variables (which I struggled with, too), but it is similar. Global variables mean global state. Global state means more side effects. It also makes it harder to write correct threading code. By putting variables closer to their usage, though, it makes refactoring far easier. It means you can reason about smaller portions of the code, too. You can see the scope, especially if it is just within a small block versus the whole method.
Protocols
Here’s another thing that I noticed in my code. My hand-rolled network protocol is terrible. I’m not saying I needed a fully documented protocol, but I didn’t document it at all. I still don’t know if has_move
below is supposed to serve the purpose of a boolean or not. A big issue is that the protocol is coupled tightly to the AI implementation.
parsed = strRecv.Split( new char[] { ':' }, 5 );
char[] board = parsed[0].ToCharArray();
int difficulty = Int32.Parse( parsed[1] ) + 1;
int ai_select = Int32.Parse( parsed[2] );
int hu_select = Int32.Parse( parsed[3] );
int has_move = Int32.Parse( parsed[4] );
Error Handling
Related to my custom network protocol, I realize now that the code did almost nothing with error handling. It just assumes that everything will work. And let’s not speak of logging. The only original logging that existed in the code was to update the WinForms tray status text. If it crashed suddenly, I would have no idea why.
So… What’s Next?
There are plenty of other issues I could come up with, but these are a good start.
As I’ve been working through my old code, I decided to modernize the Othello project and get it running on my computer again. Converting from .NET 1.0 up to .NET 8.0 honestly won’t be too difficult… the hardest part will probably be the Flash user interface, but I’m hopeful that Ruffle will be able to help here, at least for a first iteration. I don’t know if I’ll be able to get any version of the Flash UI in a textual format in source, though.
Once it is done, I’m planning to publish the code online. So, stay tuned for that.