Objects, Inheritance, and X-Wings

Not too long ago, a friend who has only done procedural programming asked me to explain object-oriented programming. Today it occurred to me that it might be fun to explain objects in terms of the X-Wing Miniatures Game. [Obligatory disclaimer: this game is owned by Fantasy Flight. I have no rights to it and use it solely because I thought it would be a fun example. Rules may be simplified.]

In the X-Wing Miniatures game, players send fleets of ships against each other until one side is destroyed. Each ship is, naturally, a physical object that we can model with a logical object. How can we do that efficiently?

Base class: Ship

There are certain properties that are common across all ships, regardless of their physical aspects. Each ship has a name and a physical location in space. It has an attack value (how much damage it could potentially do), an agility value (how quickly it can maneuver), and a hull value (how many hits it can take before it is destroyed). It may or may not have shields. We can’t actually play a generic ship; this is just our common attributes. In our code, we can create the Ship class as an abstract class, which can be inherited from (we can have other classes that “are” Ships) but not instantiated (we can’t just create a Ship).

Subclass: Small, Medium, or Large Ship

A given ship may be small, medium, or large. While a small ship and a medium ship each have all the attributes of the generic Ship class, they use different size bases (plastic bases, not to be confused with the base class) and follow slightly different rules.

Two plastic squares, one larger than the other, on top of a playmat.
Abstract classes cannot be instantiated; they only form the base for the concrete class to be used later.

These will also be abstract classes; we can’t actually fly a copy of SmallShip.

Object: The actual ship

When we instantiate an object, we create it in memory and provide values for its required attributes. For example, suppose we wish to instantiate a T-65 X-wing fighter. We create an XWing class which inherits from SmallShip (and thus indirectly inherits from Ship). The constructor sets the ship name (T-65 X-wing) and attributes (3 attack, 2 agility, 4 hull, 2 shields). We also need to assign a pilot to fly the ship, so we’ll instantiate an instance of Jek Porkins (a Pilot object) and associate it with the XWing object.

Jek needs some support, so we’ll instantiate a second XWing object and associate it with a Luke Skywalker Pilot object. The two X-wings were instantiated from the same class and have identical properties (although the pilots will use them in different ways) but they are different objects; what happens to one does not necessarily affect the other.

 

Four small ships (2 TIE fights, an X-wing, and a Y-wing) prepare to engage in battle over the Death Star.
Concrete classes can be instantiated, giving us objects we can manipulate. Here we have four ship objects, two of which are instantiations of the TIEFighter class.

Using the objects

Now that we’ve instantiated our fleet, we’re ready to send it out to battle some TIE fighters! The Ship class has Move as an abstract method; each of the concrete classes that inherits from Ship must implement this method. We’re in a hurry to blow up some TIEs, so we’ll call xwing1.Move(3) and xwing2.Move(4) to send our small fleet straight ahead into battle. If a pair of TIE fighters zero in on Jek and do four damage, his shields will be wiped out and his hull reduced to two. This doesn’t immediately affect Luke’s X-wing, but he’d better get over there and help…

tldr

With object-oriented programming, we model the system as a collection of objects, each of which has properties (hull, agility) and methods (move, attack). Each object is responsible for keeping track of its own state, freeing the programmer from keeping track of it in an external data structure. A class that inherits from another class “is a” thing of that class. In our X-wing example, an X-wing and a Lambda-class shuttle may have different implementations of the Move() method, but because they are both Ships, we can call any method defined on the Ship class and get the actual method implemented on the associated class. Thus, we can have a function which takes a Ship as a parameter and calls ship.MoveSlowly(), and we can pass that function any object which is instantiated from a class that derives from Ship; we don’t need a separate function for X-wings and TIE fighters (or even for SmallShips and MediumShips).

Now go blow up some TIEs.

Difficult for Us, Easy for Them

Suppose you’re working on a complex piece of software with a due date that’s coming up fast. At the last minute, someone realizes that several of the help text popups contain typos. Is it worth fixing?

One thing I’ve noticed – and this is true in general, not just in software – is that a lot of people (and businesses) just don’t really care about what they might see as “little things” like this. I’ve lost count of how many times I’ve seen misplaced apostrophes or quotation marks on business materials, even for large chain stores that presumably can afford graphic designers and proofreaders.

The argument for ignoring the “little things” is simple: most people probably won’t notice or care. I would argue, however, that taking the time to fix even a minor misspelling or grammatical error is worthwhile, for several reasons.

By flickr user volkspider. Used under creative commons license.

The obvious one is professionalism. All of the written content you put out is part of how you present yourself. When a business cuts corners on proofreading, I have to wonder what else they cut corners on. This seems particularly relevant in a field like software development where attention to detail can be extremely important.

Possibly more important, though, is the effect that sloppy writing can have on the ability of your customers to use your software. The amount of attention customers have is limited. Any little bit of that that’s being used up on understanding a poorly written prompt is attention that’s focused on your application instead of on the task the customer is attempting to complete.

This is one small element of a larger topic: keeping the software out of the way of the user. Ideally, when the user is attempting to accomplish a task with your software, the software should be intuitive and require minimal thought to use. If I’m creating a document in Word and I’m not using any special formatting, I don’t have to think about how to use the program; my attention is all on what I’m writing. Anytime I have to actually pay attention to the software – if, for example, I’m trying to typeset mathematics (in which case, yes, I really should be using LaTex) – it’s going to lower my productivity.

When designing and developing software, it’s worth asking: what are the pain points going to be for the user? Is it possible to smooth out those points, making the software simpler from an end user perspective even if that makes it more complicated behind the scenes?

We get paid to deal with complexity. Part of that complexity is figuring out how to make things as easy as possible for our users.

MVVM and MVC

Those of us who have been putting together websites for a long while will remember back when everything was included in the .html (or even .htm) file. Eventually this was replaced by a separation of concerns – content in the .html file, behavior in the .js file, appearance in the .css file.

We can see a similar separation for a web application, where we keep the data itself in one file and behaviors that interact with that data in another file (or several files). This makes the application easier to test and maintain.

For example, suppose that we are writing an application which deals with customers. Using the MVVM (Model-View-View Model) or MVC (Model-View-Controller) pattern, the model itself holds information about the customer – name, address, and so forth. It may optionally contain methods for manipulating and validating the properties it holds. This is the back end of the website; the structure of the data can be developed without needing to know anything about the user interface.

The view is the user interface – in a web application this is the .ascx page that the user interacts with. In MVVM, the view knows about the model and view model; it may have bindings to properties on the model and may call functionality on the view model to manipulate the model. Given information about what data will be available in the model, the UI can be designed independently.

The View Model (in MVVM) or Controller (in MVC) is what ties the view and the model together. When the user attempts to do something in the view, it will actually call functionality on the view model or controller rather than manipulating the model directly.

In MVC, the controller sends commands to the model to update the state of the data and also sends commands to the view to change the presentation. In MVVM, the view model is essentially an abstraction of the view through which the view is bound to the model. Rather than using a reference to the view to communicate with it, as a controller does, the view model updates properties which the view is bound to. A view will be paired with exactly one view model.

A single model may have multiple views that display and (through the view model or controller) manipulate the model in different ways. The model doesn’t know about any of the views; it simply exposes the appropriate properties (possibly setting limits on how they can be manipulated).

Understanding these patterns makes it easier to see where a given property or method should be located. Something which is always loaded from the database in the same way regardless of which view is in use will be found on the model, as will (usually) the validators for that information. Methods which are view-specific – for example, security checks to verify whether the user of the view has the right to manipulate a given piece of data – are found on the view model. The view itself doesn’t contain data, but displays the contents of the model (and possibly additional content from the view model) to the user.

No comment?

I was listening to a podcast recently and one of the hosts made a comment that I’ve heard before. “There are two types of comments – lying and useless.”

For the most part, it’s a fair point. When people comment for the sake of commenting, we end up with useless comments – those that simply repeat the obvious. In the following example, the comment doesn’t add to the user’s understanding and in fact makes the code harder to read:

DateTime admissionDate = new DateTime(year, month, day); // a date

In this case, because the variable is descriptively named, the code is self-documenting; the urge to comment here would suggest that the variable name needs to be updated.

Similarly, a comment that describes how a function works is in danger of becoming outdated the next time the function is updated, if the developer doesn’t notice and update the comment as well. This leads to confusion as the comment no longer accurately describes the functionality. In this case, it’s worth looking at whether we can refactor the code to be more clear so that it doesn’t need a comment.

What I take issue with is the suggestion that these are the only kinds of comments; I actually find comments to be useful in a variety of situations. Here are some times I use comments:

  • Explaining the why
    • For a while, I had an extra line of code that worked around an outstanding issue I needed another team to fix. A comment explained why the line was there and when it could be removed (with a link to the issue that needed to be fixed)
  • XML comments
    • I wrote a function recently where a particular option should be used only in certain situations, where it didn’t make sense to include the entire situation in the variable name. An XML comment on the function helped make sure it was used correctly; the next time I referred to it the intellisense helped me set the parameters correctly.
  • SVN comments
    • This one might be cheating a bit, since it’s not strictly a code comment…but when trying to find one particular change among many commits (when I don’t know exactly where the change is), having descriptive svn comments is a huge help.

Self-documenting code (in particular, good naming) and version control (no need to leave large blocks of code commented out when you can simply retrieve them later if needed) greatly reduce the need for comments, but don’t remove it entirely. Your code should show intent – that is, what you want to do. Let your comments show motivation – why you’re doing it.

Analysis of a Difficult Project

In the eight years that I’ve been a software developer, I’ve had my share of difficult projects.

Early on, the difficulty might have been because I was new to development and new to the code base. I had to learn my team’s conventions and how to use our internal tools.

Recently, however, I had my most stressful project so far. The initial development seemed to go well enough, but getting the quality to where it needed to be took much, much longer than anticipated. I’m now taking the opportunity to reflect on what went wrong, so I can avoid making the same mistakes in the future.

Project Summary

In this project, I was taking some legacy code and rewriting it to be web-based. I’ve probably done this ten times before, but this was one of our most complicated activities. It was also one of my first projects since we switched to a quarterly release cycle, which meant more pressure from deadlines.

I won’t go into more detail than that, except to say that due to the nature of my job, the quality of released code has to be very, very high. The possibility of a data integrity error – either from a programming mistake or because the user is confused – is simply not acceptable.

Design

Part of our process is to have a design for each new piece of development, describing what we’re going to do and why. This is partially so that we can discover issues early on – much better for someone to realize that what we’re going to do won’t work before we start coding than after we’re finished – and partially so we have a record we can refer back to later if we’re not sure why we did something the way we did.

For this type of project – migrating legacy code to web – the assumption is that the functionality of the new activity will be the same as that of the old one, with the exception of changes that are explicitly called out. Usually, just listing out the changes is sufficient. In this case, however, because the activity is so complex, nobody on the team was familiar with all of it. This led to two issues:

  1. The quality assurance (QA) people reviewing the design weren’t fully familiar with the functionality, so they didn’t realize that there were changes they would like until they started testing the completed development. At that point, it was a lot more difficult to make the changes than it would have been if they had been brought up during the design. If the existing behavior had been fully spelled out in the design it would have been easier to catch changes we needed to make.
  2. Part of functional testing involved performing the same workflows in the old and new activities and ensuring that the functionality matched except where called out in the design. Since we didn’t have a complete list of functionality, testing was more difficult.

Development

The original project plan called for 300 hours of development.

However, this project was not the only thing on my plate, and I was assigned a lot of other work that kept pushing it back. At the same time, I was under pressure to get it done so we could keep making progress towards getting rid of our legacy code.

As a result, I pushed harder than I should have, finishing the initial development in just over 100 hours. That put me way ahead of schedule, development-time-wise (if not by the calendar) but with three big problems:

  • I barely had any unit tests. What I did have covered only a small fraction of the code.
  • I hadn’t done enough manual testing to ensure everything worked as anticipated.
  • I didn’t have a complete description of how the activity was supposed to work, so the new activity had less than 100% functionality.

Testing

As I mentioned above, we have very high quality standards, and as such every piece of new development has to undergo inspection by multiple people. This would normally mean that two other developers would review my code and two QAers would test it. Due to the complexity involved, we actually ended up with three QAers assigned to test and two unscripted testing sessions when additional QAers tried out the activity. We also had usability sessions when nontechnical people came in and tried out the new functionality.

The lack of unit tests hit me again at this stage. When doing a fix, it was difficult to ensure that I wasn’t causing any new issues – and we were still pushing hard to meet (new) deadlines. As a result, in the first round of code review and QA, we ended up with a fair number of new issues created by fixing the old ones.

Towards the end of the first round of testing, we were still finding differences between the old and new activities – an indicator that we really needed to have a detailed written description of exactly how everything worked.

Conclusion

This was a project in which it seemed that everything which could go wrong, did. Requirements kept changing – both technological and functional. We were constantly brushing up against (and shooting past) deadlines. Getting the quality up to expectations took much longer than anticipated.

We did have some wins. From a usability perspective, the new activity is much better than the old one, and the users who participated in usability testing were excited to see the improvements. Accessibility – which presented its own challenges – is also much improved. The process worked – while it took a long time to make the code stable, it was never in danger of being released to end users before the quality was where it needed to be.

For future projects, my takeaways were:

  • Take the time to make the design as detailed as it needs to be – even if that means (as it would have in this case) spending an extra week on it. It’ll pay off in the long run.
  • Test, test, test. An extra two-four weeks up front writing unit tests and doing manual testing would have easily paid for itself in the long run. I’ve been pushing unit tests for a while and I’m now getting buy-in for spending the extra time required.
  • Given unrealistic deadlines, push back. I have a reputation for fixing problems quickly, especially when it’s a high-priority bug, but in this case I was simply being assigned too much work and I should have said so instead of trying to get everything done. Not pushing back enough left me stressed out as I struggled to finish everything, and quality suffered – which meant taking more time to ensure that the code was in the state it needed to be in.

I have many more projects like this to take care of over the next year (although this was the most complex). I’ll make things a lot less stressful on myself by insisting on following our best practices, even if it takes more time up front. And now I have an updated list of possible bugs to watch for in the future.

This was definitely a learning opportunity. Here’s to being better.

Doing big things

Fans of Marvel movies know to wait around until the end of the credits to see the extra scene. This adds a bit of time to the theater experience, as the credits go on for quite some time; it takes hundreds of people to make a movie on that scale.

At work, I’m one of a dozen developers on my team and one of several thousand developers in the company. While each team largely sets its own policies, there are some general standards for consistency across the company.

Even relatively small side projects tend to involve several people. I’m currently finishing a small computer science textbook that I’ve been working on for the last few years. Even though I have a PhD, work experience in both writing and editing, and a tendency to do everything myself, my book still has a technical editor, a copy editor, and a graphic designer. I could certainly finish the book without any of those people, but they’ll help me to make it as good as it can be.

In short, any project of significant complexity, even if it’s small enough to be done by one person, will benefit from having multiple sets of eyes. When multiple people are working on the same content, however, it’s important to have standards for consistency and for resolving disagreements. Sometimes this means there’s one person in charge – the director, the lead developer, the author – who makes decisions that the rest of the team will follow. Sometimes it’s a collaborative process. But consistency is key.

What are some things to do to help ensure that your projects are successful?

1. Save frequently and use version control.
Not too long ago, I needed to retrieve some code that had been deleted by another developer who had left the company over a year before. Although the code never made it to production, I was still able to retrieve it from svn, which saved me dozens of hours of work.

1a. Make small commits with good comments.
In the past, my habit was to save when I finished a chunk of development or at the end of the day, whichever came first. That meant that even if a disaster occurred I wouldn’t lose more than a day’s work, but it also meant that my svn commits while working on a project tended to be rather large and contain a number of fixes, making it more difficult to locate a specific change. Now, I generally commit (with a descriptive svn comment) every time I fix one bug or add one new feature. The primary reason for this is so that when I go on to the next change, if I screw it up it’s easy to roll back to the last stable state without losing any of my previous work, but it will also make it easier for any future developer (including future me) to find a specific fix if needed.

1b. Delete commented-out code.
I’m not opposed to comments – I’m in the school that believes that what the code is doing should generally be clear from reading it, but comments are useful to explain why the code is doing what it’s doing and document any assumptions. When working it’s easy to comment out sections of code for testing or because they’re not currently needed, but leaving those in afterwards clutters up the code and makes it more difficult to tell if something should have been turned back on. Version control means not needing to keep commented-out code. Just delete it – it can be restored if it’s needed again in the future.

2. Have consistent standards.
There are a lot of areas where there are multiple ways to do something and it doesn’t really matter which one you pick, but being consistent in your choice makes things easier. For example, my team consistently puts the list of properties for a class at the bottom of that class; while it’s easy enough to use F12 to jump to a given property definition, having all the properties together in the same place in each class makes it easier to look through them when needed, without having to think about it.

3. Use good names.
Few things result in as much unneeded mental effort as deciphering code with unclear or misleading variable names. I’ve certainly had instances where towards the beginning of a decent-sized project I’ve given two properties similar names and by the end I’m having trouble remembering which is which. If it’s not clear what information a variable or property is holding, then the name needs to be updated! Similarly, function names should accurately describe what the function does.

4. Test everything that needs to be tested.
What I’ve struggled with the most as a developer is making sure to test all the things that could possibly go wrong with my code. Unit tests can do a big chunk of the actual testing, but the developer still needs to come up with the test cases. This can be a challenge because as the developer you (hopefully) understand how the code is supposed to work, so you use it correctly. Take the time to figure out all the logic-impaired things your users might do and try those also.

5. Understand the desired outcome.
When I’ve had development that’s gone way over the expected timelines, it’s been because requirements have changed repeatedly over the course of writing the code. While that is sometimes unavoidable, do try to minimize it as much as possible for larger projects by writing a reasonably complete design document and getting buy-in from the relevant people before you start development. Sometimes you even get lucky; I’ve had projects end up becoming much less complex before I ever started coding, because my initial design was more complicated than it needed to be and my design reviewers were able to point out a better way to accomplish the same task. Aside from getting buy-in from key people, a good design helps you be sure that you understand the requirements, which makes it easier to implement them correctly.

It’s always worthwhile to make things easier for the next person who will look at your code. Very often, that person is you.

Accessibility at Cream City Code

As promised, here are the slides from my Accessibility talk at Cream City Code. Aside from the links (which, if you saw the talk, are probably why you’re here) there’s a fair amount of extra detail in the presentation notes that I didn’t have time for in the talk.

I saw people taking notes during the talk, which is great – this is stuff we need to be implementing! I’d like to particularly thank those people who actually heard me speak on this topic at That Conference last year and decided it was worth their time to come hear me again.

Accessibility slides

Thoughts on That Conference 2018

For the last few years, the beginning of August has meant one thing for me: That Conference! The joke eventually starts getting old, but the conference is always worth the time. 2018 was my fourth year attending and second year speaking.

My favorite talks this year were the first two keynotes. When I saw the title of Jessica Kerr’s talk, “the Origin of Opera and the Future of Programming”, I assumed she meant the web browser. No – she actually meant Opera singing! This isn’t a topic that I have any interest in whatsoever…but Jessica’s enthusiasm actually made it interesting (and yes, she did relate it to programming). Then Cory House gave the keynote I was particularly looking forward to, on building a career; he talked about the tradeoffs he had to make to do what he does. Videos of both talks are available on the That Conference Facebook page.

When I’m writing or speaking, I like to choose a topic that’s more conceptual than language X or program Y. Last year my talks were on accessibility (at That Conference) and sorting algorithms (at MKE.NET); this year my talk at That Conference was about graph theory and I’ll be speaking on accessibility again at Cream City Code. Over the last few years I’ve noticed that there are a lot of software developers who don’t have computer science degrees and are interested in seeing what they’ve missed, which is why I’m giving talks on various topics that would normally be covered in the course of a CS degree (and am writing a book in the area as well). I was happy to see quite a few people turn out to hear what was essentially a math talk and stay engaged to the end. Graph theory isn’t something that most of us will use every day, but it definitely has a lot of practical applications in programming.

If you came to my talk, I appreciate it! See you next year for more waterpark and bacon :-)

Graph Theory slides for That Conference 2018

Setting Priorities

Assuming you get to set your own schedule, how do you choose what to work on next?

Do you pick the most important task? The one with the closest deadline? The one most likely to make your boss (or significant other) happy?

After I read about Kanban, I started trying to arrange my tasks such as to minimize the amount of work that I have in progress. My goal is to minimize the number of things that I am responsible for at any point in time.

Obviously, this doesn’t mean that the highest-priority tasks don’t still get done first – I can’t imagine my team leader would be happy if I told him I was going to put off responding to a critical bug report for a week because I have too many low-priority tasks on my plate – but it means that I’ll try to prioritize finishing something that’s already started over starting something new. This results in less time for tasks overall, because you’re more likely to get back to things while they’re still partially cached in your memory, rather than having to get up to speed again before you can start working. I actually prefer to tackle the tasks that can be knocked off quickly before anything else, and get them through the process and off my plate entirely so they’re not taking up any mental energy.

Plus, it sounds a lot better (at least to me) to say “I finished 5 tasks last week and I’m working on my big project” vs just “I spent last week working on my big project.” Particularly when progress on the larger project is difficult to measure, this shows you’re still getting work done.

Communication in Software Development

At a meetup I was at recently, the speaker asked what we thought were the most important soft skills. My answer was English, or more generally, communication.

Programming can be thought of as communicating with the computer – telling the system what we want it to do – but software development is often just as much about communicating with other people.

  • Before we code, we (sometimes) write a design that lays out exactly what the code should do.
  • When coding, we (hopefully) name our methods and variables, and sometimes even comment, in such a manner as to make it clear to the future reader (whether that be ourselves or another programmer) exactly what’s going on.
  • After the code is complete, we may write testing instructions that should be clear as to exactly what behavior is expected from the code.

These are all areas where many people get sloppy. The design doesn’t really explain what the new expected behavior is, the code is difficult to read, the testing instructions are essentially just “make sure it works.” Often much of this documentation fails to be complete sentences. The result is debate over whether or not the code is working correctly, because the stakeholders don’t have a shared understanding of what “working correctly” looks like.

For the QAer, clarity can be even more important: a bug report is much more useful when it specifies exactly what the problem is, what should be happening, and how to reproduce the issue. Specific, actionable bug reports tend to be acted on; vague notes that don’t explain how to reproduce the problem tend to sit in the bug tracking system for years until they eventually get closed as obsolete (or blow up).

Take the time to communicate clearly; it speeds things up in the long run.