Category Archives: Software Development

Nebulas July 2023 Update

A couple of years ago, I wrote about designing a game that is the opposite of Asteroids. Last week, I found raylib, a C library for making games and decided to try making it (I also renamed it Nebulas).

Here’s what I have so far.

  • You are a triangle ship in the center of a starfield, which can help with navigation.
  • You can rotate (LEFT and RIGHT) and thrust (UP)
  • Space is infinite in all directions. You are always at the center of the screen.
  • There are nebulas in space around you moving.
  • You can go into them and suck their energy into your ship

Here’s a video of my progress so far:

Next

  • The color of the nebula is a clue to what power your ship gets from it. (e.g. Red is thrust)
  • The color of the ship is which gas you are currently using.
  • I will show tanks for each color so you can see how much you have of each gas.

The game is to survive as long as possible, conserving your gases. As long as you survive, you can explore.

A Little Linear Algebra Helps to Make Games

I started playing with raylib to make a game that is the opposite of Asteroids. Just to start, I need to be able to draw a triangle that is rotated by some angle around its center, and that’s simple if you understand transformations. Raylib does include a raymath module to do basic vector math, but not specifically what I needed.

So, again, I’m feeling The Unreasonable Effectiveness of Mathematics in Programming. I’m not sure you can make any reasonable game without some transformations.

raylib First Impressions

I just ran into raylib (because I’ve been reading a ton of HN blogs) and it’s making me dream of Programming With the Joy of a Thirteen Year-Old. All I wanted to do when I was 13 was make games and raylib looks like a fun way to do it.

I read the homepage, played a few games, and then read their source. Here’s what I love

  1. You write in “easy” C. I searched for pointers and signs of dynamic memory and found none in the simple games I read. I’m sure it’s there in more complex games, but they aren’t making you use pointers just to get started
  2. It runs in the browser. It’s C, so it’s expected that it would be cross-platform, but it can also compile to something that runs in the browser.
  3. No external dependency philosophy. Dependencies are just future tech debt.
  4. No (or very little) magic. It’s just a library. Games are mostly a loop of reading the controller, updating some state, and rendering that state. That logic is very clear in raylib code.
  5. Simple games are simple. I played three classic games and then read their source. They were each one file and followed similar logic.
  6. There’s is more to it. Once you progress from the simple stuff, it looks to be full-featured with other things you might want in a game library. But, you don’t need to use any of it at first.

I might play around and see where I can get with Nebulous.

Thinking Fast with Keyboard Shortcuts

The book Thinking, Fast and Slow by Daniel Kahneman describes our brain as having two thinking systems, a fast one and a slow one. The fast system is automatic and multitasking, while the slow system is methodical and single-focussed. When you are doing something complex, you are usually concentrating your slow system on the main problem, while the fast system can be doing several related (and even unrelated tasks).

This is the main reason I try to memorize and practice several keyboard shortcuts for the programs I use every day. I am trying to get as much of the mechanics of the code editing into my automatic, fast thinking system.

There is a common belief that mousing is faster than keyboard shortcuts, which probably originated with this AskTog article:

We’ve done a cool $50 million of R & D on the Apple Human Interface. We discovered, among other things, two pertinent facts:

  • Test subjects consistently report that keyboarding is faster than mousing.
  • The stopwatch consistently proves mousing is faster than keyboarding.

This is probably true for the general case and for when the user is learning a new UI. But, the study, Comparison of Mouse and Keyboard Efficiency, suggests that:

[…] for heavily-used interfaces, keyboard shortcuts can be as efficient as toolbars and have the advantage of providing fast access to all commands.

For me, the key is that shortcuts have to be able to be deployed with no conscious thought.

Programming often requires you to keep several interrelated thoughts in your head until you get the code written and working. For example, even for simple web UI blocks, you have to think of the semantic structure of the tags, the layout, and the style. To write that code, you will have to possibly jump through a few files and parts of those files. So, to keep from adding more cognitive overhead, you want to make the manipulation of the editor as automatic as possible.

This is something I think that can never be accomplished with the mouse, but is possible for a small set of shortcuts. That set should be the most common actions that happen while you are actively programming. The goal is to keep your slow system and short term memory focussed on the programming task at hand.

For me, that is:

  1. Cut, Copy, Paste, Undo, Redo
  2. In-file navigation with arrows and modifiers (including using Shift for selection)
  3. Find and multi-file Find
  4. Show the current file in the project navigator
  5. Open another file, tab cycling
  6. Jump to the definition of the identifier under the cursor
  7. Comment (or uncomment) the current selection

I do these commands all of the time. I often need to string a series of these commands together. Doing the equivalent without the commands risks engaging your slow thinking system and breaking you out of flow.

Observations on the MIT Study on GitHub Copilot

I just saw this study on GitHub Copilot from February. Here is the abstract:

Generative AI tools hold promise to increase human productivity. This paper presents results from a controlled experiment with GitHub Copilot, an AI pair programmer. Recruited software developers were asked to implement an HTTP server in JavaScript as quickly as possible. The treatment group, with access to the AI pair programmer, completed the task 55.8% faster than the control group. Observed heterogenous effects show promise for AI pair programmers to help people transition into software development careers.

The researchers report benefits to less experienced developers, which is at odds with this other study I wrote about and my own intuition. However, all of the developers were experienced Javascript developers, and not literally learning programming, which is where I think the more detrimental effect would be.

Using Zeno’s Paradox For Progress Bars

When showing progress, if you have a list of a known length and processing each item takes about the same time, you can implement it like this pseudocode:

for (int i = 0; i < list.length; ++i) {
    process(list[i]);
    // notifyProgress takes a numerator and denominator to 
    // calculate percent of progress
    notifyProgress(i, list.length);
}

One common problem is not knowing the length beforehand.

A simple solution would be to pick a value for length and then make sure not to go over it.

int lengthGuess = 100;
for (int i=0; list.hasMoreItems(); ++i) {
    process(list.nextItem());
    notifyProgress(min(i, lengthGuess), lengthGuess);
}
notifyProgress(lengthGuess, lengthGuess);

This works ok, if the length is near 100, but if it’s much smaller, it will have to jump at the end, and if it’s much bigger, it will get to 100% way too soon.

To fix this, we might adjust lengthGuess as we learn more:

int lengthGuess = 100;
for (int i=0; list.hasMoreItems(); ++i) {
    process(list.nextItem());
    if (i > 0.8 * lengthGuess) {
        lengthGuess = 2*i;      
    }
    notifyProgress(i, lengthGuess);
}
notifyProgress(lengthGuess, lengthGuess);

In this last example, whenever i is 80% of the way through, we set lengthGuess to 2*i.  This has the effect that the progress goes back and forth between 50% and 80% and then it jumps to the end.  This won’t work. 

What I want is:

  1. The progress bar should be monotonically increasing
  2. It should get to 100% at the end and not before
  3. It should look as smooth as possible, but can jump

An acceptable effect, would be to progress quickly to 50%, then slow down to 75% (50% of the way from 50% to 100%), then slow down again at 87.5% (halfway between 75% and 100%), and so on.  If we keep doing that, we’ll never get to 100% in the loop and can jump to it at the end. This is like Zeno’s Dichotomy paradox (from the Wikipedia).

Suppose Homer wants to catch a stationary bus. Before he can get there, he must get halfway there. Before he can get halfway there, he must get a quarter of the way there. Before traveling a quarter, he must travel one-eighth; before an eighth, one-sixteenth; and so on.

To do that we have to keep a factor to use to adjust the progress we’ve made (playing around with it, I found that using a factor of 1/3 rather than 1/2 was more pleasing).

int lengthGuess = 100;
double begin = 0;
double end = lengthGuess;
double iFactor = 1.0;
double factorAdjust = 1.0/3.0;
for (int i = 0; list.hasMoreItems(); ++i) {
    process(list.nextItem());               
    double progress = begin + (i - begin) * iFactor;
    if (progress > begin + (end-begin) * factorAdjust) {                   
        begin = progress;
        iFactor *= factorAdjust;
    }               
    notifyProgress(progress, lengthGuess);
}
notifyProgress(lengthGuess, lengthGuess);

The choice of lengthGuess is important, I think erring on too small is your best bet.  You don’t want it to be exact, because we’ll slow down when we get 1/3 toward the goal (factorAdjust).  The variables lengthGuess and factorAdjust could be passed in and determined from what information you have about the length of the list.

How to fix WCErrorCodePayloadUnsupportedTypes Error when using sendMessage

If you are sending data from the iPhone to the Apple Watch, you might use sendMessage.

func sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)? = nil)

If you do this and get the error WCErrorCodePayloadUnsupportedTypes this is because you put an unsupported type in the message dictionary.

The first parameter (message) is a dictionary of String to Any, but the value cannot really be any type. If you read the documentation, it says that message is

A dictionary of property list values that you want to send. You define the contents of the dictionary that your counterpart supports. This parameter must not be nil.

“property list values” means values that can be stored in a Plist. This means you can use simple types like Int, Bool, and String and you can also use arrays and dictionaries as long as they are of those simple types (e.g. an Array of Ints)

I ran into this issue because I tried to use a custom struct in the message dictionary, which is not supported.

Note: I made this post because google is sending people to Programming Tutorials Need to Pick a Type of Learner because it mentions WCErrorCodePayloadUnsupportedTypes incidentally, but isn’t really about that.

Pre-define Your Response to the Dashboard

A few days ago, I wrote about using Errors Per Million (EPM) instead of success rate to get better intuition on reliability. I also recently said that Visualizations Should Generate Actions. Sometimes it’s obvious what to do, but if not, you can think through the scenarios and pre-define what actions you would take.

Here’s an example. This is a mock up of what a dashboard showing EPM over time might look like. The blue line is the EPM value on a date:

The three horizontal lines set levels of acceptability. Between Green and Yellow is excellent, between Yellow and Red is acceptable, and above Red is unacceptable. When we did this, we thought about using numbered severity levels (like in the Atlassian incident response playbook), but we decided to use Green/Yellow/Red for simplicity and intuition.

We also pre-defined the response you should have at each level. It was something like this:

LevelResponse
GreenNone
YellowThere must be at least one item in the current sprint with high priority to address this until the level is back to Green. It can be deployed when the current sprint is deployed.
RedAt least one person must be actively working to resolve the issue and doing hot fix deploys until the level is back to Yellow.

The advantage of this was that these actions were all pre-negotiated with management and product managers. This meant that we could just go ahead and fix things (at a certain level) instead of items getting lost in the backlog.

When we created this dashboard, we were in the Red, but we knew that going in. We worked to get ourselves Green and in practice, we were rarely not Green. This is another reason to pre-define your response, as it becomes too hard to remember how to handle situations that rarely happen.

When 99.8% Success Wasn’t Very Good

One of my projects at Trello was looking into and fixing a reliability problem we were having. Even though it seemed solid in testing, we were getting enough support tickets to know that it must be worse than we thought. We collected data on its success rate and found out that it was successful 99.8% of the time. To move forward with doing more work on it, I had to convince our team and management that 99.8% was bad.

At the time, there was a company-wide push at Atlassian to improve reliability, and there was a line-manager assigned to oversee it across Trello teams, so I spoke to him. I showed him the data, but also told him that there’s an overall feeling on our team that it’s affecting our customers more than it seems.

He suggested that I flip the ratio and instead look at the data as Errors Per Million. When you do that, with the same data, you get 2,000 errors per million attempts. This particular thing happened around 2 million times per day, so that was 4,000 errors per day. That partially explained the issue, because it wouldn’t take much for the support tickets to get out of control. Luckily, not all 4,000 were of the same severity, and many were being retried. Still, that number is too high.

The other thing I found after more analysis, was that the errors were not evenly spread across the user base. They tended to cluster among a smaller cohort, which was experiencing a much worse EPM than then rest of the users.

With that in hand, we greenlit a project to address this by targeting the most severe problems. We found several bugs. Eventually we got the success rate to 99.95%.

It’s not clear that 99.95% is four times better than 99.8%, but the equivalent EPM after the fixes is 500 (as opposed to 2,000 before). What surprised me is how different a reaction a high EPM got compared to equivalent numbers. 2,000 EPM is literally the same as 99.8% success and 0.2% failure rates, but the latter two seem fine. Even if I say we get 2 million attempts per day, it’s hard to intuitively understand what that means.

When I said 2,000 EPM, we instinctively felt that we failed 2,000 users. When I said we get 2 million attempts, everyone knows to double EPM to get 4,000 incidents per day, and we felt even worse. That simple change in reporting made all the difference in our perspective.

How I Use JIRA and Trello Together

I started using JIRA for issue tracking when I worked at Trello (at Atlassian), and I still use it now. JIRA does everything I need in managing software projects, but I never send people outside of my team to JIRA because it’s not easy for casual users. For that I use Trello.

I have a Trello board for each project I am managing that is meant to be a high-level summary of that project. It is useful for onboarding and getting its current status easily. It has links to JIRA, Confluence (for specifications), Atlas (for status) and Figma.

This Trello board is the first place I send a new team member to help with onboarding. If someone has a question in Slack about the project, I make sure that it was something you could find out on the board and then link them to it there. The board is a kind of dashboard and central hub of the project.

These hub boards are curated, so I don’t try to use any automations to bring things over. If I think you need more information, I send you directly to the source.

JIRA is useful to the people that work on the project every day. I use Trello for those that just check in weekly or monthly.